普通视图

发现新文章,点击刷新页面。
昨天 — 2026年2月27日首页

Taro是怎么实现一码多端的【底层原理】

2026年2月27日 18:20

1. Taro适配各端整体流程

开始了解各个细节之前,让我们先整体过一遍taro适配各端的流程(接下来将通过微信小程序、h5、React Native三端举例)

我们可简单将taro适配各端的过程分为 编译时运行时:

  • 编译时:将JSX转为平台代码,输出可部署的静态文件
  • 运行时:用户打开应用后如何处理用户交互、更新界面等等

1.1 编译时

整个编译时过程可概括为:

  1. 使用 Babel 解析 JSX 代码为抽象语法树(AST)
  2. 将 AST 转换为不同平台的模板语法
    • 微信小程序 → WXML
    • H5 → HTML
    • React Native → React Native 组件
  3. PostCSS 处理样式单位转换 (px → rpx/rem)

1.1.1 将JSX代码解析为AST (Parsing)

在编译时,Taro使用 @babel/parser 将JSX代码(Taro代码)解析为抽象语法树(AST)

Taro原代码:

function App() {
  return (
    <View className="container">
      <Text>Hello Taro</Text>
    </View>
  );
}

使用 @babel/parser 将JSX代码(Taro代码)解析为抽象语法树(AST):

{
  "type": "JSXElement",
  "openingElement": {
    "name": { "name": "View" },
    "attributes": [
      { "name": "className", "value": "container" }
    ]
  },
  "children": [
    {
      "type": "JSXElement",
      "openingElement": { "name": { "name": "Text" } },
      "children": [{ "type": "JSXText", "value": "Hello Taro" }]
    }
  ]
}

1.1.2 将AST转化为目标平台代码(Transformation)

通过 @babel/traverse 遍历AST,将其转化为目标平台代码

组件名映射:

// React 组件 → 平台组件
{
  'View': {
    'weapp': 'view',
    'h5': 'div',
    'rn': 'View'
  },
  'Text': {
    'weapp': 'text',
    'h5': 'span',
    'rn': 'Text'
  }
}

属性转化:

// className → class (小程序)
// className → className (RN)
// style 对象 → style 字符串 (小程序)
{ color: 'red', fontSize: 14 }"color: red; font-size: 14px"

事件映射:

{
  'onClick': {
    'weapp': 'bindtap',
    'alipay': 'onTap',
    'h5': 'onclick',
    'rn': 'onPress'
  },
  'onChange': {
    'weapp': 'bindinput',
    'h5': 'onchange',
    'rn': 'onChangeText'
  }
}

根据平台生产目标代码:

微信小程序 (WXML):

<view class="container">
  <text>Hello Taro</text>
</view>

H5 (HTML):

<div class="container">
  <span>Hello Taro</span>
</div>

React Native (JSX):

<View className="container">
  <Text>Hello Taro</Text>
</View>

1.2 运行时

当用户与界面交互时,会调用平台api、更新界面等等,taro是怎么做的呢,大致可分为以下流程:

  1. 创建平台无关的虚拟 DOM 树,使用diff算法比较新旧虚拟 DOM 树,计算最小更新集合
  2. 使用适配器模式统一不同平台的 API。
  3. 协调器负责将虚拟 DOM 的变化应用到真实平台。

1.2.1 虚拟 DOM (Virtual DOM)

Taro 使用虚拟 DOM 作为中间层,实现平台无关的 UI 描述。

虚拟节点结构:

class VNode {
  constructor(type, props, children) {
    this.type = type;        // 节点类型
    this.props = props;      // 属性
    this.children = children; // 子节点
  }
}

创建虚拟DOM:

const vnode = createElement(
  'View',
  { className: 'container' },
  createElement('Text', {}, 'Hello'));

// 结果:
// {
//   type: 'View',
//   props: { className: 'container' },
//   children: [
//     { type: 'Text', props: {}, children: ['Hello'] }
//   ]
// }

1.2.2 Diff算法

比较新旧虚拟 DOM 树,计算最小更新集合。

diff过程:

