Taro 插件功能

2021-09-23 21:08 更新

Taro 引入了插件化機(jī)制,目的是為了讓開發(fā)者能夠通過編寫插件的方式來為 Taro 拓展更多功能或?yàn)樽陨順I(yè)務(wù)定制個(gè)性化功能。

官方插件

Taro 提供了一些官方插件

如何引入插件

你可以從 npm 或者本地中引入插件,引入方式主要通過 編譯配置中的 plugins 和 presets,使用如下

plugins

插件在 Taro 中,一般通過編譯配置中的 plugins 字段進(jìn)行引入。

plugins 字段取值為一個(gè)數(shù)組,配置方式如下:

  1. const config = {
  2. plugins: [
  3. // 引入 npm 安裝的插件
  4. '@tarojs/plugin-mock',
  5. // 引入 npm 安裝的插件,并傳入插件參數(shù)
  6. ['@tarojs/plugin-mock', {
  7. mocks: {
  8. '/api/user/1': {
  9. name: 'judy',
  10. desc: 'Mental guy'
  11. }
  12. }
  13. }],
  14. // 從本地絕對(duì)路徑引入插件,同樣如果需要傳入?yún)?shù)也是如上
  15. '/absulute/path/plugin/filename',
  16. ]
  17. }

presets

如果你有一系列插件需要配置,而他們通常是組合起來完成特定的事兒,那你可以通過插件集 presets 來進(jìn)行配置。

配置編譯配置中的 presets 字段,如下。

  1. const config = {
  2. presets: [
  3. // 引入 npm 安裝的插件集
  4. '@tarojs/preset-sth',
  5. // 引入 npm 安裝的插件集,并傳入插件參數(shù)
  6. ['@tarojs/plugin-sth', {
  7. arg0: 'xxx'
  8. }],
  9. // 從本地絕對(duì)路徑引入插件集,同樣如果需要傳入?yún)?shù)也是如上
  10. '/absulute/path/preset/filename',
  11. ]
  12. }

在了解完如何引入插件之后,我們來學(xué)習(xí)一下如何編寫一個(gè)插件。

如何編寫一個(gè)插件

一個(gè) Taro 的插件都具有固定的代碼結(jié)構(gòu),通常由一個(gè)函數(shù)組成,示例如下:

  1. export default (ctx, options) => {
  2. // plugin 主體
  3. ctx.onBuildStart(() => {
  4. console.log('編譯開始!')
  5. })
  6. ctx.onBuildFinish(() => {
  7. console.log('編譯結(jié)束!')
  8. })
  9. }

插件函數(shù)可以接受兩個(gè)參數(shù):

  • ctx:插件當(dāng)前的運(yùn)行環(huán)境信息,包含插件相關(guān)的 API、當(dāng)前運(yùn)行參數(shù)、輔助方法等等
  • options:為插件調(diào)用時(shí)傳入的參數(shù)

在插件主體代碼部分可以按照自己的需求編寫相應(yīng)代碼,通常你可以實(shí)現(xiàn)以下功能。

Typings

建議使用 typescript 來編寫插件,這樣你就會(huì)獲得很棒的智能提示,使用方式如下:

  1. import { IPluginContext } from '@tarojs/service'
  2. export default (ctx: IPluginContext, pluginOpts) => {
  3. // 接下來使用 ctx 的時(shí)候就能獲得智能提示了
  4. ctx.onBuildStart(() => {
  5. console.log('編譯開始!')
  6. })
  7. }

主要功能

命令行擴(kuò)展

你可以通過編寫插件來為 Taro 拓展命令行的命令,在之前版本的 Taro 中,命令行的命令是固定的,如果你要進(jìn)行擴(kuò)展,那你得直接修改 Taro 源碼,而如今借助插件功能,你可以任意拓展 Taro 的命令行。

這個(gè)功能主要通過 ctx.registerCommand API 來進(jìn)行實(shí)現(xiàn),例如,增加一個(gè)上傳的命令,將編譯后的代碼上傳到服務(wù)器:

  1. export default (ctx) => {
  2. ctx.registerCommand({
  3. // 命令名
  4. name: 'upload',
  5. // 執(zhí)行 taro upload --help 時(shí)輸出的 options 信息
  6. optionsMap: {
  7. '--remote': '服務(wù)器地址'
  8. },
  9. // 執(zhí)行 taro upload --help 時(shí)輸出的使用例子的信息
  10. synopsisList: [
  11. 'taro upload --remote xxx.xxx.xxx.xxx'
  12. ],
  13. async fn () {
  14. const { remote } = ctx.runOpts
  15. await uploadDist()
  16. }
  17. })
  18. }

