mount, 意思为挂载。可以理解为将vue实例(逻辑应用),挂靠在某个dom元素(载体)上的一个过程。
一、创建Vue实例时的渲染过程
上一篇文章我们讲到, 在创建一个vue实例的时候(var vm = new Vue(options))。Vue的构造函数将自动运行 this._init(启动函数)。启动函数的最后一步为initRender(vm),
// Vue.prototype._init
...
initLifecycle(vm);
initEvents(vm);
callHook(vm, 'beforeCreate');
initState(vm);
callHook(vm, 'created');
initRender(vm);
initRender中调用vm.$mount(vm.$options.el),将实例挂载到dom上,至此启动函数完成。
//initRender
...
if (vm.$options.el) {
vm.$mount(vm.$options.el);
}
可以看出,vm.$mount为vue渲染的主要函数
二、Vue的渲染机制
上图,展示的是独立构建时的一个渲染流程图
模板字符串
//模板字符串
<div id = "app">
{{message}}
</div>
render函数
//render函数
function anonymous() {
with(this){return _h('div',{attrs:{"id":"app"}},["\n "+_s(message)+"\n"])}
}
vnode
真实dom节点($el)
独立构建 与 运行时构建
我们先看一下官方文档 独立构建和运行时构建
这两个概念,我在初学的时候是一头雾水。现在对照着渲染的流程图,我们可以知道
独立构建:包含模板编译器
渲染过程: html字符串 → render函数 → vnode → 真实dom节点
运行时构建: 不包含模板编译器
渲染过程: render函数 → vnode → 真实dom节点
运行时构建通过砍掉模板编译器,让整个包少了30%(官方数据)。我在阅读源码的过程中,发现vue源码7000余行,而和模板编译相关的代码,则约有1000行左右。看起来确实是轻便了。这是在鼓励我们多用render函数吗?
三、$mount函数
上面我们说到,运行时构建的包,会比独立构建少一个模板编译器。在$mount函数上也不同
运行时构建的 $mount函数
而独立构建的 $mount函数,会先用一个临时变量mount保存上面的$mount方法
var mount = Vue$2.prototype.$mount; //此处mount即为运行时版的 $mount
然后重写$mount函数,这时,调用$mount就会包括模板编译功能了
var mount = Vue$2.prototype.$mount;
Vue$2.prototype.$mount = function (el, hydrating) {
...省略代码(里面为模板编译器入口)...
return mount.call(this, el, hydrating)
};
我们可以看到,不管独立构建还是运行时构建,都会调用 vm._mount方法我们来看看源码
Vue.prototype._mount = function(el, hydrating) {
...一些防止运行时的包,却用了template的报错代码...
callHook(vm, 'beforeMount');
vm._watcher = new Watcher(vm, function () {
vm._update(vm._render(), hydrating);
}, noop);
hydrating = false;
if (vm.$vnode == null) {
vm._isMounted = true;
callHook(vm, 'mounted');
}
return vm
}
使用过的vue的人,都会很敏锐地发现, 在调用beforeMount生命周期,和mounted生命周期中间的关键代码为
鉴于大牛已经讲过很多次这里的数据监听了,我们只讲其中渲染部分
vm._update(vm._render(), hydrating);
vm._render函数返回一个vnode作为 vm._update的参数。 hydrating是与服务器渲染(SSR)相关的,浏览器端可以不用管。
vm._render (将render函数转化成vnode)
最核心代码为
var render = vm.$options.render
try{
vnode = render.call(vm._renderProxy, vm.$createElement);
}catch{
...
}
此处,使用call方法, 将this指向 vm.renderProxy js功底差的同学要去补补知识了。
vm.renderProxy是个代理,代理vm,主要用来报错,如果render函数上使用了vm上没有的属性或方法,就会报错。
vm.$createElement 这个是创建vnode的方法,作为第一个参数传入。
render函数
这里的h即是, vm.$createElement ,便是在vm._render这个阶段被传入。
vm._update (将vnode生成真实dom节点)
最关键一句话为
vm.$el = vm.__patch__(prevVnode, vnode);
vm.__patch__也是个大家伙,我之后会再去研究。
里面的方法,将新旧vnode使用 diff算法进行比对,找出要替换的地方,这样更新dom的性能会有较大优化。
最后会返回一个dom节点。
这个时候将vm.$el 赋值为这个dom节点,挂载完成!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。