Vue中各个⽣命周期阶段的源码探究--beforeCreate和created阶段
本⽂主要探讨⼀下,Vue的beforeCreate和created阶段⼀个⼤致运⾏流程,我们知道,它的⽣命周期包
括,beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed。出于简单起见,这⾥暂时讨论只讨论beforeCreate,created阶段
本次探究所⽤的代码如下,vue版本为v2.5.17-beta.0:
<!--html模板-->
<div id="box">
<ul>
<li v-for="item in data"@click="logChange(item.NAME,item.AGE)">
{{item.NAME}}
</li>
</ul>
</div>
svg实例//js
new Vue({
el:'#box',
data:{
data:[
{NAME:'SDF',AGE:3},
{NAME:'JIKU',AGE:6},
{NAME:'HYF',AGE:3}
]
},
methods:{
logChange:function(name,age){
console.log(name,age);
}
}
});
⼀、beforeCreate阶段
这个阶段主要是完成vue中关于⽣成周期以及事件的⼀些初始化⼯作,在这之前它会执⾏⼀个mergeOptions函数,得到$options选项,并把它设置成Vue实例的⼀个属性。
1.1、⽣成$options选项
//vm为Vue实例
vm.$options =mergeOptions(
structor),
options ||{},
vm
);
这个函数⾸先是检查我们的⼀个组件名称是否合法,它会执⾏如下代码⽚段:
//name是传⼊的组件名称
if(!/^[a-zA-Z][\w-]*$/.test(name)){
warn(
'Invalid component name: "'+ name +'". Component names '+
'can only contain alphanumeric characters and the hyphen, '+
'and must start with a letter.'
)
;
}
if(isBuiltInTag(name)|| config.isReservedTag(name)){
warn(
'Do not use built-in or reserved HTML elements as component '+
'id: '+ name
);
}
从这⾥我们可以看出,⼀个合法的组件名应该是必须是以字母开头,并且中间可以加⼀个或多个横线连接,但vue中内置的⼀些标签名
如'slot,component'以及常规的html不能⽤作组件名,这个标签如下;⼀种是标准的html,还有就是svg标签。
var isHTMLTag =makeMap(
'html,body,base,head,link,meta,style,title,'+
'address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,'+
'div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,'+
'a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,'+
's,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,'+
'embed,object,param,source,canvas,script,noscript,del,ins,'+
'caption,col,colgroup,table,thead,tbody,td,th,tr,'+
'button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,'+
'output,progress,select,textarea,'+
'details,dialog,menu,menuitem,summary,'+
'content,element,shadow,template,blockquote,iframe,tfoot'
);
// this map is intentionally selective, only covering SVG elements that may
// contain child elements.
var isSVG =makeMap(
'svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,'+
'foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,'+
'polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view',
true
);
1.2、对传⼊的选项做合并处理
该过程会执⾏mergeField⽅法。
function mergeField(key){
var strat = strats[key]|| defaultStrat;
options[key]=strat(parent[key], child[key], vm, key);
}
这个⽅法⾥⾯,它是根据我们传⼊的选项对象的键名来确定⼀个“策略函数”,然后将相关的键值传⼊该函数,最后得到$options对象的⼀个属性信息(即⽣成键值对)。其中parent值的信息如下图
以components键为例,它对应的“策略函数”,mergeAssets函数
function mergeAssets(
parentVal,
childVal,
vm,
key
){
var res = ate(parentVal ||null);
if(childVal){
"development"!=='production'&&assertObjectType(key, childVal, vm);
return extend(res, childVal)
}else{
return res
}
}
在这个函数中,它是将⽗选项作为⼀个对象的原型对象(如果有⽗选项传⼊的话),然后将所有⼦选项的属性复制到该对象上(如果有⼦选项传⼊的话)。通过执⾏这些合并操作后,最后在本例中,得到的$option对象如下:
1.3、初始化代理对象
这⼀步是⽣成Vue实例的⼀个渲染代理对象,通过new Proxy(vm, handlers)⽣成。
之后相关数据的动态绑定都是要依靠这个代理对象来实现的。它会执⾏initProxy⽅法
initProxy=function initProxy(vm){
if(hasProxy){
// determine which proxy handler to use
var options = vm.$options;
var handlers = der && der._withStripped
? getHandler
: hasHandler;
vm._renderProxy =new Proxy(vm, handlers);
}else{
vm._renderProxy = vm;
}
};
hasProxy⽤于判断浏览器是否⽀持Proxy代理。如果不⽀持,这个渲染代理就⽤Vue实例本⾝。
1.3、给Vue实例添加与⽣命周期相关的属性
例如:
vm.$parent = parent;
vm.$root = parent ? parent.$root : vm;
vm.$children =[];
vm.$refs ={};
vm._watcher =null;
vm._inactive =null;
vm._directInactive =false;
vm._isMounted =false;
vm._isDestroyed =false;
vm._isBeingDestroyed =false;
最后再初始化与⽗级组件相关的事件以及添加⽣成虚拟节点的⼯具函数。
例如
vm._c=function(a, b, c, d){return createElement(vm, a, b, c, d,false);};
// normalization is always applied for the public version, used in
// user-written render functions.
vm.$createElement=function(a, b, c, d){return createElement(vm, a, b, c, d,true);};
⼆、created阶段
这个阶段主要是初始化与依赖注⼊相关的操作,以及数据的动态绑定。
会依次执⾏
initInjections(vm);// resolve injections before data/props
initState(vm);//涉及给数据绑定,给⽅法绑定vue实例的执⾏上下⽂
initProvide(vm);// resolve provide after data/props
由于本⽂没有涉及到依赖注⼊,只探讨initState⽅法,这个⽅法中涉及的流程如下:
2.1、初始化⽅法
这个阶段是对给⽅法绑定vue实例的执⾏上下⽂,它会遍历我们传⼊的methods选项。
function initMethods(vm, methods){
var props = vm.$options.props;
for(var key in methods){
{
if(methods[key]==null){
warn(
"Method \""+ key +"\" has an undefined value in the component definition. "+
"Did you reference the function correctly?",
vm
);
}
if(props &&hasOwn(props, key)){
warn(
("Method \""+ key +"\" has already been defined as a prop."),
vm
);
}
if((key in vm)&&isReserved(key)){
warn(
"Method \""+ key +"\" conflicts with an existing Vue instance method. "+
"Avoid defining component methods that start with _ or $."
);
}
}
vm[key]= methods[key]==null? noop :bind(methods[key], vm);
}
}
从这⾥可以看出,⽅法选项对象的键与值必须存在;不能与props选项对象中的某个键同名;不能与vue实例⾃带的以$或_开关的属性同名;否则它都会给我们⼀个错误的警告。最后会给每个⽅法绑定与vue实例的执⾏上下⽂即(bind(methods[key], vm);)并且将绑定好上下⽂的⽅法添加到vue实例的⼀个属性上。
2.2、初始化选项数据
function initData(vm){
var data = vm.$options.data;
data = vm._data =typeof data ==='function'
?
getData(data, vm)
: data ||{};
if(!isPlainObject(data)){
data ={};
"development"!=='production'&&warn(
'data functions should return an object:\n'+
'/v2/guide/components.html#data-Must-Be-a-Function',
vm
);
}
// proxy data on instance
var keys = Object.keys(data);
var props = vm.$options.props;
var methods = vm.$hods;
var i = keys.length;
while(i--){
var key = keys[i];
{
if(methods &&hasOwn(methods, key)){
warn(
("Method \""+ key +"\" has already been defined as a data property."),
vm
)
;
}
}
if(props &&hasOwn(props, key)){
"development"!=='production'&&warn(
"The data property \""+ key +"\" is already declared as a prop. "+
"Use prop default value instead.",
vm
);
}else if(!isReserved(key)){
proxy(vm,"_data", key);
}
}
// observe data
observe(data,true/* asRootData */);
}
在这⾥⾸先是var data = vm.$options.data;从$options⾥⾯获取数据选项,当然我们设置的数据选项可能是通过⼀个函数返回的对象,这就是为什么会有typeof data === 'function' ? getData(data, vm): data || {};⽽getData函数⾥是会执⾏我们传⼊的会返回数据选项的函数,具体如下:
function getData(data, vm){
// #7573 disable dep collection when invoking data getters
pushTarget();
try{
return data.call(vm, vm)
}catch(e){
handleError(e, vm,"data()");
return{}
}finally{
popTarget();
}
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论