前言
本文是vue2.x源碼分析的第二篇车荔,主要講解Vue初始化過程锻弓!
1掩宜、貫穿全文的例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue</title>
<script src="./vue.js" type="text/javascript" charset="utf-8" ></script>
</head>
<body>
<div id="app">
{{message}}
<div v-text='abc' v-on:click='methods_a'></div>
</div>
<script>
var app=new Vue({
el:'#app',
name:'app',
mixins:{
beforeCreate(){
console.log('beforeCreate-1');
}
}
props:['header-value'],
data:{
message:'hello'
},
beforeCreate(){
console.log('beforeCreate-2');
},
computed:{
abc(){
return this.message+'s'
}
},
components:{
ABF:'abf'
},
directives:{
v-focus:function some(){}
},
extends:{
ext_a:'a'
},
methods:{
methods_a:function(){
alert('methods_a')
}
},
watch:{
'message':function(){
console.log('message changed');
},
methods_a:function(){}
}
})
// debugger;
// setTimeout(()=>app.message='world',0)
</script>
</body>
</html>
這里先放一個(gè)簡單的例子蔫骂,隨著分析的深入,會(huì)一步步將例子變復(fù)雜牺汤。分析的手段就是通過設(shè)置斷點(diǎn)一步步看執(zhí)行過程辽旋。
2、new Vue({})執(zhí)行過程分析
- 首先找到Vue構(gòu)造函數(shù)(啟用IDE的查找功能能快速鎖定)
function Vue$3 (options) {
if ("development" !== 'production' &&
!(this instanceof Vue$3)) {
warn('Vue is a constructor and should be called with the `new` keyword');
}
<!-- debugger; -->
this._init(options); //程序主入口
}
- 打開上面代碼的debugger注釋來分析_init函數(shù)檐迟,該函數(shù)是通過initMixin(Vue)定義在Vue.prototype上的
Vue.prototype._init = function (options) {
var vm = this;
// 每個(gè)vm實(shí)例都會(huì)有一個(gè)唯一的_uid屬性
vm._uid = uid++;
// startTag, endTag 暫時(shí)不清楚干嘛的补胚,不影響主流程,記為Unknown-2.1
var startTag, endTag;
/* istanbul ignore if */
if ("development" !== 'production' && config.performance && mark) {
startTag = "vue-perf-init:" + (vm._uid);
endTag = "vue-perf-end:" + (vm._uid);
mark(startTag);
}
// 設(shè)置_isVue標(biāo)簽避免被觀測(cè),記為Unknown-2.2
vm._isVue = true;
// 選項(xiàng)合并
if (options && options._isComponent) {
initInternalComponent(vm, options);
} else {
//主要執(zhí)行過程-1(合并選項(xiàng))
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor),
options || {},
vm
);
}
/* 設(shè)置代理 */
{
initProxy(vm);
}
// 將vm實(shí)例同時(shí)掛在_self屬性上追迟,記為Unknown-2.3
vm._self = vm;
// 主要執(zhí)行過程-2(實(shí)例的初始化過程)
initLifecycle(vm);
initEvents(vm);
initRender(vm);
callHook(vm, 'beforeCreate');
initInjections(vm); // resolve injections before data/props
initState(vm);
initProvide(vm); // resolve provide after data/props
callHook(vm, 'created');
/* istanbul ignore if */
if ("development" !== 'production' && config.performance && mark) {
vm._name = formatComponentName(vm, false);
mark(endTag);
measure(((vm._name) + " init"), startTag, endTag);
}
if (vm.$options.el) {
// 主要執(zhí)行過程-3(依賴收集溶其、組件掛載等過程)
vm.$mount(vm.$options.el);
}
};
- 可以看到_init函數(shù)主要做了如下三件事,上述代碼有標(biāo)注
- 主要執(zhí)行過程-1(合并選項(xiàng)過程)
- 主要執(zhí)行過程-2(實(shí)例的初始化過程)
- 主要執(zhí)行過程-3(依賴收集敦间、組件掛載等過程)
- 這三件事后面會(huì)單獨(dú)分析瓶逃,除了這三件事,還有一個(gè)代理操作每瞒,來看看initProxy(vm)
initProxy = function initProxy (vm) {
//如果有本地Proxy類可用
if (hasProxy) {
// 判斷用哪個(gè)代理處理對(duì)象
var options = vm.$options;
var handlers = options.render && options.render._withStripped
? getHandler
: hasHandler;
vm._renderProxy = new Proxy(vm, handlers);
} else {
vm._renderProxy = vm;
}
};
這里大家可能對(duì)Proxy類比較陌生金闽,可以先看看這里了解基本用法。其實(shí)就是給vm實(shí)例做一個(gè)hasHandler代理剿骨,并將代理對(duì)象掛在_renderProxy上代芜,使得在訪問vm._renderProxy對(duì)象上的屬性時(shí)先調(diào)用has方法,返回值為真才去取該屬性浓利。這在后面的_render函數(shù)會(huì)用到
3挤庇、小結(jié)
- 下一步分析內(nèi)容
? 主要執(zhí)行過程-1(合并選項(xiàng)過程)
? 主要執(zhí)行過程-2(實(shí)例的初始化過程)
? 主要執(zhí)行過程-3(依賴收集、組件掛載等過程) - 遺留問題解決情況
? Unknown2.1
? Unknown2.2
? Unknown2.3