刺破 vue 的心脏之——详解 render function code

vue在使用上入门并无什么过高的门槛,但前端同窗们也不应仅仅停留在使用上。以 vue 设计、编码之优秀,足当抽丝剥茧,扒开它的外壳,深刻其原理。让咱们一块儿来刺破 vue 的心脏javascript

vue核心执行过程图

vue核心的执行过程主要分为这几个阶段:
1) 编译模板,生成可复用的render function code(这是今天要重点解读的),这一步在vue实例的整个生命周期中只会执行一次甚至零次,由于咱们能够在打包的时候能够预编译html

2) 生成watcher等核心渲染监听,在整个vue实例的生命过程当中持续发生着做用,对view和modal进行双向绑定前端

3) 虚拟dom的diff比较,当watcher监听到data的变动的时候,就会根据注入新的data执行render function code,生成新的虚拟dom,跟老的虚拟dom(第一次执行的时候可能为空)进行diff比对,不一样的部分将写入真实的domvue

这几个过程都会以源码解析的方式分篇解读,今天咱们重点讲解的是第一部分模板编译中输出的render function codejava

render function code解析

1、模板编译过程分解

// 生成ast语法树
const ast = parse(template.trim(), options)
// 标记静态内容(以避免diff的时候须要重复比较)
optimize(ast, options)
// 生成render function code
const code = generate(ast, options)复制代码

很遗憾,上诉三步的代码今天都只是点到为止,不须要太努力,就能在网上搜到相关解析的文章,若是实在找不到同时又感兴趣,能够找到vue源码中的下面文件去读:node

// 编译入口
src/compiller/index.js
// html解析
src/compiller/parser/html-parser.js
// src/compiller下的其它文件复制代码

换一个姿式读源码,今天咱们要读的是generate(ast, options)生成的render function code的具体代码webpack

2、写一个 vue demo

以前有提到过,读源码须要先了解总体的设计思想、架构,上面那个执行过程图在此列;另外,搭建一个demo执行环境进行debug单步调试,也是一个重要手段(特别是你对源码的目录结构不是特别清晰的时候)。为了让生成的render function code更为完整,写一个覆盖面尽可能广的 demo :web

//template
    <div id="app">
      <p>普通属性:{{ message }}</p>
      <p>{{msg()}}</p>
      <p>{{ct}}</p>
      <input v-model="message">
      <div v-for="item in items">
          {{ item.text }}
       </div>
       <button v-on:click="bindClick">点我抓同伟</button>
    </div>

    // js
    new Vue({
      el: '#app',
      data: {
        message: '以vue的名义',
        items: [{
            text: '达康书记'
        }, {
            text: '育良书记'
        }]
      },
      methods: {
        bindClick: function() {
            this.message = '这就抓同伟去';
        },
        msg: function() {
            return this.message + "这个方法每次都会执行";
        }
      },
      computed: {
        ct: function() {
            return this.message + "计算属性并不会每次都执行";
        }
      }
    })复制代码

render function code 解析

debug 拿到生成的 render function code(固然也能够经过 webpack 的 vue-loader 编译以后的 dist 文件拿到,此处省略1000字)express

with(this) {
    return _c('div', {
        attrs: {
            "id": "app"
        }
    },
    [_c('p', [_v("普通属性:" + _s(message))]), _v(" "), _c('p', [_v(_s(msg()))]), _v(" "), _c('p', [_v(_s(ct))]), _v(" "), _c('input', {
        directives: [{
            name: "model",
            rawName: "v-model",
            value: (message),
            expression: "message"
        }],
        domProps: {
            "value": (message)
        },
        on: {
            "input": function($event) {
                if ($event.target.composing) return;
                message = $event.target.value
            }
        }
    }), _v(" "), _l((items),
    function(item) {
        return _c('div', [_v("\n\t\t " + _s(item.text) + "\n\t ")])
    }), _v(" "), _c('button', {
        on: {
            "click": bindClick
        }
    },
    [_v("点我出奇迹抓同伟")])], 2)
}复制代码

甭看这段代码有点怪,可是若是告诉你,这段代码,注入 data 执行,生成的就是传得玄乎其玄的虚拟 dom 树,而后再来一本正经的解(cai)读(ce)一下,你又会以为这段代码其实没有这么晦涩难懂。ok,结合 demo
来看看,模板相关指令都被解析成什么了:架构

1){{messge}} 解析成了 _s(message),果断认为这个 _s 就是 toString

2){{msg()}} method 解析成了 _s(msg()),可见每一次 render, msg 方法都会被执行一遍(即便最终没有被反应到真实 dom 上),这就是计算属性存在的意义

3){{ct}} 计算属性依然被解析成了 _s(ct),虽然计算属性能够称之为属性,可是形式上毕竟仍是方法,是否是以为比较奇怪?其实 watcher 除了监听组件,还会监听计算属性依赖的属性,一旦属性发生变动,就会执行计算属性方法,并将执行结果赋值给实例做用域下与计算属性方法名相同的属性,这就是直接使用 _s(ct) 而不是 _s(ct()) 就能正确引用计算属性值的缘由,计算属性快就快在了这里

4)v-for="item in items" 被解析成了

_l((items),
    function(item) {
        return _c('div', [_v("\n\t\t " + _s(item.text) + "\n\t ")])
    })复制代码

可见 _l 跟 for-each 很相似

5) 至于 v-on:click 则解析成了

_c('button', {
        on: {
            "click": bindClick
        }
    }复制代码

6) 再来看看 _c, c->create->createNode, 假设这个方法就是建立虚拟节点,回头看上述代码,是否是挺有道理,_c 建立根节点,传入的子节点一样须要这个方法建立。找找源码验证下 src/core/vdom/vnode.js,如下中文注释是我加的

constructor (
    tag?: string,  //标签名
    data?: VNodeData, //属性数据,事件监听等
    children?: ?Array<VNode>, //子节点
    text?: string,
    elm?: Node,
    context?: Component,
    componentOptions?: VNodeComponentOptions
  ) {
    ...
  }复制代码

至此,也算是自圆其说了

公众号:菲麦前端
相关文章
相关标签/搜索