编写端平台插件

扩展一个编译平台,需要编写一个 Taro 插件,对编译时和运行时分别进行兼容。

端平台插件架构

插件目录组织

@tarojs/plugin-platform-weapp 为例:

  1. ├── src 源码目录
  2. | ├── index.ts 插件入口
  3. | ├── program.ts 编译时入口
  4. | ├── template.ts 模板处理逻辑
  5. | ├── runtime.ts 运行时入口
  6. | ├── runtime-utils.ts 运行时依赖工具
  7. | ├── apis.ts API 相关处理
  8. | ├── apis-list.ts API 列表
  9. | ├── components.ts 组件列表
  10. | └── components-react.ts React 使用的组件类型
  11. ├── types 类型
  12. ├── index.js 编译时入口
  13. ├── tsconfig.json
  14. ├── rollup.config.json
  15. ├── package.json
  16. └── README.md

架构图

编写端平台插件 - 图1

编译时

处理编译相关操作,如 Webpack 配置、模板生成规则等。

一、编写 Taro 插件

前置阅读:【如何编写一个 Taro 插件】

首先我们需要编写一个 Taro 插件来注册我们的编译平台,如:

index.ts

  1. export default (ctx) => {
  2. ctx.registerPlatform({
  3. name: 'weapp',
  4. useConfigName: 'mini',
  5. async fn (arg) {
  6. // ...
  7. }
  8. })
  9. }

ctx.registerPlatform(options: object)

注册一个编译平台

options.name

string

平台名称,用于 CLI 编译命令。

如配置了 ‘xxx’,则编译此平台时使用的 CLI 命令为:

  1. taro build --type xxx
  2. taro build --type xxx --watch
options.useConfigName

string

把 Taro 编译配置中的指定字段纳入编译。

Taro 小程序相关配置默认放在 mini 字段下,因此一般情况配置 usingConfigName: mini 即可。

options.fn(arg)

function

端平台编译的入口函数,接受一个参数 arg,在此函数内我们可以开始编写端平台编译的逻辑。

arg

object

整合上述 options.useConfigName 指定字段后的 Taro 编译配置,编译配置各字段详情请看编译配置详情

二、编写平台类

接下来给上一节中提到的插件入口函数添加内容。

我们把编译时常用的逻辑抽象出了一个基类 TaroPlatformBase,开发者可以继承于此基类,从而实现端平台的编译。

然后在插件入口函数中调用上述自定义平台类的编译接口:

index.ts

  1. import Weapp from './program'
  2. export default (ctx) => {
  3. ctx.registerPlatform({
  4. name: 'weapp',
  5. useConfigName: 'mini',
  6. async fn (arg) {
  7. // 调用自定义平台类的 start 函数,开始端平台编译
  8. const program = new Weapp(ctx, config)
  9. await program.start()
  10. }
  11. })
  12. }

运行时

处理运行时相关操作,如 API、组件、Taro runtime 逻辑等。

一、处理运行时入口

1. 编写 runtime.ts

runtime.ts 是我们运行时的入口文件,Webpack 编译时会把它注入到 app.js 中进行引用。

例子:

runtime.ts

  1. import { mergeReconciler, mergeInternalComponents } from '@tarojs/shared'
  2. import { hostConfig, components } from './runtime-utils'
  3. mergeReconciler(hostConfig)
  4. mergeInternalComponents(components)

runtime-utils.ts

  1. export * from './components'
  2. export const hostConfig = {}

runtime.ts 主要负责:

  • 使用 mergeReconciler 函数把自定义的 hostConfig 合并到全局 Reconciler 中。
  • 使用 mergeInternalComponents 函数把自定义组件信息 components.ts 合并到全局 internalComponents 组件信息对象中。

抽取 runtime-utils.ts 是为了方便其它插件引用

2. 连接插件入口

为了让 Webpack 知道去哪里引用上述运行时入口文件,需要配置 runtimePath