將這個(gè)插件配置到中項(xiàng)目之后,就可以通過 taro upload --remote xxx.xxx.xxx.xxx 命令將編譯后代碼上傳到目標(biāo)服務(wù)器。

編譯過程擴(kuò)展

同時(shí)你也可以通過插件對(duì)代碼編譯過程進(jìn)行拓展。

正如前面所述,針對(duì)編譯過程,有 onBuildStart、onBuildFinish 兩個(gè)鉤子來分別表示編譯開始,編譯結(jié)束,而除此之外也有更多 API 來對(duì)編譯過程進(jìn)行修改,如下:

  • ctx.onBuildStart(() => viod),編譯開始,接收一個(gè)回調(diào)函數(shù)
  • ctx.modifyWebpackChain(args: { chain: any }) => void),編譯中修改 webpack 配置,在這個(gè)鉤子中,你可以對(duì) webpackChain 作出想要的調(diào)整,等同于配置 webpackChain
  • ctx.modifyBuildAssets(args: { assets: any }) => void),修改編譯后的結(jié)果
  • ctx.modifyBuildTempFileContent(args: { tempFiles: any }) => void),修改編譯過程中的中間文件,例如修改 app 或頁面的 config 配置
  • ctx.onBuildFinish(() => viod),編譯結(jié)束,接收一個(gè)回調(diào)函數(shù)

編譯平臺(tái)拓展

你也可以通過插件功能對(duì)編譯平臺(tái)進(jìn)行拓展。

使用 API ctx.registerPlatform,Taro 中內(nèi)置的平臺(tái)支持都是通過這個(gè) API 來進(jìn)行實(shí)現(xiàn)。

注意:這是未完工的功能,需要依賴代碼編譯器 @tarojs/transform-wx 的改造完成

API

通過以上內(nèi)容,我們已經(jīng)大致知道 Taro 插件可以實(shí)現(xiàn)哪些特性并且可以編寫一個(gè)簡(jiǎn)單的 Taro 插件了,但是,為了能夠編寫更加復(fù)雜且標(biāo)準(zhǔn)的插件,我們需要了解 Taro 插件機(jī)制中的具體 API 用法。

插件環(huán)境變量

ctx.paths

包含當(dāng)前執(zhí)行命令的相關(guān)路徑,所有的路徑如下(并不是所有命令都會(huì)擁有以下所有路徑):

  • ctx.paths.appPath,當(dāng)前命令執(zhí)行的目錄,如果是 build 命令則為當(dāng)前項(xiàng)目路徑
  • ctx.paths.configPath,當(dāng)前項(xiàng)目配置目錄,如果 init 命令,則沒有此路徑
  • ctx.paths.sourcePath,當(dāng)前項(xiàng)目源碼路徑
  • ctx.paths.outputPath,當(dāng)前項(xiàng)目輸出代碼路徑
  • ctx.paths.nodeModulesPath,當(dāng)前項(xiàng)目所用的 node_modules 路徑

ctx.runOpts

獲取當(dāng)前執(zhí)行命令所帶的參數(shù),例如命令 taro upload --remote xxx.xxx.xxx.xxx,則 ctx.runOpts 值為:

  1. {
  2. _: ['upload'],
  3. options: {
  4. remote: 'xxx.xxx.xxx.xxx'
  5. },
  6. isHelp: false
  7. }

ctx.helper

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

ctx.initialConfig

獲取項(xiàng)目配置。

ctx.plugins

獲取當(dāng)前所有掛載的插件。

插件方法

Taro 的插件架構(gòu)基于 Tapable

ctx.register(hook: IHook)

注冊(cè)一個(gè)可供其他插件調(diào)用的鉤子,接收一個(gè)參數(shù),即 Hook 對(duì)象。

一個(gè)Hook 對(duì)象類型如下:

  1. interface IHook {
  2. // Hook 名字,也會(huì)作為 Hook 標(biāo)識(shí)
  3. name: string
  4. // Hook 所處的 plugin id,不需要指定,Hook 掛載的時(shí)候會(huì)自動(dòng)識(shí)別
  5. plugin: string
  6. // Hook 回調(diào)
  7. fn: Function
  8. before?: string
  9. stage?: number
  10. }

