Vue 源码
你想要的 vue 源码分析 · 10
合并配置 new Vue 有两个场景,一种是外部代码主动主动调用 new Vue(options),另一种是组件内部通过 new Vue(options)实例化组件,无论哪种场景,都会用到_init(options),首先会执行 merge options 的操作,组件内部通过 new Vue(options)实例化比外部主动调用 new Vue(options)要快,合并完的结果保存在 vm.$…
合并配置 new Vue 有两个场景,一种是外部代码主动主动调用 new Vue(options),另一种是组件内部通过 new Vue(options)实例化组件,无论哪种场景,都会用到_init(options),首先会执行 merge options 的操作,组件内部通过 new Vue(options)实例化比外部主动调用 new Vue(options)要快,合并完的结果保存在 vm.$…
new Vue 有两个场景,一种是外部代码主动主动调用 new Vue(options),另一种是组件内部通过 new Vue(options)实例化组件,无论哪种场景,都会用到_init(options),首先会执行 merge options 的操作,组件内部通过 new Vue(options)实例化比外部主动调用 new Vue(options)要快,合并完的结果保存在 vm.$options 中
外部调用场景下的合并配置是通过 mergeOption,遵循一定的合并策略,对于不同的 key 的合并策略是不一样
通过 initInternalComponent 进行 merge,在子组件构造函数,先把 Vue.options 和子组件的 option 合并,然后在实例化子组件的时候,再把合并的 options,Sub.options 和传入 options 进行合并
以此为例import Vue from 'vue';
let childComp = {
template: '<div>{{msg}}</div>',
created() {
console.log('child created');
},
mounted() {
console.log('child mounted');
},
data() {
return {
msg: 'Hello Vue'
};
}
};
Vue.mixin({
created() {
console.log('parent created');
}
});
let app = new Vue({
el: '#app',
render: (h) => h(childComp)
});
// 外部调用 new Vue后 options的merge 合并策略后的结构
vm.$options = {
components: {},
created: [
function created() {
console.log('parent created');
}
],
directives: {},
filters: {},
_base: function Vue(options) {
// ...
},
el: '#app',
render: function(h) {
//...
}
};
// 组件调用场景 new Vue后 options的merge 合并策略后的结构
vm.$options = {
parent: Vue /*父Vue实例*/,
propsData: undefined,
_componentTag: undefined,
_parentVnode: VNode /*父VNode实例*/,
_renderChildren: undefined,
__proto__: {
components: {},
directives: {},
filters: {},
_base: function Vue(options) {
//...
},
_Ctor: {},
created: [
function created() {
console.log('parent created');
},
function created() {
console.log('child created');
}
],
mounted: [
function mounted() {
console.log('child mounted');
}
],
data() {
return {
msg: 'Hello Vue'
};
},
template: '<div>{{msg}}</div>'
}
};
export function initMixin(Vue: GlobalAPI) {
Vue.mixin = function(mixin: Object) {
this.options = mergeOptions(this.options, mixin);
return this;
};
}
先通过 Vue.mixin 把 create 钩子混入到 Vue.options 中,然后执行 new Vue(options),然后执行 Vue._init
1._init// src/core/instance/init.js
// 不同场景对于options的合并逻辑是不一样的,传入的options也不一样
//
Vue.prototype._init = function(options?: Object) {
const vm: Component = this;
// a uid
vm._uid = uid++;
let startTag, endTag;
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
startTag = `vue-perf-start:${vm._uid}`;
endTag = `vue-perf-end:${vm._uid}`;
mark(startTag);
}
// a flag to avoid this being observed
vm._isVue = true;
// merge options
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options);
} else {
// 当执行 new Vue的时候,会执行this._init(options)
// resolveConstructorOptions(vm.constructor) 的返回值和 options 做合并
// 在以下例子下,只是简单的返回vm.constructor.options,相当于Vue.options
// Vue.options在initGlobalAPI(Vue) 中定义了,【见2.initGlobalAPI】
// 再见【3.mergeOptions】
vm.$options = mergeOptions(resolveConstructorOptions(vm.constructor), options || {}, vm);
}
// ...
};
2.initGlobalAPI// src/core/global-api/index.js
export function initGlobalAPI(Vue: GlobalAPI) {
// ...
// 首先建一个空对象
Vue.options = Object.create(null);
// 然后遍历ASSET_TYPES
// export const ASSET_TYPES = [
// 'component',
// 'directive',
// 'filter'
// ]
ASSET_TYPES.forEach((type) => {
Vue.options[type + 's'] = Object.create(null);
});
// 遍历后的结果
/*
Vue.options.components = {}
Vue.options.directives = {}
Vue.options.filters = {}
*/
// 为了扩展普通对象,weex方式用
Vue.options._base = Vue;
// 通过下面方法,把一些内置组件扩展到Vue.options.components上
// Vue的内置组件有<keep-alive>、<transition> 和 <transition-group> 组件
extend(Vue.options.components, builtInComponents);
// ...
}
3.mergeOptions// src/core/util/options.js
// mergeOptions的主要功能是,把parent和child两个对象,进行合并,合成一个新对象返回
// 如果是组件场景,那么parent就存在,如果是外部常见,parent不存在
export function mergeOptions(parent: Object, child: Object, vm?: Component): Object {
if (process.env.NODE_ENV !== 'production') {
checkComponents(child);
}
if (typeof child === 'function') {
child = child.options;
}
normalizeProps(child, vm);
normalizeInject(child, vm);
normalizeDirectives(child);
const extendsFrom = child.extends;
// 核心步骤1:递归把extends和mixins合并到parent
if (extendsFrom) {
parent = mergeOptions(parent, extendsFrom, vm);
}
if (child.mixins) {
for (let i = 0, l = child.mixins.length; i < l; i++) {
parent = mergeOptions(parent, child.mixins[i], vm);
}
}
const options = {};
let key;
// function mergeHook(parentVal: ?Array<Function>, childVal: ?Function | ?Array<Function>): ?Array<Function> {
// return childVal ? (parentVal ? parentVal.concat(childVal) : Array.isArray(childVal) ? childVal : [childVal]) : parentVal;
// }
// LIFECYCLE_HOOKS.forEach((hook) => {
// strats[hook] = mergeHook;
// });
// 核心步骤2:然后遍历parent,调用mergeField
for (key in parent) {
mergeField(key);
}
// 核心步骤2:然后再遍历child,如果key不在parent的自身属性上,则调用mergeField
for (key in child) {
if (!hasOwn(parent, key)) {
mergeField(key);
}
}
// mergeField对不同key有着不同的合并策略
// 通过执行mergeField 函数,把合并后的结果保存在options对象中,最终返回
function mergeField(key) {
const strat = strats[key] || defaultStrat;
// 如果child和parent都定义了相同的钩子函数,那么就会把两个钩子函数合并成一个数组
options[key] = strat(parent[key], child[key], vm, key);
}
return options;
// 回到【以此为例】
}
1.Vue.extend// src/core/global-api/extend.js
Vue.extend = function(extendOptions: Object): Function {
// ...
// extendOptions就是组件对象
// Vue.options 最终合并到 Sub.opitons
Sub.options = mergeOptions(Super.options, extendOptions);
Sub.superOptions = Super.options;
Sub.extendOptions = extendOptions;
Sub.sealedOptions = extend({}, Sub.options);
// ...
return Sub;
};
2.组件初始化过程 createComponentInstanceForVnode// src/core/vdom/create-component.js
export function createComponentInstanceForVnode(
vnode: any, // we know it's MountedComponentVNode but flow doesn't
parent: any // activeInstance in lifecycle state
): Component {
const options: InternalComponentOptions = {
_isComponent: true,
_parentVnode: vnode,
parent
};
// vnode.componentOptions.Ctor指向Vue.extend的返回值Sub,
// 所以执行new的过程,接着执行 this._init(options)
// 又因为 options._isComponent 为true
// 就会走到initInternalComponent(vm, options) 见【3.initInternalComponent】
return new vnode.componentOptions.Ctor(options);
}
3.initInternalComponent// src/core/instance/init.js
// 只是做了简单一层对象赋值,并不涉及到递归、合并策略等复杂逻辑
export function initInternalComponent(vm: Component, options: InternalComponentOptions) {
// vm.constructor.options就是构造函数Sub
// 相当于vm.$options = Sub.options
const opts = (vm.$options = Object.create(vm.constructor.options));
// 把实例化子组件传入子组件父Vnode实例parentVnode
const parentVnode = options._parentVnode;
// 子组件的父Vue实例parent保存在vm.$options中
opts.parent = options.parent;
// 保留了 parentVnode 配置中的其它的属性
opts._parentVnode = parentVnode;
//
const vnodeComponentOptions = parentVnode.componentOptions;
opts.propsData = vnodeComponentOptions.propsData;
opts._parentListeners = vnodeComponentOptions.listeners;
opts._renderChildren = vnodeComponentOptions.children;
opts._componentTag = vnodeComponentOptions.tag;
if (options.render) {
opts.render = options.render;
opts.staticRenderFns = options.staticRenderFns;
}
// 回到【以此为例】
}
暂无评论
确认操作
请再次确认编辑图片
待上传图片 (剩余 0 张)
项目详情
确认评论邮箱
邀请制评论