function diff(oldVNode, newVNode) {
  // 1. 节点类型变化 → REPLACE
  if (oldVNode.type !== newVNode.type) {
    return [{ type: 'REPLACE', oldVNode, newVNode }];
  }
  
  // 2. 属性变化 → UPDATE_PROPS
  const propPatches = diffProps(oldVNode.props, newVNode.props);
  
  // 3. 子节点变化 → UPDATE_CHILDREN
  const childPatches = diffChildren(oldVNode.children, newVNode.children);
  
  return [...propPatches, ...childPatches];
}

diff示例:

// 旧节点
<View className="box">
  <Text>Old</Text>
</View>

// 新节点
<View className="box updated">
  <Text>New</Text>
  <Button>Click</Button>
</View>

// Diff 结果
[
  { type: 'PROPS', patches: [{ key: 'className', value: 'box updated' }] },
  { type: 'CHILDREN', patches: [
    { index: 0, patches: [{ type: 'TEXT', value: 'New' }] },
    { index: 1, patches: [{ type: 'CREATE', vnode: Button }] }
  ]}
]

1.2.3 平台适配器 (Platform Adapter)

使用适配器模式统一不同平台的 API。

适配器接口:

class PlatformAdapter {
  createElement(type) {}
  createTextNode(text) {}
  setAttribute(element, key, value) {}
  appendChild(parent, child) {}
  // ... 其他 DOM 操作
}

微信小程序适配器:

class WeappAdapter extends PlatformAdapter {
  createElement(type) {
    // 创建小程序虚拟节点
    return new WeappElement(type);
  }
  
  render(element) {
    // 生成 WXML 模板
    return element.toTemplate();
  }
}

H5适配器:

class H5Adapter extends PlatformAdapter {
  createElement(type) {
    // 创建真实 DOM 节点
    return document.createElement(type);
  }
  
  render(element) {
    // 返回 HTML
    return element.outerHTML;
  }
}

1.2.4 协调器 (Reconciler)

协调器负责将虚拟 DOM 的变化应用到真实平台。

工作流程:

class Reconciler {
  mount(vnode, container) {
    // 1. 创建真实节点
    const node = this.createNode(vnode);
    
    // 2. 挂载到容器
    this.platformAdapter.appendChild(container, node);
  }
  
  update(newVNode) {
    // 1. Diff 计算补丁
    const patches = diff(this.currentVNode, newVNode);
    
    // 2. 应用补丁
    this.applyPatches(patches);
    
    // 3. 更新当前树
    this.currentVNode = newVNode;
  }
}

1.3 整体流程概述

image.png

2. 定位源码,细节拆解

了解了taro整个工作流程我们也许会产生几个问题:

  • babel将jsx代码转化为了ast,taro 是怎么遍历 ast 并将其转化为目标平台代码的
  • taro是怎么实现样式转换的
  • taro是怎么创建虚拟dom树,diff算法的细节
  • taro平台适配器的细节
  • taro是怎么适配不同平台的api,并且更新界面的

接下来我们逐一解答,并标注各自在源码中的实现位置:

2.1 babel将jsx代码转化为了ast,taro 是怎么遍历 ast 并将其转化为目标平台代码的

Taro 的 AST 转换主要在以下源码包中实现:

packages/taro-transformer-wx/
├── src/
│ ├── index.ts # 主入口,定义 Babel 遍历规则
│ ├── render.ts # JSX 渲染逻辑,处理条件、循环等
│ ├── jsx.ts # JSX 元素解析和转换
│ ├── class.ts # 类组件处理
│ └── utils.ts # 工具函数

完整转化流程:

image.png

JSX元素转换:

image.png

条件渲染转化流程:

image.png

循环渲染转换流程:

image.png

2.2 taro是怎么实现样式转换的

Taro 的样式转换主要通过 PostCSS 插件实现,源码位置:

packages/
├── postcss-pxtransform/ # 核心单位转换插件
│ ├── index.js # 主入口 (1-372行)
│ └── lib/
│ └── pixel-unit-regex.js # 像素单位正则匹配

├── taro-webpack5-runner/src/postcss/
│ ├── postcss.mini.ts # 小程序 PostCSS 配置 (7-99行)
│ ├── postcss.h5.ts # H5 PostCSS 配置 (7-106行)
│ └── postcss.harmony.ts # HarmonyOS PostCSS 配置 (7-100行)

