Skip to content
目录

03 — 小程序模板、hydrate 与 setData

1. 模板体系的「两个维度」(源码注释即标准答案)

@tarojs/sharedtemplate.ts 文件头把小程序宿主差异抽象成两类问题:

1:13:packages/shared/src/template.ts
/**
 * 这里我们需要关心的小程序种类有两类:
 * 1. 模板递归:
 *  - 支持:tmpl0 套 tmpl0
 *  - 不支持:这就使得我们必须生成多级的模板,tmpl0 套 tmpl1,tmpl1 套 tmpl2……
 *           直到超过阈值 N (N = config.miniapp.baseLevel) tmplN 会套组件 comp,组件 comp 重新再套 tmpl0。
 * 2. 小程序脚本语言(wxs, sjs, etc...):
 *  - 支持:可以在模板使用函数缩减模板大小或提高性能(存疑),例如判断一个值是不是假值(falsy value)。
 *         将来或许会把数据序列化^1 的操作也放到小程序脚本语言里。
 *  - 不支持:使用纯 *xml 语法
 *
 * ^1: packages/taro-runtime/src/hydrate.ts
*/

简明表述

  • 递归模板:微信等部分平台允许模板自引用;不支持时就必须 展开层级,用 baseLevel 控制深度,深层用 自定义组件 comp 再切入新子树(控制模板体积与编译限制)。
  • WXS/SJS:可在模板里做简单逻辑,减少 wxml 体积;不支持时退化为 三元表达式等纯模板语法isUseXS 为 false 时的分支在 createMiniComponents 里可见)。

2. Adapter:把通用描述映射到具体小程序语法

同一套生成逻辑通过 IAdapter 映射到 wx:ifwx:for 或他端关键字:

44:77:packages/shared/src/template.ts
export interface IAdapter {
  if: string
  else: string
  elseif: string
  for: string
  forItem: string
  forIndex: string
  key: string
  xs?: string
  type: string
}

const weixinAdapter: IAdapter = {
  if: 'wx:if',
  else: 'wx:else',
  elseif: 'wx:elif',
  for: 'wx:for',
  forItem: 'wx:for-item',
  forIndex: 'wx:for-index',
  key: 'wx:key',
  xs: 'wxs',
  type: 'weapp'
}

platform-* 包会替换 Adapter 与 internalComponents 差异,实现 「一套生成器,多端拼字符串」

3. hydrate:从「类 DOM 树」到「小程序 data」

@tarojs/runtimehydrate 注释直接对齐 React 的命名:遍历 + 改数据形状,把 TaroElement 树变成小程序 setData 可用的 扁平/短键 结构:

23:50:packages/taro-runtime/src/hydrate.ts
/**
 * React also has a fancy function's name for this: `hydrate()`.
 * You may have been heard `hydrate` as SSR-related function,
 * actually, `hydrate` basicly do the `render()` thing, but ignore some properties,
 * it's a vnode traverser and modifier: that's exactly what Taro's doing in here.
 */
export function hydrate (node: TaroElement | TaroText): MiniData {
  // 初始化 componentsAlias
  componentsAlias ||= getComponentsAlias()
  // ...
  if (isText(node)) {
    return {
      sid: node.sid,
      [Shortcuts.Text]: node.nodeValue,
      [Shortcuts.NodeName]: componentsAlias[nodeName]?._num || '8'
    }
  }

设计原因

  • 小程序视图层只认 模板 + data,没有浏览器 DOM;Runtime 必须提供 稳定、可 diff 的数据协议Shortcuts 缩短 key 长度也是常见优化手段,见 sharedshortcuts)。

4. setData 调度:TaroRootElement

根节点 enqueueUpdate 收集 payload,在 performUpdate 里合并后调用小程序 ctx.setData。注意 scheduleTask 选用 setTimeout 而非微任务 的注释——这是实践里对 连续 setData时机 的取舍:

78:112:packages/taro-runtime/src/dom/root.ts
public scheduleTask(fn: TFunc) {
    // 这里若使用微任务可略微提前setData的执行时机,但在部分场景下可能会出现连续setData两次,造成更大的性能问题
    setTimeout(fn)
  }

  public enqueueUpdate (payload: UpdatePayload): void {
    this.updatePayloads.push(payload)

    if (!this.pendingUpdate && this.ctx) {
      this.performUpdate()
    }
  }

  public performUpdate (initRender = false, prerender?: TFunc) {
    this.pendingUpdate = true
    // ...
      while (this.updatePayloads.length > 0) {
        const { path, value } = this.updatePayloads.shift()!
        if (path.endsWith(Shortcuts.Childnodes)) {
          resetPaths.add(path)
        }
        data[path] = value
      }

后续同文件还会处理 CustomWrapper 路径:把更新拆到 自定义组件 上,减少整页 setData 数据量(大列表场景常见优化)。

5. 平台钩子:hostConfig 示例(微信)

platform-weappruntime-utils 导出 hostConfig,通过 mergeReconciler 把平台特有生命周期、混合原生时的 transferHydrateData 等注入运行时(与 plugin-inject 同类机制):

10:33:packages/taro-platform-weapp/src/runtime-utils.ts
export const hostConfig = {
  initNativeApi,
  getMiniLifecycle (config) {
    const methods = config.page[5]
    if (methods.indexOf('onSaveExitState') === -1) {
      methods.push('onSaveExitState')
    }
    return config
  },
  transferHydrateData (data, element, componentsAlias) {
    if (element.isTransferElement) {
      const pages = getCurrentPages()
      const page = pages[pages.length - 1]
      data[Shortcuts.NodeName] = element.dataName
      page.setData({
        [toCamelCase(data.nn)]: data
      })
      // ...
    }
  },
}

要点:这是 「内核 + 平台补丁」 模式:核心 runtime 保持通用,差异通过 hostConfig 合并。

6. 与「编译成各平台源码」的关系

严格说:

  • JS:编译成小程序可加载的 JS 包(+ 可能的预构建、代码分割),不是「翻译成 WXML 里的 JS」。
  • WXML/XML:由 模板引擎(shared + platform) 生成,与 hydrate 产出的 data 形状 协议对齐。
  • 样式:见 05-CSS与样式转换.md

参考资料

MIT License.