最新发布《数智时代的AI人才粮仓模型解读白皮书(2024版)》,立即领取! 了解详情
写点什么

你不知道的 virtual DOM(一):Virtual Dom 介绍

  • 2020-03-08
  • 本文字数:2393 字

    阅读完需:约 8 分钟

你不知道的virtual DOM(一):Virtual Dom介绍

一、前言

目前最流行的两大前端框架,React 和 Vue,都不约而同的借助 Virtual DOM 技术提高页面的渲染效率。那么,什么是 Virtual DOM?它是通过什么方式去提升页面渲染效率的呢?本系列文章会详细讲解 Virtual DOM 的创建过程,并实现一个简单的 Diff 算法来更新页面。本文的内容脱离于任何的前端框架,只讲最纯粹的 Virtual DOM。敲单词太累了,下文 Virtual DOM 一律用 VD 表示。


这是 VD 系列文章的开篇,后续还会有更多的文章带你深入了解 VD 的奥秘。

二、VD 是什么

本质上来说,VD 只是一个简单的 JS 对象,并且最少包含 tagpropschildren三个属性。不同的框架对这三个属性的命名会有点差别,但表达的意思是一致的。它们分别是标签名(tag)、属性(props)和子元素对象(children)。下面是一个典型的 VD 对象例子:


{  tag: "div",  props: {},  children: [    "Hello World",     {      tag: "ul",      props: {},      children: [{        tag: "li",        props: {          id: 1,          class: "li-1"        },        children: ["第", 1]      }]    }  ]}
复制代码


VD 跟 dom 对象有一一对应的关系,上面的 VD 是由以下的 HTML 生成的:


<div>  Hello World  <ul>    <li id="1" class="li-1">      第1    </li>  </ul></div>
复制代码


一个 dom 对象,比如 li,由 tag(li), props({id:1,class:"li-1"})children(["第",1])三个属性来描述。

三、为什么需要 VD

借助 VD,可以达到有效减少页面渲染次数的目的,从而提高渲染效率。我们先来看下页面的更新一般会经过几个阶段:



从上面的例子中,可以看出页面的呈现会分以下 3 个阶段:


  • JS 计算

  • 生成渲染树

  • 绘制页面


这个例子里面,JS 计算用了 691毫秒,生成渲染树 578毫秒,绘制 73毫秒。如果能有效的减少生成渲染树和绘制所花的时间,更新页面的效率也会随之提高。


通过 VD 的比较,我们可以将多个操作合并成一个批量的操作,从而减少 dom 重排的次数,进而缩短了生成渲染树和绘制所花的时间。至于如何基于 VD 更有效率的更新 dom,是一个很有趣的话题,日后有机会将另写一篇文章介绍。

四、如何实现 VD 与真实 DOM 的映射

我们先从如何生成 VD 说起。借助 JSX 编译器,可以将文件中的 HTML 转化成函数的形式,然后再利用这个函数生成 VD。看下面这个例子:


function render() {  return (    <div>      Hello World      <ul>        <li id="1" class="li-1">          第1        </li>      </ul>    </div>  );}
复制代码


这个函数经过 JSX 编译后,会输出下面的内容:


function render() {  return h(    'div',    null,    'Hello World',    h(      'ul',      null,      h(        'li',        { id: '1', 'class': 'li-1' },        '\u7B2C1'      )    )  );}
复制代码


这里的 h 是一个函数,可以起任意的名字。这个名字通过 babel 进行配置:


// .babelrc文件{ "plugins": [  ["transform-react-jsx", {   "pragma": "h"  // 这里可配置任意的名称  }] ]}
复制代码


接下来,我们只需要定义 h 函数,就能构造出 VD:


function flatten(arr) {  return [].concat.apply([], arr);}
function h(tag, props, ...children) { return { tag, props: props || {}, children: flatten(children) || [] };}
复制代码


h 函数会传入三个或以上的参数,前两个参数一个是标签名,一个是属性对象,从第三个参数开始的其它参数都是 children。children 元素有可能是数组的形式,需要将数组解构一层。比如:


function render() {  return (    <ul>      <li>0</li>      {        [1, 2, 3].map( i => (          <li>{i}</li>        ))      }    </ul>  );}
// JSX编译后function render() { return h( 'ul', null, h( 'li', null, '0' ), /* * 需要将下面这个数组解构出来再放到children数组中 */ [1, 2, 3].map(i => h( 'li', null, i )) );}
复制代码


继续之前的例子。执行 h 函数后,最终会得到如下的 VD 对象:


{  tag: "div",  props: {},  children: [    "Hello World",     {      tag: "ul",      props: {},      children: [{        tag: "li",        props: {          id: 1,          class: "li-1"        },        children: ["第", 1]      }]    }  ]}
复制代码


下一步,通过遍历 VD 对象,生成真实的 dom


