Taro 引入了插件化機制,目的是為了讓開發(fā)者能夠通過編寫插件的方式來為 Taro 拓展更多功能或為自身業(yè)務定制個性化功能。
Taro 提供了一些官方插件
你可以從 npm 或者本地中引入插件,引入方式主要通過 編譯配置中的 plugins 和 presets,使用如下
插件在 Taro 中,一般通過編譯配置中的 plugins 字段進行引入。
plugins 字段取值為一個數(shù)組,配置方式如下:
const config = {
plugins: [
// 引入 npm 安裝的插件
'@tarojs/plugin-mock',
// 引入 npm 安裝的插件,并傳入插件參數(shù)
['@tarojs/plugin-mock', {
mocks: {
'/api/user/1': {
name: 'judy',
desc: 'Mental guy'
}
}
}],
// 從本地絕對路徑引入插件,同樣如果需要傳入?yún)?shù)也是如上
'/absulute/path/plugin/filename',
]
}
如果你有一系列插件需要配置,而他們通常是組合起來完成特定的事兒,那你可以通過插件集 presets 來進行配置。
配置編譯配置中的 presets 字段,如下。
const config = {
presets: [
// 引入 npm 安裝的插件集
'@tarojs/preset-sth',
// 引入 npm 安裝的插件集,并傳入插件參數(shù)
['@tarojs/plugin-sth', {
arg0: 'xxx'
}],
// 從本地絕對路徑引入插件集,同樣如果需要傳入?yún)?shù)也是如上
'/absulute/path/preset/filename',
]
}
在了解完如何引入插件之后,我們來學習一下如何編寫一個插件。
一個 Taro 的插件都具有固定的代碼結構,通常由一個函數(shù)組成,示例如下:
export default (ctx, options) => {
// plugin 主體
ctx.onBuildStart(() => {
console.log('編譯開始!')
})
ctx.onBuildFinish(() => {
console.log('編譯結束!')
})
}
插件函數(shù)可以接受兩個參數(shù):
在插件主體代碼部分可以按照自己的需求編寫相應代碼,通常你可以實現(xiàn)以下功能。
建議使用 typescript 來編寫插件,這樣你就會獲得很棒的智能提示,使用方式如下:
import { IPluginContext } from '@tarojs/service'
export default (ctx: IPluginContext, pluginOpts) => {
// 接下來使用 ctx 的時候就能獲得智能提示了
ctx.onBuildStart(() => {
console.log('編譯開始!')
})
}
你可以通過編寫插件來為 Taro 拓展命令行的命令,在之前版本的 Taro 中,命令行的命令是固定的,如果你要進行擴展,那你得直接修改 Taro 源碼,而如今借助插件功能,你可以任意拓展 Taro 的命令行。
這個功能主要通過 ctx.registerCommand API 來進行實現(xiàn),例如,增加一個上傳的命令,將編譯后的代碼上傳到服務器:
export default (ctx) => {
ctx.registerCommand({
// 命令名
name: 'upload',
// 執(zhí)行 taro upload --help 時輸出的 options 信息
optionsMap: {
'--remote': '服務器地址'
},
// 執(zhí)行 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 命令將編譯后代碼上傳到目標服務器。
同時你也可以通過插件對代碼編譯過程進行拓展。
正如前面所述,針對編譯過程,有 onBuildStart、onBuildFinish 兩個鉤子來分別表示編譯開始,編譯結束,而除此之外也有更多 API 來對編譯過程進行修改,如下:
你也可以通過插件功能對編譯平臺進行拓展。
使用 API ctx.registerPlatform,Taro 中內置的平臺支持都是通過這個 API 來進行實現(xiàn)。
注意:這是未完工的功能,需要依賴代碼編譯器 @tarojs/transform-wx 的改造完成
通過以上內容,我們已經(jīng)大致知道 Taro 插件可以實現(xiàn)哪些特性并且可以編寫一個簡單的 Taro 插件了,但是,為了能夠編寫更加復雜且標準的插件,我們需要了解 Taro 插件機制中的具體 API 用法。
包含當前執(zhí)行命令的相關路徑,所有的路徑如下(并不是所有命令都會擁有以下所有路徑):
獲取當前執(zhí)行命令所帶的參數(shù),例如命令 taro upload --remote xxx.xxx.xxx.xxx,則 ctx.runOpts 值為:
{
_: ['upload'],
options: {
remote: 'xxx.xxx.xxx.xxx'
},
isHelp: false
}
為包 @tarojs/helper 的快捷使用方式,包含其所有 API。
獲取項目配置。
獲取當前所有掛載的插件。
Taro 的插件架構基于 Tapable。
注冊一個可供其他插件調用的鉤子,接收一個參數(shù),即 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
? 進行觸發(fā)。
我們約定,按照傳入的 Hook 對象的 ?name
? 來區(qū)分 Hook 類型,主要為以下三類:
on
?開頭,如 ?onStart
?,這種類型的 Hook 只管觸發(fā)而不關心 Hook 回調 fn 的值,Hook 的回調 fn 接收一個參數(shù) ?opts
? ,為觸發(fā)鉤子時傳入的參數(shù)modify
?開頭,如 ?modifyBuildAssets
?,這種類型的 Hook 觸發(fā)后會返回做出某項修改后的值,Hook 的回調 fn 接收兩個參數(shù) ?opts
?和 ?arg
?,分別為觸發(fā)鉤子時傳入的參數(shù)和上一個回調執(zhí)行的結果add
?開頭,如 ?addConfig
?,這種類型 Hook 會將所有回調的結果組合成數(shù)組最終返回,Hook 的回調 fn 接收兩個參數(shù) ?opts
?和 ?arg
?,分別為觸發(fā)鉤子時傳入的參數(shù)和上一個回調執(zhí)行的結果如果 Hook 對象的 ?name
?不屬于以上三類,則該 Hook 表現(xiàn)情況類似事件類型 Hook。
鉤子回調可以是異步也可以是同步,同一個 Hook 標識下一系列回調會借助 Tapable 的 AsyncSeriesWaterfallHook 組織為異步串行任務依次執(zhí)行。
向 ctx 上掛載一個方法可供其他插件直接調用。
主要調用方式:
ctx.registerMethod('methodName')
ctx.registerMethod('methodName', () => {
// callback
})
ctx.registerMethod({
name: 'methodName'
})
ctx.registerMethod({
name: 'methodName',
fn: () => {
// callback
}
})
其中方法名必須指定,而對于回調函數(shù)則存在兩種情況。
則直接往 ctx 上進行掛載方法,調用時 ctx.methodName 即執(zhí)行 registerMethod 上指定的回調函數(shù)。
則相當于注冊了一個 methodName 鉤子,與 ctx.register 注冊鉤子一樣需要通過方法 ctx.applyPlugins 進行觸發(fā),而具體要執(zhí)行的鉤子回調則通過 ctx.methodName 進行指定,可以指定多個要執(zhí)行的回調,最后會按照注冊順序依次執(zhí)行。
內置的編譯過程中的 API 如 ctx.onBuildStart 等均是通過這種方式注冊。
注冊一個自定義命令。
interface ICommand {
// 命令別名
alias?: string,
// 執(zhí)行 taro <command> --help 時輸出的 options 信息
optionsMap?: {
[key: string]: string
},
// 執(zhí)行 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('請輸入需要創(chuàng)建的頁面名稱'))
}
if (type === 'page') {
const Page = require('../../create/page').default
const page = new Page({
pageName: name,
projectDir: appPath,
description
})
page.create()
}
}
})
注冊一個編譯平臺。
interface IFileType {
templ: string
style: string
script: string
config: string
}
interface IPlatform extends IHook {
// 編譯后文件類型
fileType: IFileType
// 編譯時使用的配置參數(shù)名
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 參數(shù)
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)
}
})
觸發(fā)注冊的鉤子。
傳入的鉤子名為 ?ctx.register
? 和 ?ctx.registerMethod
? 指定的名字。
這里值得注意的是如果是修改類型和添加類型的鉤子,則擁有返回結果,否則不用關心其返回結果。
使用方式:
ctx.applyPlugins('onStart')
const assets = await ctx.applyPlugins({
name: 'modifyBuildAssets',
initialVal: assets,
opts: {
assets
}
})
為插件入?yún)⑻砑有r?,接受一個函數(shù)類型參數(shù),函數(shù)入?yún)?joi 對象,返回值為 joi schema。
使用方式:
ctx.addPluginOptsSchema(joi => {
return joi.object().keys({
mocks: joi.object().pattern(
joi.string(), joi.object()
),
port: joi.number(),
host: joi.string()
})
})
向編譯結果目錄中寫入文件,參數(shù):
生成編譯信息文件 .frameworkinfo,參數(shù):
根據(jù)當前項目配置,生成最終項目配置,參數(shù):
更多建議: