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 | |
| 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')
| -rw-r--r-- | node_modules/vue/src/core/util/debug.js | 100 | ||||
| -rw-r--r-- | node_modules/vue/src/core/util/env.js | 95 | ||||
| -rw-r--r-- | node_modules/vue/src/core/util/error.js | 48 | ||||
| -rw-r--r-- | node_modules/vue/src/core/util/index.js | 11 | ||||
| -rw-r--r-- | node_modules/vue/src/core/util/lang.js | 39 | ||||
| -rw-r--r-- | node_modules/vue/src/core/util/next-tick.js | 117 | ||||
| -rw-r--r-- | node_modules/vue/src/core/util/options.js | 438 | ||||
| -rw-r--r-- | node_modules/vue/src/core/util/perf.js | 24 | ||||
| -rw-r--r-- | node_modules/vue/src/core/util/props.js | 202 |
9 files changed, 1074 insertions, 0 deletions
diff --git a/node_modules/vue/src/core/util/debug.js b/node_modules/vue/src/core/util/debug.js new file mode 100644 index 00000000..6b7315f4 --- /dev/null +++ b/node_modules/vue/src/core/util/debug.js @@ -0,0 +1,100 @@ +/* @flow */ + +import config from '../config' +import { noop } from 'shared/util' + +export let warn = noop +export let tip = noop +export let generateComponentTrace = (noop: any) // work around flow check +export let formatComponentName = (noop: any) + +if (process.env.NODE_ENV !== 'production') { + const hasConsole = typeof console !== 'undefined' + const classifyRE = /(?:^|[-_])(\w)/g + const classify = str => str + .replace(classifyRE, c => c.toUpperCase()) + .replace(/[-_]/g, '') + + warn = (msg, vm) => { + const trace = vm ? generateComponentTrace(vm) : '' + + if (config.warnHandler) { + config.warnHandler.call(null, msg, vm, trace) + } else if (hasConsole && (!config.silent)) { + console.error(`[Vue warn]: ${msg}${trace}`) + } + } + + tip = (msg, vm) => { + if (hasConsole && (!config.silent)) { + console.warn(`[Vue tip]: ${msg}` + ( + vm ? generateComponentTrace(vm) : '' + )) + } + } + + formatComponentName = (vm, includeFile) => { + if (vm.$root === vm) { + return '<Root>' + } + const options = typeof vm === 'function' && vm.cid != null + ? vm.options + : vm._isVue + ? vm.$options || vm.constructor.options + : vm || {} + let name = options.name || options._componentTag + const file = options.__file + if (!name && file) { + const match = file.match(/([^/\\]+)\.vue$/) + name = match && match[1] + } + + return ( + (name ? `<${classify(name)}>` : `<Anonymous>`) + + (file && includeFile !== false ? ` at ${file}` : '') + ) + } + + const repeat = (str, n) => { + let res = '' + while (n) { + if (n % 2 === 1) res += str + if (n > 1) str += str + n >>= 1 + } + return res + } + + generateComponentTrace = vm => { + if (vm._isVue && vm.$parent) { + const tree = [] + let currentRecursiveSequence = 0 + while (vm) { + if (tree.length > 0) { + const last = tree[tree.length - 1] + if (last.constructor === vm.constructor) { + currentRecursiveSequence++ + vm = vm.$parent + continue + } else if (currentRecursiveSequence > 0) { + tree[tree.length - 1] = [last, currentRecursiveSequence] + currentRecursiveSequence = 0 + } + } + tree.push(vm) + vm = vm.$parent + } + return '\n\nfound in\n\n' + tree + .map((vm, i) => `${ + i === 0 ? '---> ' : repeat(' ', 5 + i * 2) + }${ + Array.isArray(vm) + ? `${formatComponentName(vm[0])}... (${vm[1]} recursive calls)` + : formatComponentName(vm) + }`) + .join('\n') + } else { + return `\n\n(found in ${formatComponentName(vm)})` + } + } +} diff --git a/node_modules/vue/src/core/util/env.js b/node_modules/vue/src/core/util/env.js new file mode 100644 index 00000000..07cadd9c --- /dev/null +++ b/node_modules/vue/src/core/util/env.js @@ -0,0 +1,95 @@ +/* @flow */ + +// can we use __proto__? +export const hasProto = '__proto__' in {} + +// Browser environment sniffing +export const inBrowser = typeof window !== 'undefined' +export const inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform +export const weexPlatform = inWeex && WXEnvironment.platform.toLowerCase() +export const UA = inBrowser && window.navigator.userAgent.toLowerCase() +export const isIE = UA && /msie|trident/.test(UA) +export const isIE9 = UA && UA.indexOf('msie 9.0') > 0 +export const isEdge = UA && UA.indexOf('edge/') > 0 +export const isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android') +export const isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios') +export const isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge + +// Firefox has a "watch" function on Object.prototype... +export const nativeWatch = ({}).watch + +export let supportsPassive = false +if (inBrowser) { + try { + const opts = {} + Object.defineProperty(opts, 'passive', ({ + get () { + /* istanbul ignore next */ + supportsPassive = true + } + }: Object)) // https://github.com/facebook/flow/issues/285 + window.addEventListener('test-passive', null, opts) + } catch (e) {} +} + +// this needs to be lazy-evaled because vue may be required before +// vue-server-renderer can set VUE_ENV +let _isServer +export const isServerRendering = () => { + if (_isServer === undefined) { + /* istanbul ignore if */ + if (!inBrowser && !inWeex && typeof global !== 'undefined') { + // detect presence of vue-server-renderer and avoid + // Webpack shimming the process + _isServer = global['process'].env.VUE_ENV === 'server' + } else { + _isServer = false + } + } + return _isServer +} + +// detect devtools +export const devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__ + +/* istanbul ignore next */ +export function isNative (Ctor: any): boolean { + return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) +} + +export const hasSymbol = + typeof Symbol !== 'undefined' && isNative(Symbol) && + typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys) + +let _Set +/* istanbul ignore if */ // $flow-disable-line +if (typeof Set !== 'undefined' && isNative(Set)) { + // use native Set when available. + _Set = Set +} else { + // a non-standard Set polyfill that only works with primitive keys. + _Set = class Set implements SimpleSet { + set: Object; + constructor () { + this.set = Object.create(null) + } + has (key: string | number) { + return this.set[key] === true + } + add (key: string | number) { + this.set[key] = true + } + clear () { + this.set = Object.create(null) + } + } +} + +interface SimpleSet { + has(key: string | number): boolean; + add(key: string | number): mixed; + clear(): void; +} + +export { _Set } +export type { SimpleSet } diff --git a/node_modules/vue/src/core/util/error.js b/node_modules/vue/src/core/util/error.js new file mode 100644 index 00000000..b7d05a6d --- /dev/null +++ b/node_modules/vue/src/core/util/error.js @@ -0,0 +1,48 @@ +/* @flow */ + +import config from '../config' +import { warn } from './debug' +import { inBrowser, inWeex } from './env' + +export function handleError (err: Error, vm: any, info: string) { + if (vm) { + let cur = vm + while ((cur = cur.$parent)) { + const hooks = cur.$options.errorCaptured + if (hooks) { + for (let i = 0; i < hooks.length; i++) { + try { + const capture = hooks[i].call(cur, err, vm, info) === false + if (capture) return + } catch (e) { + globalHandleError(e, cur, 'errorCaptured hook') + } + } + } + } + } + globalHandleError(err, vm, info) +} + +function globalHandleError (err, vm, info) { + if (config.errorHandler) { + try { + return config.errorHandler.call(null, err, vm, info) + } catch (e) { + logError(e, null, 'config.errorHandler') + } + } + logError(err, vm, info) +} + +function logError (err, vm, info) { + if (process.env.NODE_ENV !== 'production') { + warn(`Error in ${info}: "${err.toString()}"`, vm) + } + /* istanbul ignore else */ + if ((inBrowser || inWeex) && typeof console !== 'undefined') { + console.error(err) + } else { + throw err + } +} diff --git a/node_modules/vue/src/core/util/index.js b/node_modules/vue/src/core/util/index.js new file mode 100644 index 00000000..115d77cd --- /dev/null +++ b/node_modules/vue/src/core/util/index.js @@ -0,0 +1,11 @@ +/* @flow */ + +export * from 'shared/util' +export * from './lang' +export * from './env' +export * from './options' +export * from './debug' +export * from './props' +export * from './error' +export * from './next-tick' +export { defineReactive } from '../observer/index' diff --git a/node_modules/vue/src/core/util/lang.js b/node_modules/vue/src/core/util/lang.js new file mode 100644 index 00000000..96b8219a --- /dev/null +++ b/node_modules/vue/src/core/util/lang.js @@ -0,0 +1,39 @@ +/* @flow */ + +/** + * Check if a string starts with $ or _ + */ +export function isReserved (str: string): boolean { + const c = (str + '').charCodeAt(0) + return c === 0x24 || c === 0x5F +} + +/** + * Define a property. + */ +export function def (obj: Object, key: string, val: any, enumerable?: boolean) { + Object.defineProperty(obj, key, { + value: val, + enumerable: !!enumerable, + writable: true, + configurable: true + }) +} + +/** + * Parse simple path. + */ +const bailRE = /[^\w.$]/ +export function parsePath (path: string): any { + if (bailRE.test(path)) { + return + } + const segments = path.split('.') + return function (obj) { + for (let i = 0; i < segments.length; i++) { + if (!obj) return + obj = obj[segments[i]] + } + return obj + } +} diff --git a/node_modules/vue/src/core/util/next-tick.js b/node_modules/vue/src/core/util/next-tick.js new file mode 100644 index 00000000..07a9e841 --- /dev/null +++ b/node_modules/vue/src/core/util/next-tick.js @@ -0,0 +1,117 @@ +/* @flow */ +/* globals MessageChannel */ + +import { noop } from 'shared/util' +import { handleError } from './error' +import { isIOS, isNative } from './env' + +const callbacks = [] +let pending = false + +function flushCallbacks () { + pending = false + const copies = callbacks.slice(0) + callbacks.length = 0 + for (let i = 0; i < copies.length; i++) { + copies[i]() + } +} + +// Here we have async deferring wrappers using both microtasks and (macro) tasks. +// In < 2.4 we used microtasks everywhere, but there are some scenarios where +// microtasks have too high a priority and fire in between supposedly +// sequential events (e.g. #4521, #6690) or even between bubbling of the same +// event (#6566). However, using (macro) tasks everywhere also has subtle problems +// when state is changed right before repaint (e.g. #6813, out-in transitions). +// Here we use microtask by default, but expose a way to force (macro) task when +// needed (e.g. in event handlers attached by v-on). +let microTimerFunc +let macroTimerFunc +let useMacroTask = false + +// Determine (macro) task defer implementation. +// Technically setImmediate should be the ideal choice, but it's only available +// in IE. The only polyfill that consistently queues the callback after all DOM +// events triggered in the same loop is by using MessageChannel. +/* istanbul ignore if */ +if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { + macroTimerFunc = () => { + setImmediate(flushCallbacks) + } +} else if (typeof MessageChannel !== 'undefined' && ( + isNative(MessageChannel) || + // PhantomJS + MessageChannel.toString() === '[object MessageChannelConstructor]' +)) { + const channel = new MessageChannel() + const port = channel.port2 + channel.port1.onmessage = flushCallbacks + macroTimerFunc = () => { + port.postMessage(1) + } +} else { + /* istanbul ignore next */ + macroTimerFunc = () => { + setTimeout(flushCallbacks, 0) + } +} + +// Determine microtask defer implementation. +/* istanbul ignore next, $flow-disable-line */ +if (typeof Promise !== 'undefined' && isNative(Promise)) { + const p = Promise.resolve() + microTimerFunc = () => { + p.then(flushCallbacks) + // in problematic UIWebViews, Promise.then doesn't completely break, but + // it can get stuck in a weird state where callbacks are pushed into the + // microtask queue but the queue isn't being flushed, until the browser + // needs to do some other work, e.g. handle a timer. Therefore we can + // "force" the microtask queue to be flushed by adding an empty timer. + if (isIOS) setTimeout(noop) + } +} else { + // fallback to macro + microTimerFunc = macroTimerFunc +} + +/** + * Wrap a function so that if any code inside triggers state change, + * the changes are queued using a (macro) task instead of a microtask. + */ +export function withMacroTask (fn: Function): Function { + return fn._withTask || (fn._withTask = function () { + useMacroTask = true + const res = fn.apply(null, arguments) + useMacroTask = false + return res + }) +} + +export function nextTick (cb?: Function, ctx?: Object) { + let _resolve + callbacks.push(() => { + if (cb) { + try { + cb.call(ctx) + } catch (e) { + handleError(e, ctx, 'nextTick') + } + } else if (_resolve) { + _resolve(ctx) + } + }) + if (!pending) { + pending = true + if (useMacroTask) { + macroTimerFunc() + } else { + microTimerFunc() + } + } + // $flow-disable-line + if (!cb && typeof Promise !== 'undefined') { + return new Promise(resolve => { + _resolve = resolve + }) + } +} 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 +} diff --git a/node_modules/vue/src/core/util/perf.js b/node_modules/vue/src/core/util/perf.js new file mode 100644 index 00000000..4a537b2d --- /dev/null +++ b/node_modules/vue/src/core/util/perf.js @@ -0,0 +1,24 @@ +import { inBrowser } from './env' + +export let mark +export let measure + +if (process.env.NODE_ENV !== 'production') { + const perf = inBrowser && window.performance + /* istanbul ignore if */ + if ( + perf && + perf.mark && + perf.measure && + perf.clearMarks && + perf.clearMeasures + ) { + mark = tag => perf.mark(tag) + measure = (name, startTag, endTag) => { + perf.measure(name, startTag, endTag) + perf.clearMarks(startTag) + perf.clearMarks(endTag) + perf.clearMeasures(name) + } + } +} diff --git a/node_modules/vue/src/core/util/props.js b/node_modules/vue/src/core/util/props.js new file mode 100644 index 00000000..a92643f9 --- /dev/null +++ b/node_modules/vue/src/core/util/props.js @@ -0,0 +1,202 @@ +/* @flow */ + +import { warn } from './debug' +import { observe, toggleObserving, shouldObserve } from '../observer/index' +import { + hasOwn, + isObject, + toRawType, + hyphenate, + capitalize, + isPlainObject +} from 'shared/util' + +type PropOptions = { + type: Function | Array<Function> | null, + default: any, + required: ?boolean, + validator: ?Function +}; + +export function validateProp ( + key: string, + propOptions: Object, + propsData: Object, + vm?: Component +): any { + const prop = propOptions[key] + const absent = !hasOwn(propsData, key) + let value = propsData[key] + // boolean casting + const booleanIndex = getTypeIndex(Boolean, prop.type) + if (booleanIndex > -1) { + if (absent && !hasOwn(prop, 'default')) { + value = false + } else if (value === '' || value === hyphenate(key)) { + // only cast empty string / same name to boolean if + // boolean has higher priority + const stringIndex = getTypeIndex(String, prop.type) + if (stringIndex < 0 || booleanIndex < stringIndex) { + value = true + } + } + } + // check default value + if (value === undefined) { + value = getPropDefaultValue(vm, prop, key) + // since the default value is a fresh copy, + // make sure to observe it. + const prevShouldObserve = shouldObserve + toggleObserving(true) + observe(value) + toggleObserving(prevShouldObserve) + } + if ( + process.env.NODE_ENV !== 'production' && + // skip validation for weex recycle-list child component props + !(__WEEX__ && isObject(value) && ('@binding' in value)) + ) { + assertProp(prop, key, value, vm, absent) + } + return value +} + +/** + * Get the default value of a prop. + */ +function getPropDefaultValue (vm: ?Component, prop: PropOptions, key: string): any { + // no default, return undefined + if (!hasOwn(prop, 'default')) { + return undefined + } + const def = prop.default + // warn against non-factory defaults for Object & Array + if (process.env.NODE_ENV !== 'production' && isObject(def)) { + warn( + 'Invalid default value for prop "' + key + '": ' + + 'Props with type Object/Array must use a factory function ' + + 'to return the default value.', + vm + ) + } + // the raw prop value was also undefined from previous render, + // return previous default value to avoid unnecessary watcher trigger + if (vm && vm.$options.propsData && + vm.$options.propsData[key] === undefined && + vm._props[key] !== undefined + ) { + return vm._props[key] + } + // call factory function for non-Function types + // a value is Function if its prototype is function even across different execution context + return typeof def === 'function' && getType(prop.type) !== 'Function' + ? def.call(vm) + : def +} + +/** + * Assert whether a prop is valid. + */ +function assertProp ( + prop: PropOptions, + name: string, + value: any, + vm: ?Component, + absent: boolean +) { + if (prop.required && absent) { + warn( + 'Missing required prop: "' + name + '"', + vm + ) + return + } + if (value == null && !prop.required) { + return + } + let type = prop.type + let valid = !type || type === true + const expectedTypes = [] + if (type) { + if (!Array.isArray(type)) { + type = [type] + } + for (let i = 0; i < type.length && !valid; i++) { + const assertedType = assertType(value, type[i]) + expectedTypes.push(assertedType.expectedType || '') + valid = assertedType.valid + } + } + if (!valid) { + warn( + `Invalid prop: type check failed for prop "${name}".` + + ` Expected ${expectedTypes.map(capitalize).join(', ')}` + + `, got ${toRawType(value)}.`, + vm + ) + return + } + const validator = prop.validator + if (validator) { + if (!validator(value)) { + warn( + 'Invalid prop: custom validator check failed for prop "' + name + '".', + vm + ) + } + } +} + +const simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/ + +function assertType (value: any, type: Function): { + valid: boolean; + expectedType: string; +} { + let valid + const expectedType = getType(type) + if (simpleCheckRE.test(expectedType)) { + const t = typeof value + valid = t === expectedType.toLowerCase() + // for primitive wrapper objects + if (!valid && t === 'object') { + valid = value instanceof type + } + } else if (expectedType === 'Object') { + valid = isPlainObject(value) + } else if (expectedType === 'Array') { + valid = Array.isArray(value) + } else { + valid = value instanceof type + } + return { + valid, + expectedType + } +} + +/** + * Use function string name to check built-in types, + * because a simple equality check will fail when running + * across different vms / iframes. + */ +function getType (fn) { + const match = fn && fn.toString().match(/^\s*function (\w+)/) + return match ? match[1] : '' +} + +function isSameType (a, b) { + return getType(a) === getType(b) +} + +function getTypeIndex (type, expectedTypes): number { + if (!Array.isArray(expectedTypes)) { + return isSameType(expectedTypes, type) ? 0 : -1 + } + for (let i = 0, len = expectedTypes.length; i < len; i++) { + if (isSameType(expectedTypes[i], type)) { + return i + } + } + return -1 +} |
