前言
本文是vue2.x源码分析的第三篇,主要讲解选项合并过程mergeOptions!
先看调用形式
vm.$options = mergeOptions(resolveConstructorOptions(vm.constructor),options || {},vm);
1、执行resolveConstructorOptions(vm.constructor)
function resolveConstructorOptions (Ctor) {var options = Ctor.options; //Ctor即Vue$3,Vue$3.options是在第一节中通过extend函数赋值的if (Ctor.super) { //这里由于没有super属性,故直接返回了optionsvar superOptions = resolveConstructorOptions(Ctor.super);var cachedSuperOptions = Ctor.superOptions;if (superOptions !== cachedSuperOptions) {// super option changed,// need to resolve new options.Ctor.superOptions = superOptions;// check if there are any late-modified/attached options (#4976)var modifiedOptions = resolveModifiedOptions(Ctor);// update base extend optionsif (modifiedOptions) {extend(Ctor.extendOptions, modifiedOptions);}options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions);if (options.name) {options.components[options.name] = Ctor;}}}return options}
返回的options长这个样子:
options = {components: {KeepAlive,Transition,TransitionGroup},directives: {model,show},filters: {},_base: Vue}
2、执行mergeOptions (parent,child,vm)
parent即上述返回的options,child即new Vue(options)传入的options
function mergeOptions (parent,child,vm) {{//检查传入的components选项的名字是否符合规定(非内置标签)checkComponents(child);}//将传入的child.props转换为驼峰式结构的对象表达形式.props能使用数组和对象语法,但在内部都会重新遍历封装到一个新对象中。normalizeProps(child);//directives使用对象语法,对象中属性的值只能为函数,该操作会将函数绑定到指令的bind和update函数上normalizeDirectives(child);//若存在extends,则将其内容合并到父对象parent中保存,最后再和自身child合并,extends最好是对象语法var extendsFrom = child.extends; //暂不清楚作用,记为Unknown3.1if (extendsFrom) {parent = typeof extendsFrom === 'function'? mergeOptions(parent, extendsFrom.options, vm): mergeOptions(parent, extendsFrom, vm);}//若存在mixins,则将其内容合并到父对象parent中保存,最后再和自身child合并,mixins只能是数组语法,数组中元素可以是对象if (child.mixins) {for (var i = 0, l = child.mixins.length; i < l; i++) {var mixin = child.mixins[i];if (mixin.prototype instanceof Vue$3) {mixin = mixin.options;}parent = mergeOptions(parent, mixin, vm);}}//初始化一个对象,用于存储parent和child合并后的内容,并作为mergeOptions函数的结果返回var options = {};var key;for (key in parent) {mergeField(key);}for (key in child) {if (!hasOwn(parent, key)) {mergeField(key);}}//使用策略对象对parent和child进行合并function mergeField (key) {var strat = strats[key] || defaultStrat;options[key] = strat(parent[key], child[key], vm, key);}return options}
2.1、分析checkComponents(child)
function checkComponents (options) {for (var key in options.components) {//对象遍历形式,故components选项只能用对象,不用数组var lower = key.toLowerCase();//组件名字不能是内置标签(slot,component)和保留标签(html标签和svg标签),用了闭包实现if (isBuiltInTag(lower) || config.isReservedTag(lower)) {warn('Do not use built-in or reserved HTML elements as component ' +'id: ' + key);}}}
2.2、分析normalizeProps(child);
function normalizeProps (options) {var props = options.props;if (!props) { return }var res = {};var i, val, name;if (Array.isArray(props)) { //props是数组i = props.length;while (i--) {val = props[i];if (typeof val === 'string') {name = camelize(val);//若val是*-i*的形式,则将其转为*I*,如v-header会被转为vHeader并被缓存res[name] = { type: null }; //每个val的值设为含有type属性的对象} else {warn('props must be strings when using array syntax.');}}} else if (isPlainObject(props)) {//props是对象for (var key in props) {val = props[key];name = camelize(key);res[name] = isPlainObject(val)//若用对象语法,则每个key的value对象最好包含type属性? val: { type: val };}}options.props = res;}// 调用前:options.props=['header-value'];// 调用后:options.props={// 'headerValue':{type:null}// }
2.3、分析normalizeDirectives(child)
function normalizeDirectives (options) {var dirs = options.directives;if (dirs) {for (var key in dirs) { //数组也可以用for in遍历,故directives选项能用对象和数组语法,当只关心数组的value而不关心key时可以用for in遍历var def = dirs[key];if (typeof def === 'function') {dirs[key] = { bind: def, update: def };}}}}// 调用前:options.directives={// v-focus:function some(){}// };// 调用后:options.directives={// 'v-focus':{// bind:some,// update:some// }// }
2.4、分析extends和mixins
二者作用都是进一步丰富parent选项,其本身是调用mergeOptions,这里不做分析,注意mixins只能用数组语法
2.5、分析合并策略对象strats
- 10个生命周期函数—mixins中同名属性用数组保存,mixins中的优先级高,先调用,
合并后类似这样:beforeCreate:[mixins中的beforeCreate,child中的beforeCreate]
- 3个assets(directives、components、filters)—继承策略,以parent中同名属性的值为原型对象,以child中同名属性的值做实例属性,指令合并后类似这样:
directives:{'v-focus':obj1,__proto__:{model:obj2,show:obj3}}
el与propsData、也是用defaultStrat函数,只是会先判断vm实例是否存在
data 返回一个函数mergedInstanceDataFn
watch 同生命周期函数的合并策略
props与methods与computed 覆盖策略,mixins中的同名属性会被child覆盖
props合并后类似这样:props:[child中的属性]
- 其余选项的合并策略采用defaultStrat函数
3、小结
- 下一步分析内容
☐ 主要执行过程-2(实例的初始化过程)
☐ 主要执行过程-3(依赖收集、组件挂载等过程) - 遗留问题解决情况
☐ Unknown3.1