├── taro-rn-style-transformer/ # React Native 样式转换
│ └── src/
│ ├── transforms/
│ │ ├── index.ts # 样式转换入口 (156-229行)
│ │ └── postcss.ts # PostCSS 插件配置 (1-113行)
│ └── config/
│ └── rn-stylelint.json # RN 样式校验规则

└── taroize/src/
└── wxml.ts # WXML 样式单位转换 (179-227行)

Taro使用多个 PostCSS 插件协同工作:

postcss-import // 处理 @import 语句

autoprefixer // 添加浏览器前缀

postcss-pxtransform // 单位转换 (核心)

postcss-html-transform // HTML 标签转换

postcss-url // 处理 url()

完整转换流程:

image.png

css单位转换流程:

image.png

平台转换规则对比:

image.png

内联样式转换流程:

image.png

2.3 taro是怎么创建虚拟dom树、diff算法的细节

Taro通过React的diff算法(react-reconciler) 实现新旧DOM树对比,但是创建虚拟DOM节点以及将 React 的更新转换为平台操作都是Taro实现的。

虚拟 DOM → Reconciler → 平台适配器 → 平台特定代码

Taro 虚拟DOM实现源代码位置:

packages/taro-runtime/src/
├── dom/
│ ├── node.ts (1-341行) # TaroNode 基类 ⭐
│ ├── element.ts # TaroElement 元素节点
│ ├── document.ts # TaroDocument 文档对象
│ ├── tree.ts # DOM 树操作
│ └── event-target.ts # 事件目标基类

├── hydrate.ts # 序列化虚拟 DOM
└── utils/index.ts # 工具函数

packages/taro-react/src/
├── reconciler.ts (1-500行) # React Reconciler 集成 ⭐
├── render.ts # 渲染函数
└── props.ts # 属性处理

虚拟DOM创建过程:

image.png

Diff算法详细过程:

image.png

属性diff过程:

image.png

子节点diff过程:

image.png

补丁应用流程:

image.png

2.4 taro平台适配器的细节

React 组件 → Taro Runtime → 平台适配层 → 平台特定代码

Taro 的平台适配器源码实现:

packages/taro-runtime/src/
├── dsl/
│ ├── common.ts (91-415行) # 页面配置创建 ⭐
│ ├── instance.ts # 实例管理
│ └── hooks.ts # 生命周期钩子

├── dom/
│ ├── root.ts # 根元素 (平台渲染入口)
│ ├── node.ts # 节点基类
│ └── element.ts # 元素节点

├── bom/
│ ├── document.ts # 文档对象适配
│ └── window.ts # 窗口对象适配

└── index.ts # 导出接口

packages/taro-platform-*/ # 各平台特定实现
├── taro-platform-weapp/ # 微信小程序
├── taro-platform-h5/ # H5
├── taro-platform-harmony/ # HarmonyOS
└── taro-platform-rn/ # React Native

平台适配器整体框架:

image.png

页面生命周期适配:

image.png

更新流程:

image.png

数据流转:

image.png

2.5 taro是怎么适配不同平台的api,并且更新界面的

核心 API 适配和更新文件源码位置:

packages/taro-runtime/src/
├── dom/
│ ├── root.ts (83-192行) # TaroRootElement 更新队列 ⭐
│ ├── node.ts (329-331行) # enqueueUpdate 入队更新 ⭐
│ ├── element.ts (205-278行) # 元素属性更新
│ └── style.ts (17-154行) # 样式更新

├── dsl/
│ ├── common.ts (91-415行) # createPageConfig 页面配置
│ └── next-tick.ts # nextTick 实现

└── interface/
└── hydrate.ts (6行) # setData 接口定义

packages/taro-api/src/
├── tools.ts # API 工具函数
└── interceptor/ # API 拦截器

packages/taro-h5/src/api/ # H5 API 实现
packages/taro-platform-weapp/ # 微信小程序 API 实现
packages/taro-platform-harmony/ # HarmonyOS API 实现

完整更新流程:

image.png

更新队列详细流程:

image.png

路径计算:

image.png

API统一封装:

image.png

3. Taro简单实现demo

Taro源码下载:github.com/NervJS/taro…

Taro deepwiki地址:

deepwiki.com/NervJS/taro

(用ai辅助实现效率更高)

❌
❌