浅析weex之vdom渲染

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: # 前言 前段时间进行了weex页面尝试, 页面滚动加载渲染得非常流畅, 让H5页面拥有了native般的体验。 如此之利器,让人非常想探一究竟,因此最近进行了js-framwork源码学习(weex开源地址:),希望能进一步了解其dom渲染机制。 # 一. 文件结构 weex代码结构如下,重点关注其js-framework实现。 ``` ├── weex-de

前言

前段时间进行了weex页面尝试, 页面滚动加载渲染得非常流畅, 让H5页面拥有了native般的体验。
如此之利器,让人非常想探一究竟,因此最近进行了js-framwork源码学习(weex开源地址:https://github.com/alibaba/weex),希望能进一步了解其dom渲染机制。

一. 文件结构

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

    ├── weex-dev
      ├── android
      ├── ios
      ├── bin
      ├── doc
      ├── examples
      ├── src
      │  ├── components
      │  ├── h5-render
      │  ├── js-framework
      │  ├── README.md
      ├── test
      ├── website
      ├── index.html
      ├── package.json
      ├── webpack.config.js
      ├── README.md
      └── ...

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

文件结构图

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

二. 主要类分析

1. DOM、Listener与EventManager

DOM结构、Listener与EventManager类图

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

compiler、directive、watcher类
Weex复用了 Vue.js 的数据监听和依赖收集的代码实现。通过observer、directive、watcher之间的协作,建立数据(Model)和视图(View)的关联关系:

  • observer 对 data 进行了监听,并且提供订阅某个数据项的变化的能力
  • compiler解析template,并解析其中的 directive,得到每一个 directive 所依赖的数据项及其更新方法
  • watcher 把上述两部分结合起来,即把 directive 中的数据依赖订阅在对应数据的 observer 上,这样当数据变化的时候,就会触发 observer,进而触发相关依赖对应的视图更新方法。

三. 初始化过程

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

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

  • define执行后的APPInstance实例结构:
    define执行后的APPInstance实例结构

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队列
    callQueue

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

部分过程细化

1. compile():

compile
首先根据tagert的type类型选择不同的编译方式:数组类型、content类型(占位,可参考special-element)、repeat元素、if元素、function类型元素、子组件元素、内置定义的元素。内置元素类型可在config.js中查看,目前是text、image、container、slider、cell。以内置元素的编译为例,进行body和element节点的编译。在编译的时候会解析节点的attr、class、style、events指令,并进行监听。
从上图可知,weex提供了两种append方式:tree、node。

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

| 1个文本节点 | 第1次 | 第2次 | 第3次 | 第4次 | 第5次 | 平均 |
| ----- | ------ | ------- | ------- | ----- | ------ | ------- |
| tree | 8ms | 10ms| 12ms| 9ms| 10ms| 9.8ms|
| node | 10ms| 9ms| 9ms| 9ms| 9ms| 9.2ms|


| 20个文本节点 | 第1次 | 第2次 | 第3次 | 第4次 | 第5次 | 平均 |
| ----- | ------ | ------- | ------- | ----- | ------ | ------- |
| tree | 17ms | 18ms| 18ms| 16ms| 18ms| 17.4ms|
| node | 18ms| 17ms| 21ms| 17ms| 18ms| 18.2ms|


| 50个文本节点 | 第1次 | 第2次 | 第3次 | 第4次 | 第5次 | 平均 |
| ----- | ------ | ------- | ------- | ----- | ------ | ------- |
| tree | 32ms | 28ms| 26ms| 27ms| 27ms|28ms|
| node | 30ms| 29ms| 34ms| 33ms| 31ms| 31.4ms|


| 100个文本节点 | 第1次 | 第2次 | 第3次 | 第4次 | 第5次 | 平均 |
| ----- | ------ | ------- | ------- | ----- | ------ | ------- |
| tree | 44ms | 41ms| 37ms| 37ms| 44ms|40.6ms|
| node | 46ms| 44ms| 41ms| 44ms| 43ms| 43.6ms|

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


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

四. 数据更新过程

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


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

总结

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

  1. tree模式比node模式渲染的快。
  2. 每个节点的创建都对应一个callQueue任务,节点逐个逐个的append到页面中。
  3. 依赖发布订阅模式收集依赖,监听每一个属性的变化,可直接获取更新操作,映射到dom结构中。
  4. 与native交互通过 CallNative()方法;响应JS调用采用CallJS()方法。

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

目录
相关文章
|
移动开发 前端开发 weex
weex新版Layout引擎以及渲染逻辑探究
# weex新版Layout引擎以及渲染逻辑探究 ## 一、背景 原来weex sdk使用Facebook yoga进行基础css布局,但是由于开源协议问题选择基于Google的FlexboxLayout了自研,此处按下不表。 一言以蔽之,Layout引擎目的是通过递归的方式将节点的css属性约束析构,然后计算出节点正确的位置等基础属性。也就是说需要先明确一点,Layout引擎只
2064 0
|
移动开发 IDE weex
Weex 实时渲染插件
强大的[Weex](https://github.com/alibaba/weex)已开源, 截止现在 Star 的数量已经达到了3576, 简直赞到不行呀~~~~ 为了方便大家更加便捷的开发 Weex 页面, 小弟写了一款 Intellij 的插件, 目前亲测可用的几款IDE 有 `Intellij Idea`, `Android Studio`, `Web Storm`, 下面简单的为大
4126 0
|
7月前
|
移动开发 前端开发 JavaScript
探究移动端混合开发技术:React Native、Weex、Flutter的比较与选择
移动端混合开发技术在移动应用开发领域日益流行,为开发者提供了更高效的跨平台开发方案。本文将比较三种主流混合开发技术:React Native、Weex和Flutter,从性能、生态系统和开发体验等方面进行评估,以帮助开发者在选择适合自己项目的技术时做出明智的决策。
417 2
|
7月前
|
移动开发 前端开发 weex
移动端混合开发技术:React Native、Weex、Flutter 之争
在移动应用开发领域,React Native、Weex 和 Flutter 是备受关注的混合开发技术。本文将对它们进行全面比较与评估,以帮助开发者做出明智的选择。我们将从开发生态、性能、跨平台能力和易用性等方面进行比较,为读者提供全面的参考和指导。
|
7月前
|
移动开发 Dart 前端开发
移动端混合开发技术:React Native、Weex、Flutter的比较与选择
移动应用的开发已经成为现代社会中的重要一环。本文将比较并评估三种主流的移动端混合开发技术:React Native、Weex和Flutter。通过对它们的特点、优势和劣势的分析,帮助开发者在选择适合自己项目的技术方案时做出明智的决策。
|
7月前
|
移动开发 开发框架 前端开发
移动端混合开发技术探析:React Native、Weex、Flutter的比较与选择
随着移动应用开发的高速发展,混合开发技术成为了一种备受关注的选择。本文将对移动端混合开发技术中的React Native、Weex和Flutter进行比较与探讨,分析它们在性能、开发体验、生态系统和跨平台支持等方面的差异,以及如何根据项目需求进行选择。
221 1
|
移动开发 JSON JavaScript
weex开发 - VS Code解除格式警告
weex开发 - VS Code解除格式警告
119 0
weex开发 - VS Code解除格式警告
|
移动开发 JavaScript weex
weex开发- 无法找到模块“weex-vue-render”的声明文件。引入vue报错,无法找到引入的vue模块
weex开发- 无法找到模块“weex-vue-render”的声明文件。引入vue报错,无法找到引入的vue模块
310 0
weex开发- 无法找到模块“weex-vue-render”的声明文件。引入vue报错,无法找到引入的vue模块
|
weex-ui 移动开发 JavaScript
weex开发-使用weex-ui绑定事件源注意事项
weex开发-使用weex-ui绑定事件源注意事项
150 0
|
移动开发 JavaScript weex
weex开发 - 方法的映射,在weex调用fetch方法,实际调用同名的原生方法,在回调中把数据传递回js
weex开发 - 方法的映射,在weex调用fetch方法,实际调用同名的原生方法,在回调中把数据传递回js
210 0