vue
function Vue (options) {
// vue必须通过new关键字创建,否则报错
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
1. vue init方法
- 在原型上添加init方法,用于初始化
- 添加_uid,给vue实例加上唯一ID
- 判断是否是组件,如果是组件,需要初始化组件选项
export function initInternalComponent (vm: Component, options: InternalComponentOptions) { // 深拷贝options const opts = vm.$options = Object.create(vm.constructor.options) // doing this because it's faster than dynamic enumeration. // 赋值父节点 const parentVnode = options._parentVnode opts.parent = options.parent 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 } }
- 如果不是组件,则直接执行数据合并 mergeOptions
vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ) export function resolveConstructorOptions (Ctor: Class
) { // 获取当前构造函数上的options let options = Ctor.options // 查看当前构造函数是否是继承来的 if (Ctor.super) { // 如果是继承来的,就合并父类的options到子类,递归查找,直到最顶层 const superOptions = resolveConstructorOptions(Ctor.super) // 缓存本次的options const cachedSuperOptions = Ctor.superOptions // 如果两次的options不一样 if (superOptions !== cachedSuperOptions) { // super option changed, // need to resolve new options. // 重置superOptions的值为本次循环options的值 Ctor.superOptions = superOptions // check if there are any late-modified/attached options (#4976) // 检查两次options之间的修改值 const modifiedOptions = resolveModifiedOptions(Ctor) // update base extend options // 将modifiedOptions上的值合并到extendOptions上面去 if (modifiedOptions) { extend(Ctor.extendOptions, modifiedOptions) } // 执行mergeOptions方法,合并options,执行相关合并策略 options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions) if (options.name) { options.components[options.name] = Ctor } } } return options }
主要是mergeOptions方法
检查component的名称是否是违禁的,不能使用HTML名称和关键字
-
由于vue的组件必须是function,所以需要判断下子类的类型,获取子类的options
// 判断是否是组件 if (typeof child === 'function') { child = child.options }
-
检查对应的选项的规范,排除不规范的选项
normalizeProps(child, vm) normalizeInject(child, vm) normalizeDirectives(child)
-
将extends继承来的属性和mixin混合的属性都添加到options中去
- 遍历父级的options
- 找到对应的默认执行策略,如果没有,就用默认执行策略
- 遍历子类的options,添加子类的私有属性,默认的策略是如果子类有就用子类的,否则就用父类的
// 遍历parent的值,看看child中是否有,如果有就替换 for (key in parent) { mergeField(key) } // 遍历child,添加child私有属性值 for (key in child) { if (!hasOwn(parent, key)) { mergeField(key) } } // 合并策略 function mergeField (key) { // strats 是自定义策略的集合 // defaultStrat 是默认策略 const strat = strats[key] || defaultStrat options[key] = strat(parent[key], child[key], vm, key) } /** * Default strategy. * 判断子类是否存在,不存在就用父类的,否则就用子类的 */ const defaultStrat = function (parentVal: any, childVal: any): any { return childVal === undefined ? parentVal : childVal }
-
data合并策略
组件data必须为function,原因是为了提高复用性if (!vm) { // 判断子类如果data不是function,就报出警告 if (childVal && typeof childVal !== 'function') { process.env.NODE_ENV !== 'production' && warn( 'The "data" option should be a function ' + 'that returns a per-instance value in component ' + 'definitions.', vm ) return parentVal } return mergeDataOrFn(parentVal, childVal) }
具体合并逻辑
/** * Helper that recursively merges two data objects together. */ function mergeData (to: Object, from: ?Object): Object { if (!from) return to let key, toVal, fromVal const keys = hasSymbol ? Reflect.ownKeys(from) : Object.keys(from) // 遍历父类的data的数据 for (let i = 0; i < keys.length; i++) { key = keys[i] // in case the object is already observed... // 如果已经添加到响应式系统,就直接跳过 if (key === '__ob__') continue // 子类的值 toVal = to[key] // 父类的值 fromVal = from[key] // 如果子类中不存在这个属性,就在子类中添加这个属性并赋值 if (!hasOwn(to, key)) { set(to, key, fromVal) } else if ( // 判断如果子类和父类都有对应的属性值,并且都是对象,就递归调用当前方法合并 toVal !== fromVal && isPlainObject(toVal) && isPlainObject(fromVal) ) { mergeData(toVal, fromVal) } } return to }
-
合并
component
directive
filter
export const ASSET_TYPES = [ 'component', 'directive', 'filter' ] // 合并逻辑 function mergeAssets ( parentVal: ?Object, childVal: ?Object, vm?: Component, key: string ): Object { // 创建一个空对象 const res = Object.create(parentVal || null) if (childVal) { process.env.NODE_ENV !== 'production' && assertObjectType(key, childVal, vm) // 将子类的属性赋值到空对象中 return extend(res, childVal) } else { return res } }
-
合并watch
// 观察者哈希不能覆盖一个 // 另一个,所以我们将它们合并为数组。 strats.watch = function ( parentVal: ?Object, childVal: ?Object, vm?: Component, key: string ): ?Object { // work around Firefox's Object.prototype.watch... // 由于火狐浏览器Gecko内核实现了watch方法,这个地方需要重置watch值 if (parentVal === nativeWatch) parentVal = undefined if (childVal === nativeWatch) childVal = undefined // 如果子类不存在,直接返回深拷贝的父类 if (!childVal) return Object.create(parentVal || null) // 判断子类是否是对象,不是则报出警告 if (process.env.NODE_ENV !== 'production') { assertObjectType(key, childVal, vm) } // 父类不存在,则直接返回子类 if (!parentVal) return childVal const ret = {} // 将父类的属性浅拷贝 extend(ret, parentVal) // // 遍历子类的watch,如果子类存在相同的监听对象, // 就将监听回调合并到一个数组中,便于后面顺序执行, // parent.concat(child) // for (const key in childVal) { let parent = ret[key] const child = childVal[key] if (parent && !Array.isArray(parent)) { parent = [parent] } ret[key] = parent ? parent.concat(child) : Array.isArray(child) ? child : [child] } // 返回合并后的数据 return ret }
-
其他合并策略
props
methods
inject
computed
/** * Other object hashes. */ strats.props = strats.methods = strats.inject = strats.computed = function ( parentVal: ?Object, childVal: ?Object, vm?: Component, key: string ): ?Object { // 判断值是否是对象 if (childVal && process.env.NODE_ENV !== 'production') { assertObjectType(key, childVal, vm) } // 父类没有值,直接返回子类 if (!parentVal) return childVal // 将父类的值赋值到空对象中 const ret = Object.create(null) extend(ret, parentVal) // 合并子类的值到ret中,子类覆盖父类 if (childVal) extend(ret, childVal) return ret }
-
合并生命周期
export const LIFECYCLE_HOOKS = [ 'beforeCreate', 'created', 'beforeMount', 'mounted', 'beforeUpdate', 'updated', 'beforeDestroy', 'destroyed', 'activated', 'deactivated', 'errorCaptured', 'serverPrefetch' ] // 遍历hook钩子,循环添加hook合并策略 LIFECYCLE_HOOKS.forEach(hook => { strats[hook] = mergeHook }) /** * Hooks and props are merged as arrays. * 将对应的hook回调合并成一个数组 */ function mergeHook ( parentVal: ?Array
, childVal: ?Function | ?Array ): ?Array { const res = childVal ? parentVal ? parentVal.concat(childVal) : Array.isArray(childVal) ? childVal : [childVal] : parentVal return res ? dedupeHooks(res) : res } function dedupeHooks (hooks) { const res = [] for (let i = 0; i < hooks.length; i++) { if (res.indexOf(hooks[i]) === -1) { res.push(hooks[i]) } } return res }
初始化生命周期
initLifecycle(vm)
export function initLifecycle (vm: Component) {
const options = vm.$options
// locate first non-abstract parent
// 查找第一个非抽象父级
let parent = options.parent
if (parent && !options.abstract) {
// 如果有abstract属性,一直往上层寻找,直到不是抽象组件
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent
}
parent.$children.push(vm)
}
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
}