Vue实例挂载的实现
Vue中我们通过\$mount实例方法去挂载vm的,\$mount方法在多个文件中都有定义,如src/platform/web/entry-runtime-with-compiler.js、src/platform/web/runtime/index.js、src/platform/weex/runtime/index.js因为\$mount这个方法的实现是和平台、构建方式都相关的。接下来我们重点分析compiler版本的\$mount实现,因为抛开webpack的vue-loader,我们在纯前端浏览器环境分析Vue的工作原理,有助于我们对原理理解的深入。
compiler版本的\$mount实现很有意思,先来看一下src/platform/web/entry-runtime-with-compiler.js文件中定义:
1 | const mount = Vue.prototype.$mount |
这段代码首先缓存了原型上的\$mount方法,再重新定义该方法。首先,它对el做了限制,Vue不能挂载在body、html这样的根节点上。接下来的是很关键的逻辑–如果没有定义render方法,则会把el或者template字符串转换成render方法。这里我们要牢记,在Vue2.0版本中,所有Vue的组件的渲染最终都需要render方法,无论我们是用单文件.vue方式开发组件,还是写了el或者template属性,最终都会转换成render方法,那么这个过程是 Vue 的一个“在线编译”的过程,它是调用 compileToFunctions 方法实现的,编译过程我们之后会介绍。最后,调用原先原型上的 \$mount 方法挂载。
原先原型上的\$mount方法在src/platform/web/runtime/index.js 中定义,之所以这么设计完全是为了复用,因为它是可以被 runtime only 版本的 Vue 直接使用的。
1 | // public mount method |
\$mount 方法实际上会去调用mountComponent方法,这个方法定义在src/core/instance/lifecycle.js文件中:
1 | export function mountComponent ( |
从上面的代码可以看到,mountComponent核心就是先调用vm._render方法先生成虚拟Node,再实例化一个渲染Watcher,在它的回调函数中会调用updateComponent方法,最终调用vm._update更新DOM。
Watcher在这里起到两个作用,一个是初始化的时候会执行回调函数,另一个是当vm实例中的监测的数据发生变化的时候执行回调函数。
函数最后判断为根节点的时候设置 vm._isMounted 为 true, 表示这个实例已经挂载了,同时执行 mounted 钩子函数。 这里注意 vm.$vnode 表示 Vue 实例的父虚拟 Node,所以它为 Null 则表示当前是根 Vue 的实例。
总结
mountComponent 方法的逻辑也是非常清晰的,它会完成整个渲染工作,接下来就是分析其中的细节,也就是最核心的2个方法:vm._render 和 vm._update。