美文网首页
Vue.js之虚拟节点VNode

Vue.js之虚拟节点VNode

作者: KimYYX | 来源:发表于2017-11-09 15:38 被阅读0次

    众所周知,Vue.jsMVVM 框架结构,其特色在于核心重点关注视图层,而虚拟节点(以下简称 VNode)是这一核心的重要支柱(另一根支柱我觉得是实现响应式框架用到的 Watcher)。下面我们稍微深挖一下 VNode 的“背景”。


    一、初识

    首先看下 VNode 的类定义:

    /**
     * tag - 标签类型,例如 p、div
     * data - 标签上的数据,例如 style、class、data-*
     * children - 顾名思义,子节点
     * text - 文本内容
     * elm - 虚拟节点绑定的真实 DOM 节点
     * context - 一般是 Vue 实例
     * componentOptions - 父组件传给子组件的属性
     */
    var VNode = function VNode(tag, data, children, text, elm, context, componentOptions, asyncFactory) {
        this.tag = tag;
        this.data = data;
        this.children = children;
        this.text = text;
        this.elm = elm;
        this.ns = undefined;
        this.context = context;
        this.key = data && data.key;
        this.parent = undefined;
        this.raw = false;
        this.componentOptions = componentOptions;
        // ... 还有些属性我就不列出来了,大家可以自行去看源码
    };
    

    从定义可以看出,VNode 纯粹是对 View 层的映射,仅有属性,没有方法。

    接着看下 VNode 对节点的解释方式:以简单节点 <p>Hello</p> 为例,其会被解释成2个 VNode 的。一个是 tag 类型为 p ,但没有 text 值的节点,下文称为标签节点;另一个是没有 tag 类型,但是有 text 值的节点,下文称为文本节点

    再来了解下 VNode解析流程,看下面的代码

    <div id="app">
      <h1 class="h1" style="color:red;" data-id="1">Hello world</h1>
      <div id="wrap">
        <p id="text1">日期:2017-11-9</p>
        <p id="text2">时间戳:1510196747299</p>
      </div>
    </div>
    

    大家不妨可以先考虑下,这个页面结构会被拆成多少个节点,再来看下面各个 VNode 的创建顺序:

    1. 文本节点 Hello world
    2. 标签节点 h1
    3. 文本节点 日期:2017-11-9
    4. 标签节点 p#text1
    5. 文本节点 时间戳:1510196747299
    6. 标签节点 p#text2
    7. 标签节点 div#wrap
    8. 标签节点 div#app

    从上面的列表可以看出 Vue 对节点的解析是自上而下,从内到外解析的。那么当页面结构中含有 component 会如何解析呢?样例我就不写了,直接给个结果:Vue 会先解析主文档,然后解析组件,而父文档中的组件位置就是组件解析完之后的挂载点。


    二、Q & A

    1. Q:为什么VNode的解析顺序是自上而下的,从内到外的

    A:因为当你写的 HTML 经过 parseHTML 的方法后会产生类似如下的函数

    /**
     * _s = toString 方法
     * _v = 创建文本节点
     * _c = 创建标签节点
     */
    function f() {
     with (this) {
       return _c(
         'div', { attrs: { "id": "app" } },
         [
           _ c(
             'h1', { staticClass: "h1", staticStyle: { "color": "red" }, attrs: { "data-id": "1" } },
             [_v(_s(message))]
           )
         ]
       )
     }
    }
    

    正好符合JS的解析顺序。同时该方法会被缓存起来,下次有节点更新直接调用就好,一定程度上提高了页面渲染的性能。

    2. Q:节点上的 v-onv-bind 等指令是什么时候绑定的

    A:parseHTML 过程的 processAttrs 方法中


    三、阅读源码后的一点感想

    1. 需要比较扎实的正则能力,parseHTML 中的元素剥离就是靠的正则
    2. 了解ES2015的新API,例如 proxydefineProperty 等,这也是 Vue 不支持旧版浏览器的原因
    3. 闭包知识,常常一个方法被包了3层之多
    4. 1万多行的代码里面,好多都是常量定义和具方法,如果剥离出来,可能会更方便阅读

    相关文章

      网友评论

          本文标题:Vue.js之虚拟节点VNode

          本文链接:https://www.haomeiwen.com/subject/inkpmxtx.html