created之后的生命周期
想看结论的直接往下翻,这文章又臭又长。
我们先看Vue的生命周期示图
在created之后就是选择el所用的DOM,并编译到render函数中(render函数就是用于编译生成Vnode的)。但render真的是在created到beforeMount阶段调用的么
created到beforMount
callHook(vm, 'created');
...
vm.$mount(vm.$options.el);
在调用created后会调用vm.$mount,如果用户没有设定el那么会开始阻塞,直到用户手动调用vm.$mount为止。
Vue.prototype.$mount = function (
el,
hydrating
) {
el = el && inBrowser ? query(el) : undefined; //返回Template或者指定ID标签的DOM
return mountComponent(this, el, hydrating)
};
el最终会返回Template或者HTML的DOM
之后执行mountComponent,this指向Vue的实例、el为DOM元素、hydrating则是undefined
我们在看看
function mountComponent (
vm,
el,
hydrating
) {
vm.$el = el;
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode;
{
/* istanbul ignore if */
if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
vm.$options.el || el) {
warn(
'You are using the runtime-only build of Vue where the template ' +
'compiler is not available. Either pre-compile the templates into ' +
'render functions, or use the compiler-included build.',
vm
);
} else {
warn(
'Failed to mount component: template or render function not defined.',
vm
);
}
}
}
callHook(vm, 'beforeMount');
...
}
好了,简化一下去除检测的
function mountComponent (vm,el,hydrating) {
vm.$el = el;
if (!vm.$options.render) {
vm.$options.render = createEmptyVNode;
}
callHook(vm, 'beforeMount');
...
}
是不是觉得 vm.$options.render = createEmptyVNode 就是执行了render
其实vm.$options.render就等于createEmptyVNode函数,还没执行
之后callHook(vm, 'beforeMount')
beforeMount结束了
beforeMount结束了,就是把获取到的DOM赋值给了el
beforeMount执行了什么
我们在看看beforeMount做了什么
function beforeMount () {
var this$1 = this;
var update = this._update;
this._update = function (vnode, hydrating) {
var restoreActiveInstance = setActiveInstance(this$1);
// force removing pass
this$1.__patch__(
this$1._vnode,
this$1.kept,
false, // hydrating
true // removeOnly (!important, avoids unnecessary moves)
);
this$1._vnode = this$1.kept;
restoreActiveInstance();
update.call(this$1, vnode, hydrating);
};
}
patch就是Vue的diff算法,返回一个新的VNode。
之后update更新组件。
但beforeMount只是修改了vm上的update方法,并没有执行。
所以到beforeMount执行完成,都没有进行render渲染
beforMount到Mounted
callHook(vm, 'beforeMount');
var updateComponent;
/* istanbul ignore if */
if (config.performance && mark) {
updateComponent = function () {
var name = vm._name;
var id = vm._uid;
var startTag = "vue-perf-start:" + id;
var endTag = "vue-perf-end:" + id;
mark(startTag);
var vnode = vm._render();
mark(endTag);
measure(("vue " + name + " render"), startTag, endTag);
mark(startTag);
vm._update(vnode, hydrating);
mark(endTag);
measure(("vue " + name + " patch"), startTag, endTag);
};
} else {
updateComponent = function () {
vm._update(vm._render(), hydrating);
};
}
// we set this to vm._watcher inside the watcher's constructor
// since the watcher's initial patch may call $forceUpdate (e.g. inside child
// component's mounted hook), which relies on vm._watcher being already defined
new Watcher(vm, updateComponent, noop, {
before: function before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate');
}
}
}, true /* isRenderWatcher */);
hydrating = false;
// manually mounted instance, call mounted on self
// mounted is called for render-created child components in its inserted hook
if (vm.$vnode == null) {
vm._isMounted = true;
callHook(vm, 'mounted');
}
return vm
beforeMount到mounted有关键的两步
1,updateComponent=的函数,这里if中判断计时器能不能用,用来计时虚拟DOM的生成时间
updateComponent=function (){vm._update(vm._render(), hydrating);}
这段是关键
2,Watcher,源码就不贴了。我总结一下这里的Watcher怎么执行的
2.1 Watcher的getter属性等于updateComponent,等Watcher触发是调用updateComponent函数
2.2 this.value = this.lazy ? undefined : this.get();
lazy只有Watcher监听计算属性时才是true
2.3 这个Watcher存入了vm._watcher和vm._watchers,一般的Watcher只能存入_watchers
this.get()
源码也不贴了,关键是value = this.getter.call(vm, vm);
在这里执行了vm._update(vm._render(), hydrating)。
实现了render渲染
最后说一点Watcher的before的那个在队列刷新对Watcher排序中使用,只有调用Watcher.update时会调用。git中的地址vue/src/core/observer/scheduler.js 。你可以download用VSCode找会方便,我像个憨批一样翻了git一个小时。
所用到的函数
inBrowser
var inBrowser = typeof window !== 'undefined';
判断是否是浏览器环境
query
function query (el) {
if (typeof el === 'string') {
var selected = document.querySelector(el);
if (!selected) {
warn(
'Cannot find element: ' + el
);
return document.createElement('div')
}
return selected
} else {
return el
}
}
获取Template或HTML的DOM
createEmptyVNode
var createEmptyVNode = function (text) {
if ( text === void 0 ) text = '';
var node = new VNode();
node.text = text;
node.isComment = true;
return node
};
创建一个空的VNode
setActiveInstance
var activeInstance = null;
function setActiveInstance(vm) {
var prevActiveInstance = activeInstance;
activeInstance = vm;
return function () {
activeInstance = prevActiveInstance;
}
}
总结
render其实在beforeMount到Mounted之间执行,虚拟DOM也在这个阶段生成。
Created到beforeMount,就是将HTML或Template的DOM元素挂载到了el上。
我的帖子可真是又臭又长。