title: TaroPlatformBase

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

例如我们创建一个微信小程序平台:

```js title=”program.ts” import { TaroPlatformBase } from ‘@tarojs/service’ export default class Weapp extends TaroPlatformBase { // … }

  1. ## 方法与属性
  2. ### constructor (ctx, config)
  3. 构造函数,接受两个参数。
  4. | 参数 | 类型 | 说明 |
  5. | :--- | :--- | :--- |
  6. | ctx | object | 插件上下文对象 |
  7. | config | object | Taro 编译配置 |
  8. ### ctx
  9. `object`
  10. 插件上下文对象。
  11. #### this.ctx.modifyWebpackChain
  12. 获取 WebpackChain,例子:
  13. ```js title="program.ts"
  14. class Weapp extends TaroPlatformBase {
  15. modifyWebpackChain () {
  16. // 通过 this.ctx.modifyWepackChain 能获取到 WebpackChain 实例
  17. this.ctx.modifyWebpackChain(({ chain }) => {
  18. // chain.xxxx
  19. })
  20. }
  21. }

helper

object

存放着一系列工具函数,对应 @tarojs/helper 包的导出内容。

config

object

编译配置对象。

(abstract) platform

抽象属性,子类必须实现。

string

平台名称,如:

```js title=”program.ts” class Weapp extends TaroPlatformBase { platform = ‘weapp’ }

  1. ### (abstract) globalObject
  2. > 抽象属性,子类必须实现。
  3. `string`
  4. 小程序挂载各种 API 的全局对象名称。如微信小程序的 `wx`,支付宝小程序的 `my`,例如:
  5. ```js title="program.ts"
  6. class Weapp extends TaroPlatformBase {
  7. globalObject = 'wx'
  8. }

(abstract) runtimePath

抽象属性,子类必须实现。

stirng | string[]

小程序编译的运行时文件的解析路径,如:

```js title=”program.ts” class Weapp extends TaroPlatformBase { runtimePath = ‘@tarojs/plugin-platform-weapp/dist/runtime’ }

  1. ### (abstract) fileType
  2. > 抽象属性,子类必须实现。
  3. `object`
  4. 平台的各种文件的后缀名,如:
  5. ```js title="program.ts"
  6. class Weapp extends TaroPlatformBase {
  7. fileType = {
  8. // 模板文件后缀
  9. templ: '.wxml',
  10. // 样式文件后缀
  11. style: '.wxss',
  12. // 配置文件后缀
  13. config: '.json',
  14. // 脚本文件后缀
  15. script: '.js',
  16. // 【可选】渲染层脚本文件后缀,如微信小程序的 wxs,支付宝小程序的 sjs
  17. xs: '.wxs'
  18. }
  19. }

(abstract) template

抽象属性,子类必须实现。

object

模板对象的实例。

(optional) projectConfigJson

子类可选择是否进行设置。

小程序配置文件的名称。

如果子类有实现 projectConfigJson,则会自动拷贝此文件到 dist 目录下。

```js title=”program.ts” class Weapp extends TaroPlatformBase { projectConfigJson = ‘project.config.json’ }

  1. ### (optional) taroComponentsPath
  2. > 子类可选择是否进行设置。
  3. 编译时对 `@tarojs/components` 包的 alias,下文将详细介绍。
  4. ```js title="program.ts"
  5. class Weapp extends TaroPlatformBase {
  6. taroComponentsPath = '@tarojs/plugin-platform-weapp/dist/components-react'
  7. }

setupTransaction

setup 阶段的事务钩子。

```js title=”program.ts” class Weapp extends TaroPlatformBase { /**

    1. setupTransaction - init
    1. setup
    1. setupTransaction - close
    1. buildTransaction - init
    1. build
    1. buildTransaction - close */ constructor (ctx, config) { super(ctx, config)

    this.setupTransaction.addWrapper({ init () {} close () {} }) } } ```

buildTransaction

build 阶段的事务钩子。

```js title=”program.ts” class Weapp extends TaroPlatformBase { /**

    1. setupTransaction - init
    1. setup
    1. setupTransaction - close
    1. buildTransaction - init
    1. build
    1. buildTransaction - close */ constructor (ctx, config) { super(ctx, config)

    this.buildTransaction.addWrapper({ init () {} close () {} }) } } ```

start ()

插件入口调用 start 方法开启编译,如:

