diff options
| author | ruki <waruqi@gmail.com> | 2018-11-08 00:38:48 +0800 |
|---|---|---|
| committer | ruki <waruqi@gmail.com> | 2018-11-07 21:53:09 +0800 |
| commit | 26105034da4fcce7ac883c899d781f016559310d (patch) | |
| tree | c459a5dc4e3aa0972d9919033ece511ce76dd129 /node_modules/vue/src/core/util/options.js | |
| parent | 2c77f00f1a7ecb6c8192f9c16d3b2001b254a107 (diff) | |
| download | xmake-docs-26105034da4fcce7ac883c899d781f016559310d.tar.gz xmake-docs-26105034da4fcce7ac883c899d781f016559310d.zip | |
switch to vuepress
Diffstat (limited to 'node_modules/vue/src/core/util/options.js')
| -rw-r--r-- | node_modules/vue/src/core/util/options.js | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/node_modules/vue/src/core/util/options.js b/node_modules/vue/src/core/util/options.js new file mode 100644 index 00000000..5f0d10d3 --- /dev/null +++ b/node_modules/vue/src/core/util/options.js @@ -0,0 +1,438 @@ +/* @flow */ + +import config from '../config' +import { warn } from './debug' +import { nativeWatch } from './env' +import { set } from '../observer/index' + +import { + ASSET_TYPES, + LIFECYCLE_HOOKS +} from 'shared/constants' + +import { + extend, + hasOwn, + camelize, + toRawType, + capitalize, + isBuiltInTag, + isPlainObject +} 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) { + warn( + `option "${key}" can only be used during instance ` + + 'creation with the `new` keyword.' + ) + } + 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)) { + set(to, key, fromVal) + } else if (isPlainObject(toVal) && isPlainObject(fromVal)) { + mergeData(toVal, fromVal) + } + } + return to +} + +/** + * Data + */ +export function mergeDataOrFn ( + parentVal: any, + childVal: any, + vm?: Component +): ?Function { + if (!vm) { + // in a Vue.extend merge, both should be functions + if (!childVal) { + 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. + return function mergedDataFn () { + return mergeData( + typeof childVal === 'function' ? childVal.call(this, this) : childVal, + typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal + ) + } + } else { + return function mergedInstanceDataFn () { + // instance merge + const instanceData = typeof childVal === 'function' + ? childVal.call(vm, vm) + : childVal + const defaultData = typeof parentVal === 'function' + ? parentVal.call(vm, vm) + : parentVal + if (instanceData) { + return mergeData(instanceData, defaultData) + } else { + return defaultData + } + } + } +} + +strats.data = function ( + parentVal: any, + childVal: any, + vm?: Component +): ?Function { + if (!vm) { + 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) + } + + return mergeDataOrFn(parentVal, childVal, vm) +} + +/** + * Hooks and props are merged as arrays. + */ +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 +}) + +/** + * Assets + * + * When a vm is present (instance creation), we need to do + * a three-way merge between constructor options, instance + * options and parent options. + */ +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 + } +} + +ASSET_TYPES.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, + vm?: Component, + key: string +): ?Object { + // work around Firefox's Object.prototype.watch... + if (parentVal === nativeWatch) parentVal = undefined + if (childVal === nativeWatch) childVal = undefined + /* istanbul ignore if */ + 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) + 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 +} + +/** + * 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) + if (childVal) extend(ret, childVal) + return ret +} +strats.provide = mergeDataOrFn + +/** + * Default strategy. + */ +const defaultStrat = function (parentVal: any, childVal: any): any { + return childVal === undefined + ? parentVal + : childVal +} + +/** + * Validate component names + */ +function checkComponents (options: Object) { + for (const key in options.components) { + validateComponentName(key) + } +} + +export function validateComponentName (name: string) { + 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 + ) + } +} + +/** + * Ensure all props option syntax are normalized into the + * Object-based format. + */ +function normalizeProps (options: Object, vm: ?Component) { + const props = options.props + if (!props) return + const res = {} + let i, val, name + 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)) { + for (const key in props) { + val = props[key] + name = camelize(key) + res[name] = isPlainObject(val) + ? val + : { type: val } + } + } else if (process.env.NODE_ENV !== 'production') { + warn( + `Invalid value for option "props": expected an Array or an Object, ` + + `but got ${toRawType(props)}.`, + vm + ) + } + options.props = res +} + +/** + * Normalize all injections into Object-based format + */ +function normalizeInject (options: Object, vm: ?Component) { + const inject = options.inject + if (!inject) return + const normalized = options.inject = {} + if (Array.isArray(inject)) { + for (let i = 0; i < inject.length; i++) { + normalized[inject[i]] = { from: inject[i] } + } + } else if (isPlainObject(inject)) { + for (const key in inject) { + const val = inject[key] + normalized[key] = isPlainObject(val) + ? extend({ from: key }, val) + : { from: val } + } + } else if (process.env.NODE_ENV !== 'production') { + warn( + `Invalid value for option "inject": expected an Array or an Object, ` + + `but got ${toRawType(inject)}.`, + vm + ) + } +} + +/** + * Normalize raw function directives into object format. + */ +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 } + } + } + } +} + +function assertObjectType (name: string, value: any, vm: ?Component) { + if (!isPlainObject(value)) { + warn( + `Invalid value for option "${name}": expected an Object, ` + + `but got ${toRawType(value)}.`, + vm + ) + } +} + +/** + * 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) + } + + if (typeof child === 'function') { + child = child.options + } + + normalizeProps(child, vm) + normalizeInject(child, vm) + normalizeDirectives(child) + const extendsFrom = child.extends + 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 + 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 +} |
