专栏名称: SegmentFault思否
SegmentFault (www.sf.gg)开发者社区,是中国年轻开发者喜爱的极客社区,我们为开发者提供最纯粹的技术交流和分享平台。
目录
相关文章推荐
程序员的那些事  ·  清华大学:DeepSeek + ... ·  2 天前  
程序员的那些事  ·  OpenAI ... ·  昨天  
OSC开源社区  ·  宇树王兴兴早年创业分享引围观 ·  3 天前  
程序猿  ·  “未来 3 年内,Python 在 AI ... ·  4 天前  
51好读  ›  专栏  ›  SegmentFault思否

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

SegmentFault思否  · 公众号  · 程序员  · 2018-10-08 08:00

正文

前言

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

这是VD系列文章的开篇,以下是本系列其它文章的传送门:

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

  • 你不知道的Virtual DOM(二):Virtual Dom的更新

  • 你不知道的Virtual DOM(三):Virtual Dom更新优化

  • 你不知道的Virtual DOM(四):key的作用

  • 你不知道的Virtual DOM(五):自定义组件

  • 你不知道的Virtual DOM(六):事件处理&异步更新

PS:系列文章可点击“ 阅读全文 ”查看。

VD是什么

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

  1. {

  2.    tag: "div",

  3.    props: {},

  4.    children: [

  5.        "Hello World",

  6.        {

  7.            tag: "ul",

  8.            props: {},

  9.            children: [{

  10.                tag: "li",

  11.                props: {

  12.                    id: 1 ,

  13.                    class: "li-1"

  14.                },

  15.                children: ["第", 1]

  16.            }]

  17.        }

  18.    ]

  19. }

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

  1.    Hello World

  2.    

    •        

    • id="1" class="li-1">
    •            第1

    •        

    •    

    一个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。看下面这个例子:

    1. function render() {

    2.    return (

    3.         <div>

    4.            Hello World

    5.            <ul>

    6.                <li id="1" class="li-1">

    7.                    1

    8.                li>

    9.            ul>

    10.        div>

    11.    );

    12. }

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

    1. function render() {

    2.    return h(

    3.        'div',

    4.        null,

    5.        'Hello World',

    6.        h(

    7.            'ul',

    8.            null,

    9.            h(

    10.                'li',

    11.                { id: '1', 'class': 'li-1' },

    12.                '\u7B2C1'

    13.            )

    14.        )

    15.    );

    16. }

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

    1. // .babelrc文件

    2. {

    3.  "plugins": [

    4.    ["transform-react-jsx", {

    5.      "pragma": "h"    // 这里可配置任意的名称

    6.    }]

    7.  ]

    8. }

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

    1. function flatten(arr) {

    2.    return [].concat.apply([], arr);

    3. }

    4. function h(tag, props, ...children) {

    5.    return {

    6.        tag,

    7.        props: props || {},

    8.        children: flatten(children) || []

    9.     };

    10. }

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

    1. function render() {

    2.    return (

    3.        <ul>

    4.            <li>0li>

    5.            {

    6.                [1, 2, 3].map( i => (

    7.                    <li>{i}li>

    8.                ))

    9.            }

    10.        ul>

    11.    );

    12. }

    13. // JSX编译后

    14. function render() {

    15.    return h(

    16.        'ul',

    17.        null,

    18.        h(

    19.            'li',

    20.            null,

    21.            '0'

    22.        ),

    23.        /*

    24.         * 需要将下面这个数组解构出来再放到children数组中

    25.         */

    26.        [1, 2, 3].map(i => h(

    27.            'li',

    28.            null,

    29.            i

    30.        ))

    31.    );

    32. }

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

    1. {

    2.    tag: "div",

    3.    props: {},

    4.    children: [

    5.        "Hello World",

    6.        {

    7.            tag: "ul",

    8.            props: {},

    9.            children: [{

    10.                tag







    请到「今天看啥」查看全文