泛谈 Taro 原理

2,503 阅读6分钟

前言

由于工作原因,需要定制化 Taro 功能实现某些自定义功能。 所以对 Taro 原理以及源码有一点了解。

但经过我 2021 年的面试经历来看,大家并不在意类似 Taro 类型框架的原理。

但 Taro 框架,我理清源码后发现,其在国内开源的作品里,应该算是较好的一小撮了。

由于工作原因,我们的应用只有涉及 H5 / 小程序 双端兼容。 在这个前提下我来讲讲 Taro 的事情。


为什么我会觉得 Taro 是那么一小撮呢?

  1. 打磨时间较久,社区有一定响应速度。
  2. 完善的 startup project 方式以及 contribute 方式,方便社区人员参与。
  3. 完善的单元测试覆盖。(很少见,很良心)
  4. 开放的生态,Taro native 在 3.x 后甚至不是由 凹凸 团队负责实现开发的。本文二次编辑时,还仍然在积极接入适配 鸿蒙 生态。
  5. 良好的开发心态,及时汲取社区新的优秀实现,反馈到社区内。

但是可能我仍然觉得有一点不太好的地方。

  1. 没有杀手级的组件库配套。
  2. 社区 roadmap 没有提供,更多的参与者在 bug fix 而不是 feature contribute。
  3. 物料市场多少有点名存实亡。

本文并无全面细致的解析,源码级的代码阅读笔记并不打算开放。


Taro 1.x

原有的 Taro 版本是基于 Nerv JS 去做的适配。

是 React 代码 到 微信小程序代码的点对点转换。

(Nerv 特性使得编译后代码甚至可以兼容 ie8)

Taro3.x

分为 web端 / 小程序,适配兼容都是在 react-reconciler 里面,做 hostConfig ,然后不同的端使用不同的 document object 调度

web 端上的话,reconciler 还是调用的 document 。在小程序上的话 reconciler 调用的是自己抽象的 Taro Document

export const document = (isBrowser ? doc : createDocument()) as TaroDocumentInstance
web端

web端 不同的东西其实相对较少

  1. 会根据小程序配置文件,梳理路由打包,这块是使用的webpack loader 来做的这块事情(在主 APP 文件入口增加个个路由文件的 require,从而使得打包文件将其识别为chunk进行打包)
  2. 自建路由,为了与小程序路由栈行为一致。(只是增加了个个页面的显示隐藏的调度)(所以一个应用可能在一个h5上有多个页面实例)
  3. __taroAppConfig 全局变量可以获得当前的所有配置信息
  4. eventCenter可以监听到每个routerChange的事件
  5. 多端的一些模版适配是依赖 ionic/stencil 来做适配的(web-components的方案)。所以如果我们要动态使用组件的时候需要使用 applyPlugin 一下,不然动态组件无法使用。(这块loader帮我们做好了)
小程序端

小程序端的打包内容很繁琐很多,但是完全可以跳过,因为只是生成目标的运行模式而已!核心只要管setData的调度。

Taro的enqueueUpdate

Taro解决了一部分setData频率的问题,在你每次setState的时候实际上会去做一个简单的调度,再统一setData处理!

在小程序上的调度完全来自于enqueueUpdate

 截图的 Taro 小程序 Data

这是一个我们在小程序的page Data

我们可以看到里面完全没有我们应用的数据,例如没有我们应用实际的state

这里记载的大多数是一个个vnode的data(也就是一个节点应该长啥样子)

这里的字段含义解释一下 (我想这里缩写是可能尽可能让每一次setData的内容更小。)

Container = 'container',
Childnodes = 'cn',
Text = 'v',
NodeType = 'nt',
NodeName = 'nn',

// Attrtibutes
Style = 'st',
Class = 'cl',
Src = 'src'

其中这里uid还有一些别的功能

例如 在Taro document上实现eventTarget,包括getElementById也是通过uid来查找的。

每一次创建的时候会将一个uid 传入一个eventSource的set中保管(每一次会将当前的一个node节点set进去)。

调用getElementById则是通过去eventSource里面get一个id出来

每个页面路由的pageID是指向路由路径的。

数据的消费场景 这块数据的消费主要有两种场景

  1. 可递归组件
  2. 不可递归组件
    1. 有限制迭代

这块的模版在小程序会预先生成。在bundle中的 base.wxml 内。

小程序所有的setData都是在一个page Ctx下做的。 除非你单独处理,用customView来做。(可以做到局部刷新) 否则的话这块全是交由page Context统一setData

各端小程序其实大同小异,核心可能仅仅在一些tag 或者是attribute名有区别而已。

Taro在这方面也做了一层bundle小程序的抽象

taro-docs.jd.com/taro/docs/p…

让你可以做到 你仅仅只需要改变一个Adapter 上的属性就可以做到平台的扩展,核心是因为小程序的模式大同小异。

而Taro把这层code-generator 抽象了。(在后面添加更多平台时,基本上构建层模版是固定的,分发到各自平台的细节实现上。)

说多一嘴reconciler

Taro在原有的模式下扩展了hide 和 show方法

在不同端的reconciler 调用方法有所差异。

在web端可能就是调用真实dom上的一个insertBefore

在小程序端上可能就是调整整个data的一些属性,例如 当前为 x.[2] 调整为x.[3], 调用一个enqueueUpdate Path来setData到小程序上更新。(不过这里批量化调整的话会使用hydrate去做)

Taro在工程化上做的东西

  1. 样式适配的策略

    Taro 是使用postcss去做样式适配的,可以根据实际个人情况定义designWidth去设置

  2. Taro cli 底层其实自建了一个类似 yeoman-template 的模式去做用户自定义模版这块的东西

  3. Taro cli 每一个打包模式/命令 都抽象成为可插拔的plugin了,这块的调度则是使用Tapable去做的。

  4. 推荐的同构适配,依赖文件后缀名。这块则是通过enhance-resolve来自定义了resolve的一些方案,会根据你当前打包的flag 判断platform来优先resolve文件。

  5. 一些开箱即用的配置 cssnano terser uglify

etc...

附赠

小程序优化方案

小程序代码包下载的时候,尽可能让代码包要小一些。

(分包,图片cdn,代码打压缩,公共模块提取,避免无用依赖,静态资源尽可能缓存)

代码运行时候

避免节点过多,避免脚本阻塞过久。

setData的频率与数据量大小。

结束

  1. 本文只是列举了大概阅读的一些心得,方便日后根据关键字于代码中得到锚点。
  2. Taro 是一个很不错的框架,大家有兴趣完全可以阅读,完全不亏。

Contact Me

encode by base64

  • Email: a2FuZzk1NjMwQGdtYWlsLmNvbQ==
  • QQ: OTA3NzQ3ODc0QHFxLmNvbQo=

转载需注明出处与作者

否则将被视为侵权

Reprinting must indicate the source and author

Otherwise it will be regarded as infringement