自 2.2 开始,Taro 引入了插件化机制,允许开发者通过编写插件的方式来为 Taro 拓展更多功能或者为自身业务定制个性化功能,欢迎大家进行尝试,共同讨论~

当前版本 2.2.1

官方插件

Taro 提供了一些官方插件

如何引入插件

你可以从 npm 或者本地中引入插件,引入方式主要通过 编译配置中的 pluginspresets,使用如下

plugins

插件在 Taro 中,一般通过编译配置中的 plugins 字段进行引入。

plugins 字段取值为一个数组,配置方式如下:

const config = {
plugins: [
// 引入 npm 安装的插件
'@tarojs/plugin-mock',
// 引入 npm 安装的插件,并传入插件参数
['@tarojs/plugin-mock', {
mocks: {
'/api/user/1': {
name: 'judy',
desc: 'Mental guy'
}
}
}],
// 从本地绝对路径引入插件,同样如果需要传入参数也是如上
'/absulute/path/plugin/filename',
]
}

presets

如果你有一系列插件需要配置,而他们通常是组合起来完成特定的事儿,那你可以通过插件集 presets 来进行配置。

配置编译配置中的 presets 字段,如下。

const config = {
presets: [
// 引入 npm 安装的插件集
'@tarojs/preset-sth',
// 引入 npm 安装的插件集,并传入插件参数
['@tarojs/plugin-sth', {
arg0: 'xxx'
}],
// 从本地绝对路径引入插件集,同样如果需要传入参数也是如上
'/absulute/path/preset/filename',
]
}

在了解完如何引入插件之后,我们来学习一下如何编写一个插件。

如何编写一个插件

一个 Taro 的插件都具有固定的代码结构,通常由一个函数组成,示例如下:

export default (ctx, options) => {
// plugin 主体
ctx.onBuildStart(() => {
console.log('编译开始!')
})
ctx.onBuildFinish(() => {
console.log('编译结束!')
})
}

插件函数可以接受两个参数:

  • ctx:插件当前的运行环境信息,包含插件相关的 API、当前运行参数、辅助方法等等
  • options:为插件调用时传入的参数

在插件主体代码部分可以按照自己的需求编写相应代码,通常你可以实现以下功能。

Typings

建议使用 typescript 来编写插件,这样你就会获得很棒的智能提示,使用方式如下:

import { IPluginContext } from '@tarojs/service'
export default (ctx: IPluginContext, pluginOpts) => {
// 接下来使用 ctx 的时候就能获得智能提示了
ctx.onBuildStart(() => {
console.log('编译开始!')
})
}

主要功能

命令行扩展

你可以通过编写插件来为 Taro 拓展命令行的命令,在之前版本的 Taro 中,命令行的命令是固定的,如果你要进行扩展,那你得直接修改 Taro 源码,而如今借助插件功能,你可以任意拓展 Taro 的命令行。

这个功能主要通过 ctx.registerCommand API 来进行实现,例如,增加一个上传的命令,将编译后的代码上传到服务器:

export default (ctx) => {
ctx.registerCommand({
// 命令名
name: 'upload',
// 执行 taro upload --help 时输出的 options 信息
optionsMap: {
'--remote': '服务器地址'
},
// 执行 taro upload --help 时输出的使用例子的信息
synopsisList: [
'taro upload --remote xxx.xxx.xxx.xxx'
],
async fn () {
const { remote } = ctx.runOpts
await uploadDist()
}
})
}

将这个插件配置到中项目之后,就可以通过 taro upload --remote xxx.xxx.xxx.xxx 命令将编译后代码上传到目标服务器。

编译过程扩展

同时你也可以通过插件对代码编译过程进行拓展。

正如前面所述,针对编译过程,有 onBuildStartonBuildFinish 两个钩子来分别表示编译开始,编译结束,而除此之外也有更多 API 来对编译过程进行修改,如下:

  • ctx.onBuildStart(() => viod),编译开始,接收一个回调函数
  • ctx.modifyWebpackChain(args: { chain: any }) => void),编译中修改 webpack 配置,在这个钩子中,你可以对 webpackChain 作出想要的调整,等同于配置 webpackChain
  • ctx.modifyBuildAssets(args: { assets: any }) => void),修改编译后的结果
  • ctx.modifyBuildTempFileContent(args: { tempFiles: any }) => void),修改编译过程中的中间文件,例如修改 app 或页面的 config 配置
  • ctx.onBuildFinish(() => viod),编译结束,接收一个回调函数

编译平台拓展

你也可以通过插件功能对编译平台进行拓展。

使用 API ctx.registerPlatform,Taro 中内置的平台支持都是通过这个 API 来进行实现。

注意:这是未完工的功能,需要依赖代码编译器 @tarojs/transform-wx 的改造完成

API

通过以上内容,我们已经大致知道 Taro 插件可以实现哪些特性并且可以编写一个简单的 Taro 插件了,但是,为了能够编写更加复杂且标准的插件,我们需要了解 Taro 插件机制中的具体 API 用法。

插件环境变量

ctx.paths

包含当前执行命令的相关路径,所有的路径如下(并不是所有命令都会拥有以下所有路径):

  • ctx.paths.appPath,当前命令执行的目录,如果是 build 命令则为当前项目路径
  • ctx.paths.configPath,当前项目配置目录,如果 init 命令,则没有此路径
  • ctx.paths.sourcePath,当前项目源码路径
  • ctx.paths.outputPath,当前项目输出代码路径
  • ctx.paths.nodeModulesPath,当前项目所用的 node_modules 路径

ctx.runOpts

获取当前执行命令所带的参数,例如命令 taro upload --remote xxx.xxx.xxx.xxx,则 ctx.runOpts 值为:

{
_: ['upload'],
options: {
remote: 'xxx.xxx.xxx.xxx'
},
isHelp: false
}

ctx.helper

为包 @tarojs/helper 的快捷使用方式,包含其所有 API。

ctx.initialConfig

获取项目配置。

ctx.plugins

获取当前所有挂载的插件。

插件方法

Taro 的插件架构基于 Tapable

ctx.register(hook: IHook)

注册一个可供其他插件调用的钩子,接收一个参数,即 Hook 对象。

一个 Hook 对象类型如下:

interface IHook {
// Hook 名字,也会作为 Hook 标识
name: string
// Hook 所处的 plugin id,不需要指定,Hook 挂载的时候会自动识别
plugin: string
// Hook 回调
fn: Function
before?: string
stage?: number
}

通过 ctx.register 注册过的钩子需要通过方法 ctx.applyPlugins 进行触发。

我们约定,按照传入的 Hook 对象的 name 来区分 Hook 类型,主要为以下三类:

  • 事件类型 Hook,Hook name 以 on 开头,如 onStart,这种类型的 Hook 只管触发而不关心 Hook 回调 fn 的值,Hook 的回调 fn 接收一个参数 opts ,为触发钩子时传入的参数
  • 修改类型 Hook,Hook name 以 modify 开头,如 modifyBuildAssets,这种类型的 Hook 触发后会返回做出某项修改后的值,Hook 的回调 fn 接收两个参数 optsarg ,分别为触发钩子时传入的参数和上一个回调执行的结果
  • 添加类型 Hook,Hook name 以 add 开头,如 addConfig,这种类型 Hook 会将所有回调的结果组合成数组最终返回,Hook 的回调 fn 接收两个参数 optsarg ,分别为触发钩子时传入的参数和上一个回调执行的结果

如果 Hook 对象的 name 不属于以上三类,则该 Hook 表现情况类似事件类型 Hook。

钩子回调可以是异步也可以是同步,同一个 Hook 标识下一系列回调会借助 Tapable 的 AsyncSeriesWaterfallHook 组织为异步串行任务依次执行。

ctx.registerMethod(arg: string | { name: string, fn?: Function }, fn?: Function)

ctx 上挂载一个方法可供其他插件直接调用。

主要调用方式:


ctx.registerMethod('methodName')
ctx.registerMethod('methodName', () => {
// callback
})
ctx.registerMethod({
name: 'methodName'
})
ctx.registerMethod({
name: 'methodName',
fn: () => {
// callback
}
})

其中方法名必须指定,而对于回调函数则存在两种情况。

指定回调函数

则直接往 ctx 上进行挂载方法,调用时 ctx.methodName 即执行 registerMethod 上指定的回调函数。

不指定回调函数

则相当于注册了一个 methodName 钩子,与 ctx.register 注册钩子一样需要通过方法 ctx.applyPlugins 进行触发,而具体要执行的钩子回调则通过 ctx.methodName 进行指定,可以指定多个要执行的回调,最后会按照注册顺序依次执行。

内置的编译过程中的 API 如 ctx.onBuildStart 等均是通过这种方式注册。

ctx.registerCommand(hook: ICommand)

注册一个自定义命令。

interface ICommand {
// 命令别名
alias?: string,
// 执行 taro <command> --help 时输出的 options 信息
optionsMap?: {
[key: string]: string
},
// 执行 taro <command> --help 时输出的使用例子的信息
synopsisList?: string[]
}