通過 ?ctx.register? 注冊(cè)過的鉤子需要通過方法 ?ctx.applyPlugins? 進(jìn)行觸發(fā)。

我們約定,按照傳入的 Hook 對(duì)象的 ?name? 來區(qū)分 Hook 類型,主要為以下三類:

  • 事件類型 Hook,Hook name 以 ?on ?開頭,如 ?onStart?,這種類型的 Hook 只管觸發(fā)而不關(guān)心 Hook 回調(diào) fn 的值,Hook 的回調(diào) fn 接收一個(gè)參數(shù) ?opts? ,為觸發(fā)鉤子時(shí)傳入的參數(shù)
  • 修改類型 Hook,Hook name 以 ?modify ?開頭,如 ?modifyBuildAssets?,這種類型的 Hook 觸發(fā)后會(huì)返回做出某項(xiàng)修改后的值,Hook 的回調(diào) fn 接收兩個(gè)參數(shù) ?opts ?和 ?arg ?,分別為觸發(fā)鉤子時(shí)傳入的參數(shù)和上一個(gè)回調(diào)執(zhí)行的結(jié)果
  • 添加類型 Hook,Hook name 以 ?add ?開頭,如 ?addConfig?,這種類型 Hook 會(huì)將所有回調(diào)的結(jié)果組合成數(shù)組最終返回,Hook 的回調(diào) fn 接收兩個(gè)參數(shù) ?opts ?和 ?arg ?,分別為觸發(fā)鉤子時(shí)傳入的參數(shù)和上一個(gè)回調(diào)執(zhí)行的結(jié)果

如果 Hook 對(duì)象的 ?name ?不屬于以上三類,則該 Hook 表現(xiàn)情況類似事件類型 Hook。

鉤子回調(diào)可以是異步也可以是同步,同一個(gè) Hook 標(biāo)識(shí)下一系列回調(diào)會(huì)借助 Tapable 的 AsyncSeriesWaterfallHook 組織為異步串行任務(wù)依次執(zhí)行。

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

向 ctx 上掛載一個(gè)方法可供其他插件直接調(diào)用。

主要調(diào)用方式:

  1. ctx.registerMethod('methodName')
  2. ctx.registerMethod('methodName', () => {
  3. // callback
  4. })
  5. ctx.registerMethod({
  6. name: 'methodName'
  7. })
  8. ctx.registerMethod({
  9. name: 'methodName',
  10. fn: () => {
  11. // callback
  12. }
  13. })

其中方法名必須指定,而對(duì)于回調(diào)函數(shù)則存在兩種情況。

指定回調(diào)函數(shù)

則直接往 ctx 上進(jìn)行掛載方法,調(diào)用時(shí) ctx.methodName 即執(zhí)行 registerMethod 上指定的回調(diào)函數(shù)。

不指定回調(diào)函數(shù)

則相當(dāng)于注冊(cè)了一個(gè) methodName 鉤子,與 ctx.register 注冊(cè)鉤子一樣需要通過方法 ctx.applyPlugins 進(jìn)行觸發(fā),而具體要執(zhí)行的鉤子回調(diào)則通過 ctx.methodName 進(jìn)行指定,可以指定多個(gè)要執(zhí)行的回調(diào),最后會(huì)按照注冊(cè)順序依次執(zhí)行。

內(nèi)置的編譯過程中的 API 如 ctx.onBuildStart 等均是通過這種方式注冊(cè)。

ctx.registerCommand(hook: ICommand)

注冊(cè)一個(gè)自定義命令。

  1. interface ICommand {
  2. // 命令別名
  3. alias?: string,
  4. // 執(zhí)行 taro <command> --help 時(shí)輸出的 options 信息
  5. optionsMap?: {
  6. [key: string]: string
  7. },
  8. // 執(zhí)行 taro <command> --help 時(shí)輸出的使用例子的信息
  9. synopsisList?: string[]
  10. }