// 创建dom元素function createElement(vdom) {  // 如果vdom是字符串或者数字类型,则创建文本节点,比如“Hello World”  if (typeof vdom === 'string' || typeof vdom === 'number') {    return doc.createTextNode(vdom);  }
const {tag, props, children} = vdom;
// 1. 创建元素 const element = doc.createElement(tag);
// 2. 属性赋值 setProps(element, props);
// 3. 创建子元素 // appendChild在执行的时候,会检查当前的this是不是dom对象,因此要bind一下 children.map(createElement) .forEach(element.appendChild.bind(element));
return element;}
// 属性赋值function setProps(element, props) { for (let key in props) { element.setAttribute(key, props[key]); }}
复制代码


createElement函数执行完后,dom 元素就创建完并展示到页面上了(页面比较丑,不要介意…)。


五、总结

本文介绍了 VD 的基本概念,并讲解了如何利用 JSX 编译 HTML 标签,然后生成 VD,进而创建真实 dom 的过程。下一篇文章将会实现一个简单的 VD Diff 算法,找出 2 个 VD 的差异并将更新的元素映射到 dom 中去。


PS: 想看完整代码见这里:


代码(https://gist.github.com/dickenslian/86c4e266ae5f2134373376133bec9e3d)

参考链接:


2020-03-08 19:241640

评论 2 条评论

发布
用户头像
这里不应该是 setProps 吧,应该是 setAttributes
2020-07-29 12:45
回复
哎,没看全,不好意思,忽视。这个方法命名好有歧义 setProps 的内部实现是 setAttributes 😂
2020-07-29 13:06
回复
没有更多了
发现更多内容

预见2024:开发体育直播平台有这4大新趋势

软件开发-梦幻运营部

VJ创作表演:Resolume Arena 6 for Mac中文版 含esolume arena 破解补丁

南屿

UML建模工具Astah Professional,实现逼真模型,让设计更加生动!

南屿

海外云手机开辟企业跨境电商新道路

Ogcloud

云手机 海外云手机 tiktok云手机 云手机海外版

软件测试学习笔记丨App压力测试

测试人

软件测试

企业到底怎么选择PaaS服务?

伤感汤姆布利柏

Lightroom Classic 2024 for Mac(LRC2024) v13.1.0中文激活版

iMac小白

群星Stellaris for mac(策略游戏)v3.10.1激活版下载

iMac小白

分布式ID介绍&实现方案总结

快乐非自愿限量之名

架构 分布式 项目开发

通过低代码开发实现数据可视化应用的简易指南

EquatorCoco

大数据 低代码 数据可视化

Bartender 5 for mac自定义隐藏及显示菜单栏

南屿

AE脚本-一键快速输出GIF动图格式插件 GifGun v2.0.15激活版

南屿

文明6铂金版 mac(策略游戏)v1.3.13中文激活版

iMac小白

文明6 Sid Meier‘s Civilization VI for mac(策略游戏)中文版

iMac小白

数维图数字孪生:能源电力行业三维可视化系统合集

2D3D前端可视化开发

大数据 物联网 可视化 智慧电力 数字孪生

云手机解决tiktok登录不上的问题

Ogcloud

云手机 海外云手机 tiktok云手机 云手机海外版 国外云手机

中国香港正在计划发行第二批代币化绿色债券

区块链软件开发推广运营

dapp开发 区块链开发 链游开发 NFT开发 公链开发

阿里云参编业内首个代码大模型标准,通义灵码获 2023 AI4SE “银弹” 案例

阿里巴巴云原生

阿里云 云原生

IPQ4019: Revolutionizing Long-Range Wireless Connectivity

wallyslilly

IPQ4019

海外云手机对于亚马逊卖家的作用

Ogcloud

云手机 海外云手机 云手机海外版

谁说后端不能画出美丽的动图?让我来给大家拜个年!

不在线第一只蜗牛

Java JavaScript 后端 动图

瀑布模型指南:优缺点和适合的团队领域

PingCode

项目管理 瀑布方法论

pdf编辑器 Acrobat Pro DC 2021 for Mac中文激活版

iMac小白

一文讲透低代码开发平台

高端章鱼哥

软件开发 低代码 JNPF

华为游戏中心“游学季”:跨界融合,探索“游戏+”正向价值

最新动态

如何查看电脑性能跑分?Geekbench极客系统跑分工具

南屿

Excel复杂表头按组按行复制

alexgaoyh

Java POI 合并单元格 复杂表头 模板生成

Cornerstone 4 for Mac(SVN管理工具) v4.2免激活版

iMac小白

Total Video Converter Pro超级转霸 Mac视频转换软件 v5.1.0下载

南屿

帝国时代3 for Mac中文激活版(酋长、亚洲王朝三合一完整版)

iMac小白

🚀2023最新版克魔助手抓包教程(9) - 克魔助手 IOS 数据抓包

你不知道的virtual DOM(一):Virtual Dom介绍_文化 & 方法_大白_InfoQ精选文章