浅析weex之vdom渲染

本文深入剖析Weex的源码,介绍了其DOM结构、初始化及数据更新过程,探讨了tree与node模式的渲染效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前段时间进行了weex页面尝试, 页面滚动加载渲染得非常流畅, 让H5页面拥有了native般的体验。

如此之利器,让人非常想探一究竟,因此最近进行了js-framwork源码学习(weex开源地址:https://github.com/alibaba/weex),希望能进一步了解其dom渲染机制。

一. 文件结构

weex代码结构如下,重点关注其js-framework实现。

0?wx_fmt=jpeg

阅读js-framework代码,我整理了一份思维导图。

0?wx_fmt=png

framework.js是Instance创建的入口,可以从这个文件开始自顶向下地阅读代码,了解其工作原理。可以重点理解它的DOM结构,初始化过程,数据更新过程,下面我也将从这几个方面进行描述。

二. 主要类分析

1. DOM、Listener与EventManager

0?wx_fmt=png

Weex的DOM结构由DocumentElementComment三类组成。Element创建普通节点,Comment用于创建frag block节点。每个节点都有一个唯一的ref值,可以很方便地在文档中被查询到,同时记录其父节点parentRef,通过这种’双向链表‘的操作可以方便进行节点拼接和获取。文档树节点Document记录整个DOM的结构,同时在Document上绑定EventManager事件处理器和Listener监听操作处理器。EventManager记录每个绑定了事件的节点和它对应的事件处理函数,提供事件的添加、删除和触发。Listener提供了dom操作转化为callNative的能力,通过将每一个操作转化为对应类型的actions,如createBodyaddElement,并将每一个actions记录updates数组。

2. compiler、directive、watcher

0?wx_fmt=png

  • observer 对 data 进行了监听,并且提供订阅某个数据项的变化的能力

  • compiler解析template,并解析其中的 directive,得到每一个 directive 所依赖的数据项及其更新方法

  • watcher 把上述两部分结合起来,即把 directive 中的数据依赖订阅在对应数据的 observer 上,这样当数据变化的时候,就会触发 observer,进而触发相关依赖对应的视图更新方法。

三. 初始化过程

当我们在浏览器中输入我们的bundle地址,其解析渲染为HTML过程大致可以分解为createInstance->initInstace->run bundle->define->boostrap->create Vm->生命周期函数。可细化为下面这些步骤:0?wx_fmt=png

initInstance: 根据webpack打包后的js代码来定义实例。define: 解析代码中的__weex_define__("@weex-component/bottom-bar")定义的component,包含依赖的子组件。并将component记录到customComponentMap[name] = exports数组中,维护组件与组件代码的对应关系。由于会依赖子组件,因此会被多次调用。

  • define执行后的APPInstance实例结构: 0?wx_fmt=png

bootstrap解析代码中的__weex_bootstrap__("@weex-component/30d1c553f95b5f87afb6a1cff70a7bbd")执行当前页面,提取customComponentMap中记录的组件代码进行初始化。只会执行一次。downgrade: 检测页面降级配置进行页面降级。initEvents: 绑定events和lifecycle(init、create、ready)执行的钩子。initScope: 执行initData()、initComputed、initMethods。初始化data、computed属性和methods,并进行data的observer监听。build: 根据预留选项opt.replace进行编译,目前该选项还未被实质使用。编译完成后执行ready的钩子命令,执行ready。compile: 编译视图。updateActions: 检测是否有数据更新需要执行。createFinish: 表明dom结构创建完成,想callQueue队列中添加一个'createFinish'的actions。processCallQueue: 依次执行队列中的actions,进行节点渲染到页面的过程,为了性能考虑,通过requestAnimationFrame进行分帧渲染。

  • callQueue队列 0?wx_fmt=png

通过初始化过程我们可以得到init -> 数据监听 -> created -> 视图生成 -> ready,为了避免重复的视图操作,可在init进行数据的获取,created阶段进行数据的赋值和修改。

部分过程细化

1. compile():

0?wx_fmt=png

  • tree:先渲染子节点树,最后渲染父节点

  • node:先渲染父节点,然后子节点一个个append

  • 进行了不同的节点数量,VM创建耗时采样对比,从图中可以看出当节点个数较多的时候tree模式比node模式渲染的快

0?wx_fmt=jpeg

2. attachTarget()、updateActions()、callTasks():

0?wx_fmt=pngattachTarget: 进行节点渲染的时候,将每个append动作细化为具体的actions,置入callQueue队列中。updateActions: 检测是否有diff,如果有,则执行diff中记录的taskcallTasks: 调用callNative,根据执行状态判断是否执行callQueue列表中的人物或者置入callQueue队列中。

四. 数据更新过程

执行click事件,其中修改了data数据值,执行顺序如下:

0?wx_fmt=pngCallJS响应事件、接受事件,通过eventManager获得事件目标响应函数并fire执行,通过Watcher监听数据修改,如果数据前后不等则将修改更新操作记入diff中,同时通知订阅它的依赖继续收集更新操作。最终执行updateActions完成数据更新操作。

总结

通过上文分析,可以认为:

  1. tree模式比node模式渲染的快。

  2. 每个节点的创建都对应一个callQueue任务,节点逐个逐个的append到页面中。

  3. 依赖发布订阅模式收集依赖,监听每一个属性的变化,可直接获取更新操作,映射到dom结构中。

  4. 与native交互通过 CallNative()方法;响应JS调用采用CallJS()方法。

以上是个人拙见,本文中描述不正确的地方欢迎指正~

作者简介:

汪怡欣,花名紫榆,淘宝-前端开发工程师,刚工作满一周年的前端小菜,一切才刚开始,希望未来能走得更远~


点击【阅读原文】,了解更多weex相关信息,微信中不能跳转的许多链接可以点击原文跳转。

MTT是手机淘宝技术团队(Mobile Taobao Tech team)的英文缩写,欢迎关注手机淘宝技术团队,一起交流分享无线技术,共创移动开发无限未来!扫描微信二维码关注我们!我们将分享更多的独家技术细节!

640?


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值