| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332 |
- /* @flow */
- import Vue from '../instance/index'
- import config from '../config'
- import { warn } from './debug'
- import { set } from '../observer/index'
- import {
- extend,
- isObject,
- 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) {
- 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 {
- let key, toVal, fromVal
- for (key in from) {
- toVal = to[key]
- fromVal = from[key]
- if (!hasOwn(to, key)) {
- set(to, key, fromVal)
- } else if (isObject(toVal) && isObject(fromVal)) {
- 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
- }
- 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.
- 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 param attributes 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
- }
- 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.
- */
- 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 parentVal
- 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 parentVal
- 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
- */
- 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.
- */
- function normalizeProps (options: Object) {
- 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 }
- }
- }
- options.props = res
- }
- /**
- * 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 }
- }
- }
- }
- }
- /**
- * 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]
- const res = assets[id] ||
- // camelCase ID
- assets[camelize(id)] ||
- // Pascal Case ID
- assets[capitalize(camelize(id))]
- if (process.env.NODE_ENV !== 'production' && warnMissing && !res) {
- warn(
- 'Failed to resolve ' + type.slice(0, -1) + ': ' + id,
- options
- )
- }
- return res
- }
|