小程序架构指南(五):源码详解小程序启动原理(下)
上篇文章中, 我们通过跟踪 React 源码, 找到了React-Reconciler
接管组件更新的原理. 但对小程序而言, React-Reconciler
接管组件更新还不够, 我们需要React-Reconciler
能够将组件的更新动作转化为界面更新指令并通知出来, 这样才能在 webview 层构建出实际 Dom. 而这, 就是HostConfig
和Container
的工作.
HostConfig 与 Container: Reconciler 与 Renderer 间的中间层
通过之前的文章我们知道, Fiber 架构下的 React 分为三层, 分别是对外的React Component API
, 也就是我们平常写的 JSX
, 和监控JSX
变动, 根据对应虚拟 Dom 结构变更生成界面操作指令的React-Reconciler
和将界面操作指令转化为对应平台实现的Renderer
渲染器.
React component API <----> Reconciler 调和器 ----> Renderer 渲染器
Reconciler
通过接管 useState
/setState
的实现获取 JSX
对象的变动情况, 并根据变动调用 JSX 对象的生命周期钩子和计算界面更新指令. 但具体实现时, Reconciler
会面临这样一个问题: 我怎么知道当前的 Renderer 渲染器支持哪些指令?
答案当然是在初始化Reconciler
时, 就要告诉Reconciler
当前渲染器支持的指令列表, 而这份列表, 就叫做HostConfig
.
对于 HostConfig, Reconciler
规定了两类 API, 分别是必须接口和可选接口.按 React 项目组的说法, 这些接口目前还不稳定所以并没有公开介绍. 但实际上, 这个功能已经可以满足日常使用了(要不怎么会有 Remax 项目&一众小程序项目). react 项目组给出了HostConfig 的示例, 这里贴一下 remax 中 hostConfig 的部分内容
1 |
|
Reconciler
会根据虚拟 Dom 变动情况, 调用HostConfig
中提供的接口, 这些调用方法和参数汇合到一起, 就是界面更新指令. 而对HostConfig
接口的调用又会被转发给Container
, 由Container
对象维护updateQueue
数组, 记录操作执行过程.
1 |
|
当Reconciler
的一个更新周期结束时, 会调用HostConfig
上的resetAfterCommit
函数, 然后被转发给Container
的applyUpdate
方法. Container
收到消息后, 将之前记录下来的界面更新指令 JSON 化为字符串, 通过 Native 转发给 运行在 webview 上的 webview-render
对象, webview-render 收到更新指令后, 根据指令操作实际 Dom, 界面构建完成.
webview-render: 更新指令的设计与用户交互的实现
界面的更新指令则由两种类型实现. SpliceUpdate
对应于节点变动, 前端收到后直接删除旧 Dom, 创建新 Dom. 但这样会出现问题. 例如, 对于<input value={$value} />
元素, 当 value 发生改变时, 如果直接删除重建 input 元素, 会导致输入光标丢失. 因此出现了SetUpdate
指令, 对于该指令, 只更新 Dom 属性, 不重建 Dom.
1 |
|
1 |
|
weview-render 收到指令后会根据 node 中的配置创建 Dom 元素, 并更新到 webview 中. 这个比较好实现, 直接document.createElement
就行. 前端 render 的难点在于: 如何将用户操作时产生的 click/touch/change 事件回传给 js-core 中的 Reconciler?
我们知道, jsx 中绑定的事件处理函数是不能在 json 化之后传递给 webview-render 的, 但是, 不能传递函数, 我们可以传递函数名啊
在生成 Dom 构建命令时, 我们可以建立一个事件处理函数映射表, 函数名命名规范为${事件名}_${递增计数器}_handler
. 在 webview 中则用 addEventListener 为对应 dom 节点绑定事件处理函数. 当事件发生时, 把 event 对象中的数据和需要调用的函数名通过 Native 传回 js-core 引擎, 然后在 js-core 中调用对应的实际函数, 触发组件状态变更, 组件重新渲染.
至此, 小程序运行流程形成闭环.
结尾的话
通过这五篇文章, 我们了解了小程序项目价值, 梳理了开发路线图, 解决了小程序开发过程中最为核心的数据传递和跨进程 Dom 交互问题. 但这并不意味着小程序任务的圆满结束. 事实上, 正如02-小程序业务流程与开发路线图
分析的那样, 后续的小程序基础库/IDE/后台/组件库更是小程序项目中所面临的难点.
不过, 这一系列的文章已经写得太长, 有必要在这里简单收束一下. 至于小程序项目中面临的其他问题该怎么解决嘛
欲知后事如何, 请待下回分解~