program.ts

  1. class Weapp extends TaroPlatformBase {
  2. runtimePath = '@tarojs/plugin-platform-weapp/dist/runtime'
  3. }

二、处理 API

在 Taro 中,用户需要从 @tarojs/taro 中引用 Taro 的内置 API 和 Promise 化 后的小程序 API。

  1. import Taro from '@tarojs/taro'
  2. // 内置 API
  3. Taro.getCurrentInstance()
  4. // 小程序 API
  5. Taro.request()

1. 配置 initNativeApi

原始的 @tarojs/taro 包只提供了内置 API。我们需要通过配置 ReconcilerinitNativeApi 选项,为全局 Taro 对象增加小程序的 API 和我们想要挂载在 Taro 对象上的 API。

apis-list.ts

  1. // 需要新增额外的原生 API 时,分拆一个单独的 `apis-list.ts` 文件能有利于维护。
  2. // 同步 API
  3. export const noPromiseApis = new Set([
  4. 'getAccountInfoSync'
  5. ])
  6. // 异步 API,这些 API 都可以设置 `success`、`fail`、`complete` 回调,需要对它们进行 Promise 化。
  7. export const needPromiseApis = new Set([
  8. 'addCard'
  9. ])

apis.ts

  1. import { processApis } from '@tarojs/shared'
  2. import { noPromiseApis, needPromiseApis } from './apis-list'
  3. declare const wx: any
  4. export function initNativeApi (taro) {
  5. // 下文将详细介绍 processApis 函数
  6. processApis(taro, wx, {
  7. noPromiseApis,
  8. needPromiseApis
  9. })
  10. // 可以为 taro 挂载任意的 API
  11. taro.cloud = wx.cloud
  12. }

runtime-utils.ts

  1. import { initNativeApi } from './apis'
  2. export const hostConfig = { initNativeApi }

2. processApis(taro, global, options)

入参
参数类型说明
taroobjectTaro 对象
globalobject小程序全局对象,如微信的 wx
optionsobject配置项
options
属性类型说明
noPromiseApisSet<string>新增的同步 API
needPromiseApisSet<string>新增的异步 API

上述 processApis 函数帮助我们做了三件事情:

  1. 挂载所有平台公共的小程序 API 到 Taro 对象上
  2. 挂载常用的小程序全局对象属性 到 Taro 对象上
  3. 挂载用户传入的小程序 API 到 Taro 对象上

打包

插件使用 Rollup 进行打包,需要打包出以下文件:

入口文件模式必要说明
src/index.tscjs插件入口,供 Taro CLI 解析
src/runtime.tses运行时入口
src/runtime-utils.tses运行时工具集合,供继承的子类引用
src/components-react.tses有新增组件时需要实现,供 React 引用

注意,Taro 相关的包需要配置 external,以免重复打包:

rollup.config.js

  1. {
  2. external: ['@tarojs/shared', '@tarojs/service']
  3. }

类型

Taro 核心库维护的类型可能没有包括当前插件新增的组件和 API,这时我们需要对 @tarojs/taro@tarojs/components 进行模块补充 (module augmentation)

创建一个类型定义文件:

types/shims-iot.d.ts

  1. // 为支付宝 IOT 小程序拓展新增的 API 和组件定义
  2. import { ComponentType } from 'react'
  3. import Taro from '@tarojs/taro'
  4. declare module '@tarojs/taro' {
  5. namespace ix {
  6. function onCashierEventReceive (cb: any): void
  7. }
  8. }
  9. interface PosterProps {
  10. posid: string
  11. audible?: boolean
  12. onSuccess?: () => void
  13. onFail?: () => void
  14. onChange?: () => void
  15. }
  16. declare module '@tarojs/components' {
  17. export const Poster: ComponentType<PosterProps>
  18. }

开发者在类型定义文件中引入此文件即可:

global.d.ts

  1. /// <reference path="node_modules/@tarojs/plugin-platform-alipay-iot/types/shims-iot.d.ts" />