/* @flow */ import Vue from '../instance/index' import config from '../config' import { warn } from './debug' import { set } from '../observer/index' import { extend, isPlainObject, hasOwn, camelize, capitalize, isBuiltInTag } from 'shared/util' /** * Option overwriting strategies are functions that handle * how to merge a parent option value and a child option * value into the final value.
覆盖策略是一个函数, 定义如何覆盖父级的对象 */ const strats = config.optionMergeStrategies /** * Options with restrictions */ if (process.env.NODE_ENV !== 'production') {
strats.el = strats.propsData = function (parent, child, vm, key) { if (!vm) {
// key 这个配置只能在 new 一个实例的时候使用, 必须传入你创建的vm new Vue warn( `option "${key}" can only be used during instance ` + 'creation with the `new` keyword.' ) }
//优先使用child的值 return defaultStrat(parent, child)
} } /** * 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 = Object.keys(from)
for (let i = 0; i < keys.length; i++) { key = keys[i] toVal = to[key] fromVal = from[key]
if (!hasOwn(to, key)) {
//这个方法是给to的属性key赋值,
// 如果to有__ob__属性, 还会把新合并的这个值设置成监控模式 , 并且即刻通知更新
// 会调用这两个方法 defineReactive(ob.value, key, val) ob.dep.notify()
set(to, key, fromVal)
} else if (isPlainObject(toVal) && isPlainObject(fromVal)) {
//如果val都是对象,继续合并 mergeData(toVal, fromVal) }
} return to } /** * Data */ strats.data = function ( parentVal: any, childVal: any, vm?: Component ): ?Function {
if (!vm) { // in a Vue.extend merge, both should be functions
//节省时间 if (!childVal) { return parentVal }
//childVal必须是函数 if (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 }
//节省时间 if (!parentVal) { return childVal }
// when parentVal & childVal are both present, // we need to return a function that returns the // merged result of both functions... no need to // check if parentVal is a function here because // it has to be a function to pass previous merges.
// 当父子都到位, 我们需要返回一个函数, 这个函数返回了两个合并的几个
// 不需要确认 parentval是个函数 因为它必须是传递给前一个合并的函数?
return function mergedDataFn () { return mergeData( childVal.call(this), parentVal.call(this) ) } } else if (parentVal || childVal) {
return function mergedInstanceDataFn () { // instance merge 合并 const instanceData = typeof childVal === 'function' ? childVal.call(vm) : childVal const defaultData = typeof parentVal === 'function' ? parentVal.call(vm) : undefined if (instanceData) { return mergeData(instanceData, defaultData) } else { return defaultData }
}
} } /** * Hooks and props are merged as arrays. */ function mergeHook ( parentVal: ?Array<Function>, childVal: ?Function | ?Array<Function> ): ?Array<Function> {
//如果有child, 看看有没有parent, 如果有合并parent和 child, 如果没有, 返回child数组 return childVal ? parentVal ? parentVal.concat(childVal) : Array.isArray(childVal) ? childVal : [childVal] : parentVal } config._lifecycleHooks.forEach(hook => { strats[hook] = mergeHook }) /** * Assets * * When a vm is present (instance creation), we need to do * a three-way merge between constructor options, instance * options and parent options.
合并构造函数Object的配置 和 实例的配置 和 父级的配置 */ function mergeAssets (parentVal: ?Object, childVal: ?Object): Object { const res = Object.create(parentVal || null) return childVal ? extend(res, childVal) : res } config._assetTypes.forEach(function (type) { strats[type + 's'] = mergeAssets }) /** * Watchers. * * Watchers hashes should not overwrite one * another, so we merge them as arrays.
避免覆盖另一个, 这里把他们合并成数组
*/ strats.watch = function (parentVal: ?Object, childVal: ?Object): ?Object { /* istanbul ignore if */
//节省时间 if (!childVal) return Object.create(parentVal || null) if (!parentVal) return childVal
const ret = {} //复制父级 extend(ret, parentVal)
//最终合并成数组形式 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) : [child] } return ret } /** * Other object hashes.
简单的合并对象 */ strats.props = strats.methods = strats.computed = function (parentVal: ?Object, childVal: ?Object): ?Object { if (!childVal) return Object.create(parentVal || null) if (!parentVal) return childVal const ret = Object.create(null) extend(ret, parentVal) extend(ret, childVal) return ret } /** * Default strategy.
选择其中一个作为默认 */ const defaultStrat = function (parentVal: any, childVal: any): any { return childVal === undefined ? parentVal : childVal } /** * Validate component names
检测不能使用vue保留字 */ function checkComponents (options: Object) { for (const key in options.components) { const lower = key.toLowerCase() if (isBuiltInTag(lower) || config.isReservedTag(lower)) { warn( 'Do not use built-in or reserved HTML elements as component ' + 'id: ' + key ) } } } /** * Ensure all props option syntax are normalized into the * Object-based format.
确保所有的属性选项标签语法 都规范化, 都基于对象格式 , 如果key的val不存在 会设置一个默认的val => { type : null }
*/ function normalizeProps (options: Object) {
const props = options.props if (!props) return const res = {} let i, val, name
//[key1,key2] if (Array.isArray(props)) {
i = props.length
while (i--) { val = props[i]
if (typeof val === 'string') { name = camelize(val) res[name] = { type: null }
} else if (process.env.NODE_ENV !== 'production') {
warn('props must be strings when using array syntax.')
} } } else if (isPlainObject(props)) {
//{key1:val1, key2:val2} for (const key in props) { val = props[key] name = camelize(key)
res[name] = isPlainObject(val) ? val : { type: val } }
} options.props = res
} /** * Normalize raw function directives into object format.
指令格式化
{ direct1: fn1, direct2: fn2 } => { direct1: { bind: fn1, update, fn1 } ,...} */ function normalizeDirectives (options: Object) { const dirs = options.directives if (dirs) { for (const key in dirs) { const def = dirs[key] if (typeof def === 'function') { dirs[key] = { bind: def, update: def } } } } } /** * Merge two option objects into a new one. 合并两个配置对象 * Core utility used in both instantiation and inheritance.
实例化和继承中使用的核心实用工具。 */ export function mergeOptions ( parent: Object, child: Object, vm?: Component ): Object { if (process.env.NODE_ENV !== 'production') { checkComponents(child) } normalizeProps(child) normalizeDirectives(child) const extendsFrom = child.extends
if (extendsFrom) { parent = typeof extendsFrom === 'function' ? mergeOptions(parent, extendsFrom.options, vm) : mergeOptions(parent, extendsFrom, vm) }
if (child.mixins) { for (let i = 0, l = child.mixins.length; i < l; i++) { let mixin = child.mixins[i] if (mixin.prototype instanceof Vue) { mixin = mixin.options } parent = mergeOptions(parent, mixin, vm) } }
const options = {} let key
for (key in parent) { mergeField(key) }
for (key in child) { if (!hasOwn(parent, key)) { mergeField(key) } }
function mergeField (key) { const strat = strats[key] || defaultStrat options[key] = strat(parent[key], child[key], vm, key) }
return options
} /** * Resolve an asset. * This function is used because child instances need access * to assets defined in its ancestor chain. */ export function resolveAsset ( options: Object, type: string, id: string, warnMissing?: boolean ): any { /* istanbul ignore if */ if (typeof id !== 'string') { return } const assets = options[type] // check local registration variations first if (hasOwn(assets, id)) return assets[id] const camelizedId = camelize(id) if (hasOwn(assets, camelizedId)) return assets[camelizedId] const PascalCaseId = capitalize(camelizedId) if (hasOwn(assets, PascalCaseId)) return assets[PascalCaseId] // fallback to prototype chain const res = assets[id] || assets[camelizedId] || assets[PascalCaseId] if (process.env.NODE_ENV !== 'production' && warnMissing && !res) { warn( 'Failed to resolve ' + type.slice(0, -1) + ': ' + id, options ) } return res }
各种合并配置项, 不知道为什么要这么多种方式