Taro 的编译机制
Taro3之前(重编译时,轻运行时):
编译时是使用 babel-parser 将 Taro 代码解析成抽象语法树,然后通过 babel-types 对抽象语法树进行一系列修改、转换操作,最后再通过 babel-generate 生成对应的目标代码。
这样的实现过程有三⼤缺点:
- JSX ⽀持程度不完美。Taro 对 JSX 的⽀持是通过编译时的适配去实现的,但 JSX ⼜⾮常之灵活,因此还不能做到 100% ⽀持所有的 JSX 语法。 JSX 是一个 JavaScript 的语法扩展,它的写法千变万化,十分灵活。之前Taro团队是采用穷举的方式对 JSX 可能的写法进行了一一适配,这一部分工作量很大。
- 不⽀持 source-map。Taro 对源代码进⾏了⼀系列的转换操作之后,就不⽀持 source-map 了,⽤户 调试、使⽤这个项⽬就会不⽅便。
- 维护和迭代⼗分困难。Taro 编译时代码⾮常的复杂且离散,维护迭代都⾮常的困难。
再看⼀下运⾏时的缺陷。对于每个⼩程序平台,都会提供对应的⼀份运⾏时框架进⾏适配。当修改⼀些 Bug 或者新增⼀些特性的时候,需要同时去修改多份运⾏时框架。
总的来说,之前的Taro3.0之前有以下问题:
Taro3之后(重运行时):
Taro 3 则可以大致理解为解释型架构(相对于 Taro 1/2 而言),主要通过在小程序端模拟实现 DOM、BOM API 来让前端框架直接运行在小程序环境中,从而达到小程序和 H5 统一的目的,而对于生命周期、组件库、API、路由等差异,依然可以通过定义统一标准,各端负责各自实现的方式来进行抹平。而正因为 Taro 3 的原理,在 Taro 3 中同时支持 React、Vue 等框架,甚至还支持了 jQuery,还能支持让开发者自定义地去拓展其他框架的支持,比如 Angular,Taro 3 整体架构如下:
- 虚拟 DOM 转换:
- Taro 使用虚拟 DOM 的概念来实现跨平台开发。开发者编写的 Vue 代码会首先被转换为 Taro 的虚拟 DOM 结构。
- 在编译过程中,Taro 的编译器会将这些虚拟 DOM 节点映射到目标平台的具体组件和 API,例如将 Vue 的
template
语法转换为小程序的wxml
,并在逻辑层生成与之对应的 JavaScript 逻辑。
- 逻辑层与视图层:
- 编译后的代码分为逻辑层和视图层。逻辑层处理数据、事件和状态变化,通过 Taro 提供的 API(如
setData
)将数据同步到视图层。 - 视图层则是将虚拟 DOM 渲染为平台特定的视图,比如微信小程序的
wxml
和wxss
。在这个过程中,Taro 会生成相应的原生代码,以便在小程序环境中运行。
- 编译后的代码分为逻辑层和视图层。逻辑层处理数据、事件和状态变化,通过 Taro 提供的 API(如
- 多端适配:
- Taro 的编译器会针对不同平台的特性进行适配,例如微信小程序、支付宝小程序的组件名称、事件处理方式等。它会在编译时根据目标平台生成相应的原生代码,确保每个平台的表现一致。
uni-app 的编译机制
- 直接转换为原生代码:
- Uni-app 的编译过程更直接,开发者的 Vue 代码在编译时会被转换为各个平台的原生代码(如小程序的
wxml
、wxss
和 H5 的 HTML、CSS)。编译器直接将 Vue 的组件结构映射到对应平台的原生组件,无需经过虚拟 DOM 的模拟过程。
- Uni-app 的编译过程更直接,开发者的 Vue 代码在编译时会被转换为各个平台的原生代码(如小程序的
- 统一的 API 接口:
- Uni-app 在编译时会将 Vue 代码中的 API 调用(如路由、请求等)转换为适合各个平台的原生 API。例如,
uni.navigateTo
会被编译为小程序或 H5 的对应导航逻辑。
- Uni-app 在编译时会将 Vue 代码中的 API 调用(如路由、请求等)转换为适合各个平台的原生 API。例如,
- 平台适配层:
- Uni-app 内置了适配层,负责在不同平台间屏蔽 API 和组件的差异。编译时,Uni-app 会根据目标平台生成适配代码,确保开发者使用的 API 在不同平台上都有一致的行为。
编译流程对比
- Taro 编译流程:
- 解析:解析 Vue 代码,生成虚拟 DOM 结构。
- 逻辑层生成:生成对应逻辑层代码,处理事件、状态等。
- 视图层生成:将虚拟 DOM 转换为目标平台的原生视图代码(如
wxml
)。 - 平台适配:在编译时,根据不同平台的特点调整生成的代码。
- uni-app 编译流程:
- 解析:直接解析 Vue 代码,识别组件、指令等。
- 原生代码生成:将 Vue 组件直接映射为各个平台的原生代码(如小程序的
wxml
和wxss
)。 - API 映射:将 Vue 中使用的 API 调用映射为对应平台的原生 API。
- 适配层处理:根据目标平台生成适配代码,确保一致性。
- 总结
- Taro 的编译过程通过虚拟 DOM 的方式增加了灵活性,但同时引入了额外的性能开销,尤其是在处理复杂状态和事件时。
- Uni-app 则采用直接映射的方式,将开发者的 Vue 代码转换为原生代码,减少了中间抽象层,通常在性能上更具优势。
这种底层实现的区别影响了框架的性能、开发体验以及在不同平台间的适配能力。在选择时,可以根据具体的项目需求和性能要求进行评估。
运行时区别
在 Taro 和 Uni-app 这两个框架中,Taro 是有运行时(runtime)的,而 Uni-app 则没有独立的运行时,主要依赖编译时生成的原生代码。
Taro 的运行时
- Taro 的运行时作用:Taro 的核心之一是运行时机制(runtime),这是因为 Taro 需要在不同的平台上模拟 Vue 或 React 的生命周期、事件处理和组件更新。这一运行时主要是在逻辑层执行的,它负责处理虚拟 DOM 的更新、组件状态的管理、事件的冒泡和捕获机制等。Taro 的运行时通过
setData
将逻辑层的数据和状态同步到视图层,并且在视图层渲染出小程序等平台的原生 UI。 - 运行时的存在原因:由于 Taro 是基于虚拟 DOM 实现的,它必须依赖运行时在逻辑层处理虚拟 DOM 操作,并将这些操作同步到不同平台的渲染机制。这也意味着 Taro 在运行时阶段会有额外的性能开销,尤其是在频繁的数据更新和复杂组件交互时。
uni-app 的无运行时架构
- uni-app 的编译时架构:相比之下,Uni-app 没有类似 Taro 的运行时。Uni-app 通过编译时直接将 Vue 代码转译成各个平台的原生代码(如微信小程序的
wxml
和wxss
),在各个平台的运行时阶段,使用的都是平台的原生渲染引擎和生命周期管理机制。因此,Uni-app 不需要额外的运行时来执行 Vue 组件的更新和渲染逻辑。 - 依赖平台的原生运行时:Uni-app 的设计是为了直接利用平台的原生运行时来处理组件渲染和事件响应。例如,在微信小程序中,Uni-app 编译的代码会直接与微信小程序的原生运行时对接,而不需要像 Taro 那样通过中间的虚拟 DOM 层进行处理。
总结
- Taro 有运行时,因为它使用虚拟 DOM 和逻辑层管理机制来适配多平台。这种架构为开发者提供了更灵活的开发体验,但也带来了运行时的性能开销。
- Uni-app 没有独立的运行时,它依赖于编译时将代码转化为原生平台的代码,并直接利用平台的原生运行时进行渲染和操作,性能开销较小。
因此,Taro 在不同平台上的代码执行过程会依赖其运行时,而 Uni-app 则是尽可能地依赖各个平台的原生运行机制。