```js title=”program.ts” class Weapp extends TaroPlatformBase { // … }

export default (ctx) => { ctx.registerPlatform({ name: ‘weapp’, useConfigName: ‘mini’, async fn ({ config }) { const program = new Weapp(ctx, config) await program.start() } }) }

  1. ### generateProjectConfig (src, dist)
  2. 用于生成 project.config.json 此类项目配置文件。
  3. | 参数 | 类型 | 默认值 | 说明 |
  4. | :--- | :--- | :--- | :--- |
  5. | src | string | | 项目源码中配置文件的名称 |
  6. | dist | string | 'project.config.json' | 编译后配置文件的名称 |
  7. 例子:
  8. ```js
  9. // 把用户编写的 `project.tt.json` 输出为 `project.config.json`
  10. generateProjectConfig('project.tt.json')
  11. // 把用户编写的 `project.swan.json` 输出为 `project.swan.json`
  12. generateProjectConfig('project.swan.json', 'project.swan.json')

recursiveReplaceObjectKeys (target, keyMap)

递归替换目标对象的 key 值。

参数 类型 说明
target object 目标对象
keyMap object key 值替换规则

例子,支付宝小程序配置项 key 值和大多数小程序的规范不一样,需要进行对齐:

  1. // this.ctx.modifyMiniConfigs 能获取到小程序入口和页面配置文件的列表
  2. this.ctx.modifyMiniConfigs(({ configMap }) => {
  3. const replaceKeyMap = {
  4. navigationBarTitleText: 'defaultTitle',
  5. navigationBarBackgroundColor: 'titleBarColor',
  6. enablePullDownRefresh: 'pullRefresh',
  7. list: 'items',
  8. text: 'name',
  9. iconPath: 'icon',
  10. selectedIconPath: 'activeIcon',
  11. color: 'textColor'
  12. }
  13. Object.keys(configMap).forEach(key => {
  14. const item = configMap[key]
  15. if (item.content) {
  16. // 递归替换配置文件里的 key 值为目标对象的 key 值
  17. this.recursiveReplaceObjectKeys(item.content, replaceKeyMap)
  18. }
  19. })
  20. })

自定义平台类

接下来将以扩展对微信小程序的编译支持为例,介绍如何创建一个自定义平台类。

1. 继承基类

继承 TaroPlatformBase 以实现 Weapp 类,并实现所有抽象属性、可选属性:

```js title=”program.ts” import { TaroPlatformBase } from ‘@tarojs/service’

const PACKAGE_NAME = ‘@tarojs/plugin-platform-weapp’

class Weapp extends TaroPlatformBase { // 平台名称 platform = ‘weapp’ // 小程序全局对象 globalObject = ‘wx’ // 小程序编译的运行时文件的解析路径 runtimePath = ${PACKAGE_NAME}/dist/runtime // 文件后缀 fileType = { templ: ‘.wxml’, style: ‘.wxss’, config: ‘.json’, script: ‘.js’, xs: ‘.wxs’ } template = new Template() // 小程序配置文件名称 projectConfigJson = ‘project.config.json’ // 对 @tarojs/components 包的 alias 路径 taroComponentsPath = ${PACKAGE_NAME}/dist/components-react

constructor (ctx, config) { super(ctx, config)

  1. /**
  2. * 1. setupTransaction - init
  3. * 2. setup
  4. * 3. setupTransaction - close
  5. * 4. buildTransaction - init
  6. * 5. build
  7. * 6. buildTransaction - close
  8. */
  9. // 可以在 setup 的不同阶段注入自定义逻辑
  10. this.setupTransaction.addWrapper({
  11. init () {}
  12. close () {}
  13. })
  14. // 可以在 build 的不同阶段注入自定义逻辑
  15. this.buildTransaction.addWrapper({
  16. init () {}
  17. close () {}
  18. })

} }

export default Weapp

  1. ### 2. 处理模板逻辑
  2. 编写一个[模板类]($docs-platform-plugin-template)以处理模板逻辑,把它的实例设置为自定义平台类的 `template` 属性:
  3. ```js title="program.ts"
  4. import { Template } from './template'
  5. class Weapp extends TaroPlatformBase {
  6. // ...
  7. // 模板实例
  8. template = new Template()
  9. }

3. 处理组件

我们把目前支持的 6 种小程序进行了组件和组件属性的比对,得出了一份最通用的组件以及其属性。访问 Template 类实例的 internalComponents 属性可以获取到这些通用组件以及属性。

抽取这份通用组件的目的是为了在生成 B 小程序的模板时,尽量不会含有 A 小程序独有的组件或属性。

