自 2.2 初步,Taro 引入了插件化机制,允许开发者通过编写插件的方法来为 Taro 拓展更多功用或许为本身业务定制个性化功用,欢迎我们进行测验,共同谈论~

其时版别 2.2.1

官方插件

Taro 供应了一些官方插件

  • @tarojs/plugin-mock,一个简易的数据 mock 插件

怎样引入插件

I ( 2 6 % y % &可以从 npm 或许本地中引入插件,引入方法首要/ g 6 A i ] ,通过 编译配备中的 pluginspresets,运用如下

plugins

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

plugins 字段取值为P 1 K . % F P v一个数组,Q # h M l ;配备方法如下:

const config = {
plug0 t ^ _ ?ins: [
//1 v O $ I / . 引入 npm 设备的插件
'@tarojs/plugin-mock',
// 引入 npm 设备的插h l  b u 8 W 6件,并传入插件参数
['@taX 7 z b ! =rojs/plugin-mock', {
mocks: {
'/api/user/1': {
name: 'jud1 K E Zy',
desc: 'Mental guy'
}
}
}],
// 从本地肯定途径引入插件,相同假设需求传入参数也是如上
O ! ? 7 A q'/absulute/path/plugin/filename',
]
}

presets

假设你有一系列插件需求配备,而他们一般是组合起来结束特定的事儿,那你可以通过插件集 presets 来进行配备。

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

coh N / l R l + snst config = {
presets: [
// 引入 npm 设备的插件集
'@tarojs/preset-sP a V Yth',
// 引入 npm 设备的插件} | 7 . A *集,并传入插件参数
['@tarojs/plugin-sth', {
arg0:a 3 m 3 7 J 'xe c : ! c Y m l Mxx'
}],
// 从本地肯定途径引入插件w / m | y - 0 e 0集,相同假设需求传入参数也是如上
'/absulute/path/p6 I l  ? k jreset/filename',
]
}

在了解完怎样引入插件之后,我们来学习一下怎样编写6 A s W H 9 j $ –一个插件。

怎样编写一个插件

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