使用方式:

  1. ctx.registerCommand({
  2. name: 'create',
  3. fn () {
  4. const {
  5. type,
  6. name,
  7. description
  8. } = ctx.runOpts
  9. const { chalk } = ctx.helper
  10. const { appPath } = ctx.paths
  11. if (typeof name !== 'string') {
  12. return console.log(chalk.red('請(qǐng)輸入需要?jiǎng)?chuàng)建的頁面名稱'))
  13. }
  14. if (type === 'page') {
  15. const Page = require('../../create/page').default
  16. const page = new Page({
  17. pageName: name,
  18. projectDir: appPath,
  19. description
  20. })
  21. page.create()
  22. }
  23. }
  24. })

ctx.registerPlatform(hook: IPlatform)

注冊(cè)一個(gè)編譯平臺(tái)。

  1. interface IFileType {
  2. templ: string
  3. style: string
  4. script: string
  5. config: string
  6. }
  7. interface IPlatform extends IHook {
  8. // 編譯后文件類型
  9. fileType: IFileType
  10. // 編譯時(shí)使用的配置參數(shù)名
  11. useConfigName: String
  12. }

使用方式:

  1. ctx.registerPlatform({
  2. name: 'alipay',
  3. useConfigName: 'mini',
  4. async fn ({ config }) {
  5. const { appPath, nodeModulesPath, outputPath } = ctx.paths
  6. const { npm, emptyDirectory } = ctx.helper
  7. emptyDirectory(outputPath)
  8. // 準(zhǔn)備 miniRunner 參數(shù)
  9. const miniRunnerOpts = {
  10. ...config,
  11. nodeModulesPath,
  12. buildAdapter: config.platform,
  13. isBuildPlugin: false,
  14. globalObject: 'my',
  15. fileType: {
  16. templ: '.awml',
  17. style: '.acss',
  18. config: '.json',
  19. script: '.js'
  20. },
  21. isUseComponentBuildPage: false
  22. }
  23. ctx.modifyBuildTempFileContent(({ tempFiles }) => {
  24. const replaceKeyMap = {
  25. navigationBarTitleText: 'defaultTitle',
  26. navigationBarBackgroundColor: 'titleBarColor',
  27. enablePullDownRefresh: 'pullRefresh',
  28. list: 'items',
  29. text: 'name',
  30. iconPath: 'icon',
  31. selectedIconPath: 'activeIcon',
  32. color: 'textColor'
  33. }
  34. Object.keys(tempFiles).forEach(key => {
  35. const item = tempFiles[key]
  36. if (item.config) {
  37. recursiveReplaceObjectKeys(item.config, replaceKeyMap)
  38. }
  39. })
  40. })
  41. // build with webpack
  42. const miniRunner = await npm.getNpmPkg('@tarojs/mini-runner', appPath)
  43. await miniRunner(appPath, miniRunnerOpts)
  44. }
  45. })

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

觸發(fā)注冊(cè)的鉤子。

傳入的鉤子名為 ?ctx.register? 和 ?ctx.registerMethod? 指定的名字。

這里值得注意的是如果是修改類型和添加類型的鉤子,則擁有返回結(jié)果,否則不用關(guān)心其返回結(jié)果。

使用方式:

  1. ctx.applyPlugins('onStart')
  2. const assets = await ctx.applyPlugins({
  3. name: 'modifyBuildAssets',
  4. initialVal: assets,
  5. opts: {
  6. assets
  7. }
  8. })

ctx.addPluginOptsSchema(schema: Function)

為插件入?yún)⑻砑有r?yàn),接受一個(gè)函數(shù)類型參數(shù),函數(shù)入?yún)?joi 對(duì)象,返回值為 joi schema。

使用方式:

  1. ctx.addPluginOptsSchema(joi => {
  2. return joi.object().keys({
  3. mocks: joi.object().pattern(
  4. joi.string(), joi.object()
  5. ),
  6. port: joi.number(),
  7. host: joi.string()
  8. })
  9. })

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

向編譯結(jié)果目錄中寫入文件,參數(shù):

  • filePath: 文件放入編譯結(jié)果目錄下的路徑
  • content: 文件內(nèi)容

ctx.generateFrameworkInfo({ platform: string })

生成編譯信息文件 .frameworkinfo,參數(shù):

  • platform: 平臺(tái)名

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

根據(jù)當(dāng)前項(xiàng)目配置,生成最終項(xiàng)目配置,參數(shù):

  • srcConfigName: 源碼中配置名
  • distConfigName: 最終生成的配置名


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)