使用方式:

ctx.registerCommand({
name: 'create',
fn () {
const {
type,
name,
description
} = ctx.runOpts
const { chalk } = ctx.helper
const { appPath } = ctx.paths
if (typeof name !== 'string') {
return console.log(chalk.red('请输入需要创建的页面名称'))
}
if (type === 'page') {
const Page = require('../../create/page').default
const page = new Page({
pageName: name,
projectDir: appPath,
description
}) page.create()
}
}
})

ctx.registerPlatform(hook: IPlatform)

注册一个编译平台。

interface IFileType {
templ: string
style: string
script: string
config: string
}
interface IPlatform extends IHook {
// 编译后文件类型
fileType: IFileType
// 编译时使用的配置参数名
useConfigName: String
}

使用方式:

ctx.registerPlatform({
name: 'alipay',
useConfigName: 'mini',
async fn ({ config }) {
const { appPath, nodeModulesPath, outputPath } = ctx.paths
const { npm, emptyDirectory } = ctx.helper
emptyDirectory(outputPath) // 准备 miniRunner 参数
const miniRunnerOpts = {
...config,
nodeModulesPath,
buildAdapter: config.platform,
isBuildPlugin: false,
globalObject: 'my',
fileType: {
templ: '.awml',
style: '.acss',
config: '.json',
script: '.js'
},
isUseComponentBuildPage: false
} ctx.modifyBuildTempFileContent(({ tempFiles }) => {
const replaceKeyMap = {
navigationBarTitleText: 'defaultTitle',
navigationBarBackgroundColor: 'titleBarColor',
enablePullDownRefresh: 'pullRefresh',
list: 'items',
text: 'name',
iconPath: 'icon',
selectedIconPath: 'activeIcon',
color: 'textColor'
}
Object.keys(tempFiles).forEach(key => {
const item = tempFiles[key]
if (item.config) {
recursiveReplaceObjectKeys(item.config, replaceKeyMap)
}
})
}) // build with webpack
const miniRunner = await npm.getNpmPkg('@tarojs/mini-runner', appPath)
await miniRunner(appPath, miniRunnerOpts)
}
})

ctx.applyPlugins(args: string | { name: string, initialVal?: any, opts?: any })

触发注册的钩子。

传入的钩子名为 ctx.registerctx.registerMethod 指定的名字。

这里值得注意的是如果是修改类型添加类型的钩子,则拥有返回结果,否则不用关心其返回结果。

使用方式:

ctx.applyPlugins('onStart')

const assets = await ctx.applyPlugins({
name: 'modifyBuildAssets',
initialVal: assets,
opts: {
assets
}
})

ctx.addPluginOptsSchema(schema: Function)

为插件入参添加校验,接受一个函数类型参数,函数入参为 joi 对象,返回值为 joi schema。

使用方式:

ctx.addPluginOptsSchema(joi => {
return joi.object().keys({
mocks: joi.object().pattern(
joi.string(), joi.object()
),
port: joi.number(),
host: joi.string()
})
})

ctx.writeFileToDist({ filePath: string, content: string })

向编译结果目录中写入文件,参数:

  • filePath: 文件放入编译结果目录下的路径
  • content: 文件内容

ctx.generateFrameworkInfo({ platform: string })

生成编译信息文件 .frameworkinfo,参数:

  • platform: 平台名

ctx.generateProjectConfig({ srcConfigName: string, distConfigName: string })

根据当前项目配置,生成最终项目配置,参数:

  • srcConfigName: 源码中配置名
  • distConfigName: 最终生成的配置名

欢迎关注凹凸实验室博客:aotu.io

或者关注凹凸实验室公众号(AOTULabs),不定时推送文章:

