vue

161 阅读2分钟
  • new Vue()做了什么
  • Vue 挂载的实现
  • render函数
  • Virtoal DOM
  • createElement 函数
  • update 函数

当前分析Vue的版本是2.6.10

new Vue()做了什么

首先,Vue是一个构造函数,通过new关键字是用来实例化一个对象。

  • 源码在src/core/instance/index.js

上图可以看到Vue函数内部会调用this._init(options)方法,options就是我们在new Vue({...})中传入的参数。而this._init方法定义在src/core/instance/init.js文件中:

从源码中我们可以看出初始化主要做了以下事情:

  1. 合并配置
  2. 初始化生命周期 initLifecycle(vm)
  3. 初始化事件中心 initEvents(vm)
  4. 初始化渲染 initRender(vm)
  5. 初始化datapropscomputedwatcher等。
  6. 初始化的最后,如果检测到options传入了el属性,则调用vm.$mount()方法挂载vm,挂载的目标就是把模板渲染成最终的DOM。

Vue 挂载的实现

Vue中我们是通过$mount实例方法去挂载的,因为$mount方法是和平台、构建方式相关的。我们重点分析带compiler版本的$mount实现。

  • $mount的实现在src/platforms/web/entry-runtime-with-compiler.js中:

  1. 这段代码首先缓存了原型上的$mount方法。然后对el做了限制,Vue不能挂载在body、html这样的根节点上。
  2. 如果没有定义render方法,则会把el或者template属性,最终都会转化为render方法。(Vue组件的渲染最终都需要render方法,无论是.vue、还是el、或者template属性)
  • 原型上的$mount方法是在src/platforms/web/runtime/index.js中:

$mount方法传入两个参数,第一个参数el,表示挂载的元素,也可以是字符串,也可以是DOM对象,如果是字符串会调用query方法转化为DOM对象,第二个参数是服务端渲染相关的。$mount方法最后会调用mountComponent方法。

  • mountComponent方法定义在src/core/instance/lifecycle.js中:

从上面代码可以看出,mountComponent核心就是先调用vm.render()方法先生成虚拟Node,再实例化一个渲染Watcher,在它的回调函数中会调用updateComponent方法,最终调用vm._update()更新DOM。 函数的最后判断根节点设置了vm._isMounted = true来表示这个实例已经挂载了,同时执行mounted钩子函数。

这里注意vm.$vnode表示Vue实例的父虚拟Node,所以为Null表示当前是根Vue的实例。

render

我们平常开发中写的较多的是template模板,很少直接手写render方法,在之前的mounted方法实现中,会把template编译成render方法。

template写法:

<div id="app">
    {{message}}
</div>

render函数写法:

render: function(createElement){
    return createElement('div', {
        attrs: {
            id: 'app'
        }
    }, this.message)
}

接下来我们看下_render方法的定义:

  • _render方法定义在src/core/instance/render.js中

_render方法用来把实例渲染成一个虚拟Node。 从vnode = render.call(vm._renderProxy, vm.$createElement)可以看出render函数中的createElement就是vm.$createElement方法

vm.$createElement方法在定义initRender方法的时候已经执行了,除了vm.$createElement方法,还有一个个vm._c方法,它是被模板编译成render函数使用。两个方法内部都调用了createElement方法。

Virtual DOM

DOM操作代价大,