但随着各小程序平台发展,各平台都可能独自新增一些组件或属性。这时我们的端平台插件就需要通过修改 template.internalComponents 来处理这些特殊的组件或属性。

3.1 编写 components.ts

components.ts 可以导出一个对象,以表示对 internalComponents 修改属性、新增属性或新增组件。

规范:

```js title=”components.ts” import { singleQuote } from ‘@tarojs/shared’

export const components = { // 组件名 CamelCase ScrollView: // 属性对象 { // value 为空字符串时,代表不设置默认值 ‘scroll-left’: ‘’, // 属性默认值为布尔值或数字时,value 写为字符串 ‘enable-flex’: ‘false’, ‘refresher-threshold’: ‘45’, // 属性默认值为字符串时,需要使用 singleQuote 函数进行包裹 ‘refresher-default-style’: singleQuote(‘black’), // 事件 bindRefresherAbort: ‘’ } }

  1. #### 3.2 合并到 template.internalComponents
  2. 编写好 `components.ts` 后,可以借助 `Template` 类实例的 `mergeComponents` 方法进行合并。
  3. ##### template.mergeComponents (ctx, patch)
  4. 合并组件补丁到 `this.internalComponents`
  5. | 参数 | 类型 | 说明 |
  6. | :--- | :--- | :--- |
  7. | ctx | object | 插件上下文对象 |
  8. | patch | object | 组件补丁 |
  9. 例子:
  10. ```js title="program.ts"
  11. import { components } from './components'
  12. class Weapp extends TaroPlatformBase {
  13. constructor (ctx, config) {
  14. super(ctx, config)
  15. // 在 setup 阶段结束时,修改模板
  16. this.setupTransaction.addWrapper({
  17. close: this.modifyTemplate
  18. })
  19. }
  20. modifyTemplate () {
  21. this.template.mergeComponents(this.ctx, components)
  22. }
  23. }

```js title=”components.ts” export const components = { ScrollView: { ‘enable-flex’: ‘true’, ‘refresher-threshold’: ‘45’ }, Xyz: { ‘a’: ‘’ } }

  1. 假设 `template.internalComponent` 的默认值为:
  2. ```js
  3. internalComponent = {
  4. ScrollView: {
  5. 'scroll-left': '',
  6. 'enable-flex': 'false',
  7. }
  8. }

合并后的结果为:

  1. internalComponent = {
  2. ScrollView: {
  3. 'scroll-left': '',
  4. // enable-flex 的默认值修改了
  5. 'enable-flex': 'true',
  6. // 新增了 refresher-threshold 属性
  7. 'refresher-threshold': '45'
  8. },
  9. // 新增了 Xyz 组件
  10. Xyz: {
  11. 'a': ''
  12. }
  13. }

3.3 直接修改 template.internalComponents

除了借助 template.mergeComponents 进行合并,我们也可以直接修改 template.internalComponents

```js title=”program.ts” class Weapp extends TaroPlatformBase { modifyTemplate () { // 删除 Slider 组件里的一些属性 this.modifySlider(this.template.internalComponents.Slider) // 改写 View 组件的属性对象 this.template.internalComponents.View = {} }

modifySlider (slider) { delete slider[‘block-size’] delete slider[‘block-color’] } }

  1. > 建议尽量编写一份 `components.ts` 进行 merge,而不是直接操作。因为运行时也需要合并后的组件信息,编写一份 `components.ts` 能进行复用。
  2. #### 3.4 编写 components-react.ts
  3. Taro 里使用 React,内置组件需要从 `@tarojs/components` 中引用后再使用。
  4. ```js
  5. import { View } from '@tarojs/components'

但如果我们增加了新的内置组件,再从 @tarojs/components 中引用就取不到这些新增的组件

因此当我们新增加了组件时,需要编写一份 components-react.ts,并配置 Webpack alias,供 React 引用。

例子:

  1. 编写 components-react.ts 文件

```js title=”components-react.ts” // 原有的组件 export * from ‘@tarojs/components/mini’ // 新增的组件 export const Editor = ‘editor’ export const OfficialAccount = ‘official-account’

  1. 2. 设置 [taroComponentsPath](./platform-plugin-base#optional-tarocomponentspath)
  2. ```js title="program.ts"
  3. const PACKAGE_NAME = '@tarojs/plugin-platform-weapp'
  4. class Weapp extends TaroPlatformBase {
  5. taroComponentsPath = `${PACKAGE_NAME}/dist/components-react`
  6. }