Taro 2.2 全面插件化,支持拓展和定制个性化功能的更多相关文章

  1. TinyFrame升级之八:实现简易插件化开发

    本章主要讲解如何为框架新增插件化开发功能. 在.net 4.0中,我们可以在Application开始之前,通过PreApplicationStartMethod方法加载所需要的任何东西.那么今天我们 ...

  2. iOS 插件化开发汇总 Small框架

    应用插件化背景 目前很多应用功能越来越多,软件显得越来越臃肿.因此插件化就成了很多软件发展的必经之路,比如支付宝这种平台级别的软件: 页上密密麻麻的功能,而且还在增多,照这个趋势发展下去,软件包的大小 ...

  3. replugin插件化,插件转场动画失效的问题解决

    说明 随着应用功能的丰富,Android程序的安装包也逐渐变大,这成为应用程序现有框架下难以摆脱的瓶颈.所以引入了rePlugin插件化框架,将应用按功能拆分为插件,以此减小apk的大小并同时增加应用 ...

  4. 分享非常漂亮的WPF界面框架源码及插件化实现原理

      在上文<分享一个非常漂亮的WPF界面框架>中我简单的介绍了一个界面框架,有朋友已经指出了,这个界面框架是基于ModernUI来实现的,在该文我将分享所有的源码,并详细描述如何基于Mod ...

  5. MVC 插件化框架支持原生MVC的Area和路由特性

    .NET MVC 插件化框架支持原生MVC的Area和路由特性 前面开放的源码只是简单的Plugin的实现,支持了插件的热插拔,最近晚上偶然想到,原生的MVC提供Areas和RouteAtrribut ...

  6. .NET MVC 插件化框架支持原生MVC的Area和路由特性

    前面开放的源码只是简单的Plugin的实现,支持了插件的热插拔,最近晚上偶然想到,原生的MVC提供Areas和RouteAtrribute等路由特性标签,按照先前的做法,无法解析插件的路由特性和Are ...

  7. RN学习1——前奏,app插件化和热更新的探索

    react_native_banner-min.png React Native(以下简称RN)有大量前端开发者的追捧.前端开发是一个活跃的社区,一直尝试着一统前后端,做一个全栈开发,RN就是他们在客 ...

  8. 基于.NET MVC的高性能IOC插件化架构(一)

    最近闲下来,整理了下最近写的代码,先写写架构,后面再分享几个我自己写的插件 最近经过反复对比,IOC框架选择了Autofac,原因很简单,性能出众,这篇博文是我的各大IOC框架的性能测试:http:/ ...

  9. 基于.NET MVC的高性能IOC插件化架构

    基于.NET MVC的高性能IOC插件化架构 最近闲下来,整理了下最近写的代码,先写写架构,后面再分享几个我自己写的插件 最近经过反复对比,IOC框架选择了Autofac,原因很简单,性能出众,这篇博 ...

随机推荐

  1. 如何基于layui的laytpl实现数据绑定

    想了半天才想起自己园子的登录密码.可想而知,多长时间没登录了 正文一开始用layui做了几个管理系统,所以用起来觉得确实很容易上手,管理后台最常用的就是form和table以及弹窗类.layui提供的 ...

  2. 百度在PWA中阐述的弹性布局-[CSS]

    原文链接 响应式布局 自从进入移动互联网时代,响应式布局这个词经常出现在 Web 设计和开发领域,它让 Web 页面在不同尺寸的设备上都具有良好的浏览体验. 开始之前 在讲解响应式布局之前,需要先了解 ...

  3. javascript入门 之 zTree(十四 增删查改)(一)

    <!DOCTYPE html> <HTML> <HEAD> <TITLE> ZTREE DEMO - beforeEditName / beforeRe ...

  4. python3(二十二) oop

    """ 面向对象编程 """ __author__ = 'shaozhiqi' # 面向对象的程序设计把计算机程序视为一组对象的集合,而每个 ...

  5. 解决xcode ***is missing from working copy

    这是由于SVN置顶文件导致的,cd 至项目根目录 命令行 输入 find . -type d -name .svn | xargs rm -rf

  6. Vmware Centos 与 windows 创建共享目录

    一路路都是坑~~ 只为了安装orcle的jdk~~,然而Orcle下载jdk是需要登录才能下载的,所以我在Centos7下使用 wget / curl  都下载不了哦~jdk7 第一步:Vmvare ...

  7. 面试问了解Linux内存管理吗?10张图给你安排的明明白白!

    文章每周持续更新,各位的「三连」是对我最大的肯定.可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇) 今天来带大家研究一下Linux内存管理.对于精通 CURD 的业务同学 ...

  8. Windows Pains poj 2585

    Boudreaux likes to multitask, especially when it comes to using his computer. Never satisfied with j ...

  9. linux sysbench : CPU性能测试详解

    1.sysbench基础知识 sysbench的cpu测试是在指定时间内,循环进行素数计算 素数(也叫质数)就是从1开始的自然数中,无法被整除的数,比如2.3.5.7.11.13.17等.编程公式:对 ...

  10. ado.net 面向对象

    面向对象:就是一个大的转换器,建立起一条通道通往数据库然后通过通道将所需(方法)数据从转换器往返于外部界面端 1   首先在项目里创建文件夹:      右击项目———添加个文件夹App_Cod 2 ...