Vue模板挂载到页面可以分为两大部分init
和mount
1. init
Vue.prototype._init = function() {
// 初始化选项,computed data
// 初始化实例,给实例绑定方法
// 触发beforeCreated created钩子
}
2. mount
上面介绍的_init
方法中,会有一个挂在到dom的方法
if (vm.$options.el) {
vm.$mount(vm.$options.el);
}
``
不是所有的Vue实例在初始化的时候都会去执行`$mount`方法去挂载DOM,比如component
```js
Vue.prototype.$mount = function(el) {
return mountComponent(this, query(el))
};
function mountComponent(vm, el) {
new Watcher(vm, function() {
// 新建watcher,并且设置一个更新函数,当数据发生变化时,会重新执行这个更新函数
vm._update(vm._render());
})
return vm
}
function Watcher(vm, expOrFn) {
this.getter = expOrFn;
this.get();
}
Watcher.prototype.get = function() {
value = this.getter(vm);
}
vm._render
会解析得到一个渲染函数,渲染函数执行完全返回一个模板对应的vnode
Vue.prototype._render = function() {
vnode = render();
return vnode
}
vm._update
这个函数的作用是对比vnode, 挂载更新DOM
- 如果存在旧DOM,那么会对比旧VNode和刚刚传入的新的VNode,不断patch得到最小变化单位,从而更新这部分DOM
- 如果不存在旧 vnode,那么就直接把 vnode 转换为 dom 挂载到页面。其中,生成DOM 和 挂载DOM 用到的方法是 createElm
function createElm(vnode, parentElm, refElm) {
var children = vnode.children;
var tag = vnode.tag;
vnode.elm = document.createElement(tag);
// 不断递归遍历子节点
createChildren(vnode, children);
// 插入DOM 节点
insert(parentElm, vnode.elm, refElm);
}
function createChildren(vnode, children) {
if (Array.isArray(children)) {
for (var i = 0; i < children.length; ++i) {
createElm(children[i], vnode.elm, null);
}
}
}
function insert(parent, elm, ref) {
if (parent) {
// 如果存在兄弟节点,就查到兄弟前面
if (ref) {
// 兄弟节点的父节点和 本节点父节点相同
if (ref.parentNode === parent) {
parent.insertBefore(elm, ref);
}
}
// 如果没有兄弟节点,就直接查到父节点最后
else {
parent.appendChild(elm);
}
}
}