exportN 6 } ~ K default (ctx, options) =&g~ i Q mt; {
// plugin 主体
ctx.onBuildStart(: T k ]() => {
console.log('编译初步!')
})
ctx.onBuildFinish( $ 9 ~ 1 r ) l Z() => {
console.log(S 2  T v o J'编译完毕!')
})
}

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

  • ctx:插件其时的运转环境信息,v [ y $ d f ? M @包含插N 7 *件相关的 API、其时运转参数、辅助方法等等
  • opI { 3 + 5 ; X & Dtions:为插件调用时传入的参数

在插件主体代码部分可以按照自己的需求编写相应代码,一般你可以结束以下功用。

Typings

主张运用 typescript 来编写插件,这样你就会获得很棒的智能提示,运用方法如下:

import { IPluginContext } from '@tar) h  `ojs/service'
export default (ctx: IPluginContext, pluginD z z ; u eOptsn & 0 v ? e X D :) => {
// 接下来运用 ctx 的时分就能获得智I  *能提示了
ctx.onBuildStart(() => {
console.log('编译初步!')
})
}

首要功用

指令行扩展

你可以通过编写插件来为 Taro 拓展指令行M ! * u ! U k G Z的指令,在之前版别的 Taro 中,指令行的指令是固定的,假设你要进行扩展,那你得直接批改 Taro 源码,而现在凭仗插件功用,你可以任意拓展 Taro 的指令n w . _ m D C E行。

这个功用首要通过 ctx.registerCommand API( U r | 来进行结束,例如,增加一个上传的指令,将编译后的代码上传到服务器:

export default (: 9 * ^ 3 g c ! Bctx) => {U { + F
ctx.registerCommand({
// 指令名
name: 'upload',
// 实行 taro upload --help 时输出的 options 信息
optionsMak = Z F q % jp: {
'--red L D l N Bmote': '服务器: M  7地址'
},
// 实行 tar) [ 4  ! )o upload --help 时输出的运用比方的信息
synopsisList: [
'taro upload --remote xxx.xxx.x~ e a ~ r txx.xxx'
],
asynd 6 1 ; ( &c fn () {
coM 3 [ C  #nst { remote } = ctx.runOpts
await uploadDist()
}
})
}

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

编译进程扩展

e 9 G 9 h = ;起你也可以通过插件对代码N w 2 s Y编译进程进& * : 8 2 ; Z行拓展。

正如前面所述,针对编译进程,J x ; T G n = ] MonBuildStartonBe / 0 8 =uildFinish 两个钩子来别离表明编译初步,编译完毕,而除此之外也有更多 API 来对编译进程进行批改o ~ Q,如下:

  • ctx.onBuildStart(() =&gp 1 a r O Y m ( 7t; viod),编译初步,接收一个回调函数
  • ctx.modifyWebpackChain(args: {] $ O { K y . f s chain: any }) => void),编译中批改 webpack 配备,在这个钩子中,你可以对 webpackChain 作出想要的调整,等同于配备 webpackChain
  • ctx.modifyBuildAssets(args: { assets: any }) => void),批改编译后的效果
  • ctx.modifyBuildTempFileContent(args: { tempFiles: any }) => void),批改编译进程中的中心文件,例如批改 app 或页面的 config 配备
  • ctx.onBuildFinish(() => viod)x [ 2 l,编译完毕,接收一个回调函数

编译途径L ( c { m K拓展

你也可以通过插件功[ E J Q 6用对编译途径进行拓展。

运用 API ctx.registerPlatform,Taro 中内置的渠$ 2 `道支撑都是通过这个 API 来进行结束。

留意:这是未竣工的功用,需求依托代码编译器$ 0 , c v g @tarojs/transform-wx 的改造结束

AP$ N % ] E A 3 . 0I

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

插件环境变量

ctx.paths

包含其时实行指令的相u 5 2 X U / 7关途径,全部的途径如下(并不是全部指令都会具有以下全部途径):

  • ctx.paths.appPath,其时指令实行的目录,假设是 build 指令则为其时项目途径
  • ctx.paths.configPath,其时项目配备目录,假设 init 指令,则没有此途径
  • ctx.paths.sourcePath,其时项目源码途径
  • ctx.paths.outputPath,其时项目输出代码途径
  • ctx.p # C O f ` ( yaE f u . 5 ;ths.nodeModulesPath,其时项目所用的 node_modules 途径

ctx.runOpts

获取其时实行指令所带的参数,例如指令 taro upload --remote xxx.xxx.xxx.xxx,则 ctx.runOpts 值为:

{
_: ['upload'],
options: {
remote: 'xxx.xxx.xxx.xxx'
},
isHelp: f3 $ ,  B K i O Galse
}

ctx.helper

为包 @tarojs/helper 的方便运用方法,包含其全部 API。

ctx.initialConfig

获取项目配备。

ctx.plugin8 o 8 B M 2 #s

获取其时全部挂载的插件。

插件方法R E y H ^ c & R

Taro 的插件架构基于 Tapable。

ctx.register(hook: IHook)

注册一个可供其他插件调用的钩C I 3 f l 5 |子,接收一个参数,即 Hook 方针。

一个 Hook 方针类型如下:_ q – r j $ )

interface IHook7 l * = m , Y ] {
// Hook 姓名,也会作为 Hook 标识
name: string
// Hook 所在4 { S -的 plugin id,不需求指定,HookO = @ A 9 a 挂载的时分会自动识别
plugin: string
// Hook 回调
fn: Function
before?: string
stage?: num_ ) d :ber
}

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

我们约好,按照传入的 Hook 目# ! Z标的 name 来区分 Hook 类型,首要为以下三类:

  • 事情类型 Hook,Hook na; – x m A : g } _me 以 on 最初,如 onStart,这种类型的 Hook 只管触发而不关心 Hook 回调 fn 的值– ] S,Hook 的回调 fn 接收一个参S G p [ lopts ,为触发钩子时传入的7 D `参数
  • 批改类型 Hook,Hook name 以Q # 2 I d 4 i – modify 最初,如 modifyBuildAssets,这种类型的 Hook 触发后会回来做出某项批改后的值,Hook 的回调 fn 接收两个参数 optsargg 2 3 & ~ K别离为触发钩子时传入的参数和上一个回调实行的效果
  • 增加类型 Hook,Hook name 以i ) . _ 6 [ Y ! add 最初,如 ads % | TdConfig,这种类型 Hook 会将全部回调的效果组合成数组终究回来,= / l /Hook 的回调 fn 接收两个^ j & = 9 $参数 optsarg ,别离为触发钩子时传入的参数和上一个回调实行的效果

假设 Hook 方针的 name 不属于以上三类,则该 Hook 表现情况类似事情类型 Hook。

钩子回调可所以异X K + * K v 3 A步也5 n i 3 * P C $可所以同步,同一个 Hook 标识下一系列回调会凭仗 Tapable 的 A4 H 1 9 s 4 ) –syncSeriesWaterfallHook 组织为异步串行使命顺次实行。

ctx.registerMethod(arg: string | { name: string, fn?: Function }P P o Y c k a, fn?: Functio7 b an)

ctx 上挂载一个方法可供其他插件直接调用。z r C 0 , a Y

首要调用方法:


cb $ b J a i [ _tx.registerMethod('methodName')
ctx.registerMethod('methodName', () =T . C Z> {
// callback
})
cti f {x.registerMethod({
name: 'methodName= N S e  a ; y'
})
ctx.r4 ( ( ^ k { qegi/ ? Y v + v + n hsterMethod({
name: 'methodh r E y } 7 ( ONak u U 6 M x u # gme',
fn: () => {
// callback
}
})

其间i o 3 z { [方法名有必要指定,而对于回调函j c u ]数则存在两种情况。

指定回调函数

则直接往 ctx 上进行挂载方法,调用时 ctx.methodName 即实行 registerMe5 ) s t H ~ !thod 上指定的回调函数。

不指定回调函数

则相当^ j ) b O于注册了一个 method9 X 3Name 钩子,与 ctx.register 注册钩子相同需求通过方法 ctx& # z p 2 j o Q Y.applyPlugins 进行触发,8 m –而具体要实行的钩子回调则通过 ctx.methodName 进行指定,可以指定多个要实行的回调,最后会按照注册次序顺次o 5 +实行。

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

ctx.registerC– x u M Z U [ Z [ommand(hook: ICommand)} 3 0 g

注册一l z b 6 q } D个自定义指令。

interface ICommand {
// 指令别号
alias?: string,
// 实行 taro <command> --X 5 [help( p c ; 时输5 ] h { U I / ~出的 opt 9 qtions 信息
optionsMap?: {
[key: string]: string
},
// 实行 taro <command> --hZ ^ @  j X /elp 时输出的运用比方的信息
synopsisList?: string[]
}

运用方法:

ctx.registerCommand({
name: 'create',
fn () {p m H s % / h &
const {
tyl G w z Gpe,
name,
description
} = ctx.runOpts
const { chalk } = ctx.helper
const% l Y * { appPath } = ctx.path% j ss
if (typeof name !== 'string') {
retu s / /rn console.log(chalk.red('请输入需求创立的页面称号'))
}
if (type === w 3 L ~ x V H i'page') {
const Page = require('../../create/page').default
const p4 O r . b Dage = new Page({
pageName: name,
projectDir: appPath,
descf , + y w D A 1rE f 6 iiption
})
page.create()
}
}
})

ctx.registerPlatform(hoX 9 W | 4 P 7ok: IPlatforz Z Nm)

注册一个编译途径。

interface IFileType {
templ: string
style:R 8 ` c S j [ string
script: string
config: string
}
interface IPlatform extends IHook {
/J U H u 5 r/ 编译后文件类型
fileType: IFileType
// 编译时运用的配备参数名
use~ I p z kConfigName: String
}

运用方法:

ctx.rv a ~ ] h oegQ q 3 ! h J n a 2isterPlatform({
name: 'alipay',
useConfigName: 'mini',
async fn ({ config }) {
const { appPath, nodeModulesPath, outputPath } = ctx.paths
const { npm, emptyDii ] 7 v ) zrectory } = ctxh X k f d.helper
emptyDirectory(outputPath)
// 准备 miniRunner 参数
const miB B 8 ; L v , A eniRunnerOpts = {
...config,
nodeModulesPath,2 2 L l I
buildAdapter: config.platform,
isBuildp n d / h o _Plugin: falsh 4 q [ de,
globalObject: !  D ]'my',
fileType: {
templ: '.awml',
style: '.acss'8 * a N y N 4 } I,
config: '.json',
script: '.jI | %s'
},
isUseComponentBuildPage: false
}
cs k A t 3 Itx.modifyBuildTempFileContent(({ tempFiles }) =&` - K | 8 Qgt; {
const replaceKeyMap = {
navigationBarTitleText: 'defaultTitle',
navigationBarBackgroundColor: 'titleBarColo. H _ ^r',
enablePullDownRefresh: 'pullRefresh'_ q ~ = q T k,
l* { iist: 'items',
text: 'name',
iconPath: 'icon',
selectedIconPath: 'activeIcon',
color: 'textColor'
}
Object.keys(tempFiles).forEach(key => {
const item = tempFiles[key]
if (item.config) {
recursiveReplaceObjectKeys(itev a  Q zm.config, replaceKeyMapA i ( $ f k Q)
}
})
})
// build with webpack
const miniRunner = awv W C % 3 j p /ait npm.getNpmPkg('@tarojs/mini-runner', appPath)
await miniRunner(appPath, mi# I W g B N 2niRunnerOpts)
}
})

ctx.appj $ E V K c E o RlyPluZ $ – ; A w p 2gins(args: string | { name: string, initialVal?: any, opts?: any })

触发注册的钩子。

传入的钩c } % 6 8 l A子名为 ctx.registerctx.registerMethod 指定的姓名。

这儿值得留意的是假设是批改类型增加类型的钩子,则具有回来效果,不然不必关心其回来效果。

运用方法:

ctx.applyPlugins(n S R I { : A {'onStart')
const assets = await ctx.applyPlugins({
name: 'modifyBuildAssets',
initc ^ Y 3 V x DialVal: assets,
opts: {
assets
}
})

ctx.addPluginOptsSchema(schema: Function)

为插件入参增加校验,承受一个函数类型参数,函数入参为 joi 方针,回来值为 joi se * A mchema。

运用方法:

ctx.addPluginOptsSchema(joi => {
return joi.object().keys({
mocks: joi.object().patternd . ^ % v F P(
joi.string(), joi.object()
),
port: joi.number(),
host: joi.string()
})
})

ctx.writeFileToDist({ filePath: string, conten, * p } m + It: string })

向编译v z t n x Z Z效果目录中写入文件,参数:

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

ctxz r s S P u ` 7 c.generateFrameworkInfo({ platform: strr U 8 ; 4ing })

生成编译信息文件 .frameworkJ ; ) G w Q ^info,参数:

  • pl1 8 1atform: 途径名

ctx.generateProj~ o . ) #ectConfig({ srcConfigName: string, distConfigName: string })

根据其时项目配备,生成终究项目配备,参数:

  • srcConfigName: 源码中配备名
  • d[ y o j @ ` bistConfigName: 终究生成的配备名

欢迎注重凹凸实验室博客g : 9 h z:aotu.io

或许注重凹凸实验室群众号(AOTULabs),不守时推送文章; – f r – ( n R

Taro 2.2 全面插件化,支撑拓展和定制个性化功用