modified .gitignore
This commit is contained in:
+7
-5
@@ -5,14 +5,16 @@ import { getFirstComponentChild } from 'core/vdom/helpers/index'
|
||||
|
||||
type VNodeCache = { [key: string]: ?VNode };
|
||||
|
||||
const patternTypes: Array<Function> = [String, RegExp]
|
||||
const patternTypes: Array<Function> = [String, RegExp, Array]
|
||||
|
||||
function getComponentName (opts: ?VNodeComponentOptions): ?string {
|
||||
return opts && (opts.Ctor.options.name || opts.tag)
|
||||
}
|
||||
|
||||
function matches (pattern: string | RegExp, name: string): boolean {
|
||||
if (typeof pattern === 'string') {
|
||||
function matches (pattern: string | RegExp | Array<string>, name: string): boolean {
|
||||
if (Array.isArray(pattern)) {
|
||||
return pattern.indexOf(name) > -1
|
||||
} else if (typeof pattern === 'string') {
|
||||
return pattern.split(',').indexOf(name) > -1
|
||||
} else if (isRegExp(pattern)) {
|
||||
return pattern.test(name)
|
||||
@@ -62,10 +64,10 @@ export default {
|
||||
},
|
||||
|
||||
watch: {
|
||||
include (val: string | RegExp) {
|
||||
include (val: string | RegExp | Array<string>) {
|
||||
pruneCache(this.cache, this._vnode, name => matches(val, name))
|
||||
},
|
||||
exclude (val: string | RegExp) {
|
||||
exclude (val: string | RegExp | Array<string>) {
|
||||
pruneCache(this.cache, this._vnode, name => !matches(val, name))
|
||||
}
|
||||
},
|
||||
|
||||
+6
@@ -16,6 +16,7 @@ export type Config = {
|
||||
performance: boolean;
|
||||
devtools: boolean;
|
||||
errorHandler: ?(err: Error, vm: Component, info: string) => void;
|
||||
warnHandler: ?(msg: string, vm: Component, trace: string) => void;
|
||||
ignoredElements: Array<string>;
|
||||
keyCodes: { [key: string]: number | Array<number> };
|
||||
|
||||
@@ -62,6 +63,11 @@ export default ({
|
||||
*/
|
||||
errorHandler: null,
|
||||
|
||||
/**
|
||||
* Warn handler for watcher warns
|
||||
*/
|
||||
warnHandler: null,
|
||||
|
||||
/**
|
||||
* Ignore certain custom elements
|
||||
*/
|
||||
|
||||
+4
-3
@@ -4,10 +4,11 @@ import { toArray } from '../util/index'
|
||||
|
||||
export function initUse (Vue: GlobalAPI) {
|
||||
Vue.use = function (plugin: Function | Object) {
|
||||
/* istanbul ignore if */
|
||||
if (plugin.installed) {
|
||||
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
|
||||
if (installedPlugins.indexOf(plugin) > -1) {
|
||||
return this
|
||||
}
|
||||
|
||||
// additional parameters
|
||||
const args = toArray(arguments, 1)
|
||||
args.unshift(this)
|
||||
@@ -16,7 +17,7 @@ export function initUse (Vue: GlobalAPI) {
|
||||
} else if (typeof plugin === 'function') {
|
||||
plugin.apply(null, args)
|
||||
}
|
||||
plugin.installed = true
|
||||
installedPlugins.push(plugin)
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ Object.defineProperty(Vue.prototype, '$isServer', {
|
||||
Object.defineProperty(Vue.prototype, '$ssrContext', {
|
||||
get () {
|
||||
/* istanbul ignore next */
|
||||
return this.$vnode.ssrContext
|
||||
return this.$vnode && this.$vnode.ssrContext
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
+22
-10
@@ -1,7 +1,13 @@
|
||||
/* @flow */
|
||||
|
||||
import {
|
||||
tip,
|
||||
toArray,
|
||||
hyphenate,
|
||||
handleError,
|
||||
formatComponentName
|
||||
} from '../util/index'
|
||||
import { updateListeners } from '../vdom/helpers/index'
|
||||
import { toArray, tip, hyphenate, formatComponentName } from '../util/index'
|
||||
|
||||
export function initEvents (vm: Component) {
|
||||
vm._events = Object.create(null)
|
||||
@@ -89,14 +95,16 @@ export function eventsMixin (Vue: Class<Component>) {
|
||||
vm._events[event] = null
|
||||
return vm
|
||||
}
|
||||
// specific handler
|
||||
let cb
|
||||
let i = cbs.length
|
||||
while (i--) {
|
||||
cb = cbs[i]
|
||||
if (cb === fn || cb.fn === fn) {
|
||||
cbs.splice(i, 1)
|
||||
break
|
||||
if (fn) {
|
||||
// specific handler
|
||||
let cb
|
||||
let i = cbs.length
|
||||
while (i--) {
|
||||
cb = cbs[i]
|
||||
if (cb === fn || cb.fn === fn) {
|
||||
cbs.splice(i, 1)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return vm
|
||||
@@ -121,7 +129,11 @@ export function eventsMixin (Vue: Class<Component>) {
|
||||
cbs = cbs.length > 1 ? toArray(cbs) : cbs
|
||||
const args = toArray(arguments, 1)
|
||||
for (let i = 0, l = cbs.length; i < l; i++) {
|
||||
cbs[i].apply(vm, args)
|
||||
try {
|
||||
cbs[i].apply(vm, args)
|
||||
} catch (e) {
|
||||
handleError(e, vm, `event handler for "${event}"`)
|
||||
}
|
||||
}
|
||||
}
|
||||
return vm
|
||||
|
||||
+13
-9
@@ -1,8 +1,8 @@
|
||||
/* @flow */
|
||||
|
||||
import { hasSymbol } from 'core/util/env'
|
||||
import { warn } from '../util/index'
|
||||
import { defineReactive } from '../observer/index'
|
||||
import { hasSymbol } from 'core/util/env'
|
||||
import { defineReactive, observerState } from '../observer/index'
|
||||
|
||||
export function initProvide (vm: Component) {
|
||||
const provide = vm.$options.provide
|
||||
@@ -16,6 +16,7 @@ export function initProvide (vm: Component) {
|
||||
export function initInjections (vm: Component) {
|
||||
const result = resolveInject(vm.$options.inject, vm)
|
||||
if (result) {
|
||||
observerState.shouldConvert = false
|
||||
Object.keys(result).forEach(key => {
|
||||
/* istanbul ignore else */
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
@@ -31,24 +32,24 @@ export function initInjections (vm: Component) {
|
||||
defineReactive(vm, key, result[key])
|
||||
}
|
||||
})
|
||||
observerState.shouldConvert = true
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveInject (inject: any, vm: Component): ?Object {
|
||||
if (inject) {
|
||||
// inject is :any because flow is not smart enough to figure out cached
|
||||
// isArray here
|
||||
const isArray = Array.isArray(inject)
|
||||
const result = Object.create(null)
|
||||
const keys = isArray
|
||||
? inject
|
||||
: hasSymbol
|
||||
? Reflect.ownKeys(inject)
|
||||
const keys = hasSymbol
|
||||
? Reflect.ownKeys(inject).filter(key => {
|
||||
/* istanbul ignore next */
|
||||
return Object.getOwnPropertyDescriptor(inject, key).enumerable
|
||||
})
|
||||
: Object.keys(inject)
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i]
|
||||
const provideKey = isArray ? key : inject[key]
|
||||
const provideKey = inject[key]
|
||||
let source = vm
|
||||
while (source) {
|
||||
if (source._provided && provideKey in source._provided) {
|
||||
@@ -57,6 +58,9 @@ export function resolveInject (inject: any, vm: Component): ?Object {
|
||||
}
|
||||
source = source.$parent
|
||||
}
|
||||
if (process.env.NODE_ENV !== 'production' && !source) {
|
||||
warn(`Injection "${key}" not found`, vm)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
+20
-8
@@ -18,6 +18,7 @@ import {
|
||||
} from '../util/index'
|
||||
|
||||
export let activeInstance: any = null
|
||||
export let isUpdatingChildComponent: boolean = false
|
||||
|
||||
export function initLifecycle (vm: Component) {
|
||||
const options = vm.$options
|
||||
@@ -65,6 +66,9 @@ export function lifecycleMixin (Vue: Class<Component>) {
|
||||
vm.$options._parentElm,
|
||||
vm.$options._refElm
|
||||
)
|
||||
// no need for the ref nodes after initial patch
|
||||
// this prevents keeping a detached DOM tree in memory (#5851)
|
||||
vm.$options._parentElm = vm.$options._refElm = null
|
||||
} else {
|
||||
// updates
|
||||
vm.$el = vm.__patch__(prevVnode, vnode)
|
||||
@@ -129,8 +133,6 @@ export function lifecycleMixin (Vue: Class<Component>) {
|
||||
if (vm.$el) {
|
||||
vm.$el.__vue__ = null
|
||||
}
|
||||
// remove reference to DOM nodes (prevents leak)
|
||||
vm.$options._parentElm = vm.$options._refElm = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,6 +208,10 @@ export function updateChildComponent (
|
||||
parentVnode: VNode,
|
||||
renderChildren: ?Array<VNode>
|
||||
) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
isUpdatingChildComponent = true
|
||||
}
|
||||
|
||||
// determine whether component has slot children
|
||||
// we need to do this before overwriting $options._renderChildren
|
||||
const hasChildren = !!(
|
||||
@@ -217,17 +223,21 @@ export function updateChildComponent (
|
||||
|
||||
vm.$options._parentVnode = parentVnode
|
||||
vm.$vnode = parentVnode // update vm's placeholder node without re-render
|
||||
|
||||
if (vm._vnode) { // update child tree's parent
|
||||
vm._vnode.parent = parentVnode
|
||||
}
|
||||
vm.$options._renderChildren = renderChildren
|
||||
|
||||
// update $attrs and $listeners hash
|
||||
// these are also reactive so they may trigger child update if the child
|
||||
// used them during render
|
||||
vm.$attrs = (parentVnode.data && parentVnode.data.attrs) || emptyObject
|
||||
vm.$listeners = listeners || emptyObject
|
||||
|
||||
// update props
|
||||
if (propsData && vm.$options.props) {
|
||||
observerState.shouldConvert = false
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
observerState.isSettingProps = true
|
||||
}
|
||||
const props = vm._props
|
||||
const propKeys = vm.$options._propKeys || []
|
||||
for (let i = 0; i < propKeys.length; i++) {
|
||||
@@ -235,12 +245,10 @@ export function updateChildComponent (
|
||||
props[key] = validateProp(key, vm.$options.props, propsData, vm)
|
||||
}
|
||||
observerState.shouldConvert = true
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
observerState.isSettingProps = false
|
||||
}
|
||||
// keep a copy of raw propsData
|
||||
vm.$options.propsData = propsData
|
||||
}
|
||||
|
||||
// update listeners
|
||||
if (listeners) {
|
||||
const oldListeners = vm.$options._parentListeners
|
||||
@@ -252,6 +260,10 @@ export function updateChildComponent (
|
||||
vm.$slots = resolveSlots(renderChildren, parentVnode.context)
|
||||
vm.$forceUpdate()
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
isUpdatingChildComponent = false
|
||||
}
|
||||
}
|
||||
|
||||
function isInInactiveTree (vm) {
|
||||
|
||||
+21
-3
@@ -1,7 +1,13 @@
|
||||
/* @flow */
|
||||
|
||||
import config from 'core/config'
|
||||
import { isObject, warn, toObject } from 'core/util/index'
|
||||
|
||||
import {
|
||||
warn,
|
||||
isObject,
|
||||
toObject,
|
||||
isReservedAttribute
|
||||
} from 'core/util/index'
|
||||
|
||||
/**
|
||||
* Runtime helper for merging v-bind="object" into a VNode's data.
|
||||
@@ -10,7 +16,8 @@ export function bindObjectProps (
|
||||
data: any,
|
||||
tag: string,
|
||||
value: any,
|
||||
asProp?: boolean
|
||||
asProp: boolean,
|
||||
isSync?: boolean
|
||||
): VNodeData {
|
||||
if (value) {
|
||||
if (!isObject(value)) {
|
||||
@@ -24,7 +31,11 @@ export function bindObjectProps (
|
||||
}
|
||||
let hash
|
||||
for (const key in value) {
|
||||
if (key === 'class' || key === 'style') {
|
||||
if (
|
||||
key === 'class' ||
|
||||
key === 'style' ||
|
||||
isReservedAttribute(key)
|
||||
) {
|
||||
hash = data
|
||||
} else {
|
||||
const type = data.attrs && data.attrs.type
|
||||
@@ -34,6 +45,13 @@ export function bindObjectProps (
|
||||
}
|
||||
if (!(key in hash)) {
|
||||
hash[key] = value[key]
|
||||
|
||||
if (isSync) {
|
||||
const on = data.on || (data.on = {})
|
||||
on[`update:${key}`] = function ($event) {
|
||||
value[key] = $event
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -7,7 +7,11 @@ import { isObject, isDef } from 'core/util/index'
|
||||
*/
|
||||
export function renderList (
|
||||
val: any,
|
||||
render: () => VNode
|
||||
render: (
|
||||
val: any,
|
||||
keyOrIndex: string | number,
|
||||
index?: number
|
||||
) => VNode
|
||||
): ?Array<VNode> {
|
||||
let ret: ?Array<VNode>, i, l, keys, key
|
||||
if (Array.isArray(val) || typeof val === 'string') {
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ export function renderSlot (
|
||||
if (scopedSlotFn) { // scoped slot
|
||||
props = props || {}
|
||||
if (bindObject) {
|
||||
extend(props, bindObject)
|
||||
props = extend(extend({}, bindObject), props)
|
||||
}
|
||||
return scopedSlotFn(props) || fallback
|
||||
} else {
|
||||
|
||||
+6
-1
@@ -14,10 +14,15 @@ export function resolveSlots (
|
||||
const defaultSlot = []
|
||||
for (let i = 0, l = children.length; i < l; i++) {
|
||||
const child = children[i]
|
||||
const data = child.data
|
||||
// remove slot attribute if the node is resolved as a Vue slot node
|
||||
if (data && data.attrs && data.attrs.slot) {
|
||||
delete data.attrs.slot
|
||||
}
|
||||
// named slots should only be respected if the vnode was rendered in the
|
||||
// same context.
|
||||
if ((child.context === context || child.functionalContext === context) &&
|
||||
child.data && child.data.slot != null
|
||||
data && data.slot != null
|
||||
) {
|
||||
const name = child.data.slot
|
||||
const slot = (slots[name] || (slots[name] = []))
|
||||
|
||||
+29
-3
@@ -8,7 +8,8 @@ import {
|
||||
looseEqual,
|
||||
emptyObject,
|
||||
handleError,
|
||||
looseIndexOf
|
||||
looseIndexOf,
|
||||
defineReactive
|
||||
} from '../util/index'
|
||||
|
||||
import VNode, {
|
||||
@@ -17,6 +18,8 @@ import VNode, {
|
||||
createEmptyVNode
|
||||
} from '../vdom/vnode'
|
||||
|
||||
import { isUpdatingChildComponent } from './lifecycle'
|
||||
|
||||
import { createElement } from '../vdom/create-element'
|
||||
import { renderList } from './render-helpers/render-list'
|
||||
import { renderSlot } from './render-helpers/render-slot'
|
||||
@@ -24,6 +27,7 @@ import { resolveFilter } from './render-helpers/resolve-filter'
|
||||
import { checkKeyCodes } from './render-helpers/check-keycodes'
|
||||
import { bindObjectProps } from './render-helpers/bind-object-props'
|
||||
import { renderStatic, markOnce } from './render-helpers/render-static'
|
||||
import { bindObjectListeners } from './render-helpers/bind-object-listeners'
|
||||
import { resolveSlots, resolveScopedSlots } from './render-helpers/resolve-slots'
|
||||
|
||||
export function initRender (vm: Component) {
|
||||
@@ -41,6 +45,23 @@ export function initRender (vm: Component) {
|
||||
// normalization is always applied for the public version, used in
|
||||
// user-written render functions.
|
||||
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
|
||||
|
||||
// $attrs & $listeners are exposed for easier HOC creation.
|
||||
// they need to be reactive so that HOCs using them are always updated
|
||||
const parentData = parentVnode && parentVnode.data
|
||||
|
||||
/* istanbul ignore else */
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () => {
|
||||
!isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)
|
||||
}, true)
|
||||
defineReactive(vm, '$listeners', vm.$options._parentListeners || emptyObject, () => {
|
||||
!isUpdatingChildComponent && warn(`$listeners is readonly.`, vm)
|
||||
}, true)
|
||||
} else {
|
||||
defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)
|
||||
defineReactive(vm, '$listeners', vm.$options._parentListeners || emptyObject, null, true)
|
||||
}
|
||||
}
|
||||
|
||||
export function renderMixin (Vue: Class<Component>) {
|
||||
@@ -57,9 +78,13 @@ export function renderMixin (Vue: Class<Component>) {
|
||||
} = vm.$options
|
||||
|
||||
if (vm._isMounted) {
|
||||
// clone slot nodes on re-renders
|
||||
// if the parent didn't update, the slot nodes will be the ones from
|
||||
// last render. They need to be cloned to ensure "freshness" for this render.
|
||||
for (const key in vm.$slots) {
|
||||
vm.$slots[key] = cloneVNodes(vm.$slots[key])
|
||||
const slot = vm.$slots[key]
|
||||
if (slot._rendered) {
|
||||
vm.$slots[key] = cloneVNodes(slot, true /* deep */)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,4 +146,5 @@ export function renderMixin (Vue: Class<Component>) {
|
||||
Vue.prototype._v = createTextVNode
|
||||
Vue.prototype._e = createEmptyVNode
|
||||
Vue.prototype._u = resolveScopedSlots
|
||||
Vue.prototype._g = bindObjectListeners
|
||||
}
|
||||
|
||||
+92
-33
@@ -3,6 +3,7 @@
|
||||
import config from '../config'
|
||||
import Dep from '../observer/dep'
|
||||
import Watcher from '../observer/watcher'
|
||||
import { isUpdatingChildComponent } from './lifecycle'
|
||||
|
||||
import {
|
||||
set,
|
||||
@@ -19,8 +20,11 @@ import {
|
||||
hasOwn,
|
||||
isReserved,
|
||||
handleError,
|
||||
nativeWatch,
|
||||
validateProp,
|
||||
isPlainObject
|
||||
isPlainObject,
|
||||
isServerRendering,
|
||||
isReservedAttribute
|
||||
} from '../util/index'
|
||||
|
||||
const sharedPropertyDefinition = {
|
||||
@@ -51,13 +55,19 @@ export function initState (vm: Component) {
|
||||
observe(vm._data = {}, true /* asRootData */)
|
||||
}
|
||||
if (opts.computed) initComputed(vm, opts.computed)
|
||||
if (opts.watch) initWatch(vm, opts.watch)
|
||||
if (opts.watch && opts.watch !== nativeWatch) {
|
||||
initWatch(vm, opts.watch)
|
||||
}
|
||||
}
|
||||
|
||||
const isReservedProp = {
|
||||
key: 1,
|
||||
ref: 1,
|
||||
slot: 1
|
||||
function checkOptionType (vm: Component, name: string) {
|
||||
const option = vm.$options[name]
|
||||
if (!isPlainObject(option)) {
|
||||
warn(
|
||||
`component option "${name}" should be an object.`,
|
||||
vm
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function initProps (vm: Component, propsOptions: Object) {
|
||||
@@ -74,14 +84,14 @@ function initProps (vm: Component, propsOptions: Object) {
|
||||
const value = validateProp(key, propsOptions, propsData, vm)
|
||||
/* istanbul ignore else */
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (isReservedProp[key] || config.isReservedAttr(key)) {
|
||||
if (isReservedAttribute(key) || config.isReservedAttr(key)) {
|
||||
warn(
|
||||
`"${key}" is a reserved attribute and cannot be used as component prop.`,
|
||||
vm
|
||||
)
|
||||
}
|
||||
defineReactive(props, key, value, () => {
|
||||
if (vm.$parent && !observerState.isSettingProps) {
|
||||
if (vm.$parent && !isUpdatingChildComponent) {
|
||||
warn(
|
||||
`Avoid mutating a prop directly since the value will be ` +
|
||||
`overwritten whenever the parent component re-renders. ` +
|
||||
@@ -120,16 +130,26 @@ function initData (vm: Component) {
|
||||
// proxy data on instance
|
||||
const keys = Object.keys(data)
|
||||
const props = vm.$options.props
|
||||
const methods = vm.$options.methods
|
||||
let i = keys.length
|
||||
while (i--) {
|
||||
if (props && hasOwn(props, keys[i])) {
|
||||
const key = keys[i]
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (methods && hasOwn(methods, key)) {
|
||||
warn(
|
||||
`Method "${key}" has already been defined as a data property.`,
|
||||
vm
|
||||
)
|
||||
}
|
||||
}
|
||||
if (props && hasOwn(props, key)) {
|
||||
process.env.NODE_ENV !== 'production' && warn(
|
||||
`The data property "${keys[i]}" is already declared as a prop. ` +
|
||||
`The data property "${key}" is already declared as a prop. ` +
|
||||
`Use prop default value instead.`,
|
||||
vm
|
||||
)
|
||||
} else if (!isReserved(keys[i])) {
|
||||
proxy(vm, `_data`, keys[i])
|
||||
} else if (!isReserved(key)) {
|
||||
proxy(vm, `_data`, key)
|
||||
}
|
||||
}
|
||||
// observe data
|
||||
@@ -148,22 +168,30 @@ function getData (data: Function, vm: Component): any {
|
||||
const computedWatcherOptions = { lazy: true }
|
||||
|
||||
function initComputed (vm: Component, computed: Object) {
|
||||
process.env.NODE_ENV !== 'production' && checkOptionType(vm, 'computed')
|
||||
const watchers = vm._computedWatchers = Object.create(null)
|
||||
// computed properties are just getters during SSR
|
||||
const isSSR = isServerRendering()
|
||||
|
||||
for (const key in computed) {
|
||||
const userDef = computed[key]
|
||||
let getter = typeof userDef === 'function' ? userDef : userDef.get
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (getter === undefined) {
|
||||
warn(
|
||||
`No getter function has been defined for computed property "${key}".`,
|
||||
vm
|
||||
)
|
||||
getter = noop
|
||||
}
|
||||
const getter = typeof userDef === 'function' ? userDef : userDef.get
|
||||
if (process.env.NODE_ENV !== 'production' && getter == null) {
|
||||
warn(
|
||||
`Getter is missing for computed property "${key}".`,
|
||||
vm
|
||||
)
|
||||
}
|
||||
|
||||
if (!isSSR) {
|
||||
// create internal watcher for the computed property.
|
||||
watchers[key] = new Watcher(
|
||||
vm,
|
||||
getter || noop,
|
||||
noop,
|
||||
computedWatcherOptions
|
||||
)
|
||||
}
|
||||
// create internal watcher for the computed property.
|
||||
watchers[key] = new Watcher(vm, getter, noop, computedWatcherOptions)
|
||||
|
||||
// component-defined computed properties are already defined on the
|
||||
// component prototype. We only need to define computed properties defined
|
||||
@@ -180,13 +208,20 @@ function initComputed (vm: Component, computed: Object) {
|
||||
}
|
||||
}
|
||||
|
||||
export function defineComputed (target: any, key: string, userDef: Object | Function) {
|
||||
export function defineComputed (
|
||||
target: any,
|
||||
key: string,
|
||||
userDef: Object | Function
|
||||
) {
|
||||
const shouldCache = !isServerRendering()
|
||||
if (typeof userDef === 'function') {
|
||||
sharedPropertyDefinition.get = createComputedGetter(key)
|
||||
sharedPropertyDefinition.get = shouldCache
|
||||
? createComputedGetter(key)
|
||||
: userDef
|
||||
sharedPropertyDefinition.set = noop
|
||||
} else {
|
||||
sharedPropertyDefinition.get = userDef.get
|
||||
? userDef.cache !== false
|
||||
? shouldCache && userDef.cache !== false
|
||||
? createComputedGetter(key)
|
||||
: userDef.get
|
||||
: noop
|
||||
@@ -194,6 +229,15 @@ export function defineComputed (target: any, key: string, userDef: Object | Func
|
||||
? userDef.set
|
||||
: noop
|
||||
}
|
||||
if (process.env.NODE_ENV !== 'production' &&
|
||||
sharedPropertyDefinition.set === noop) {
|
||||
sharedPropertyDefinition.set = function () {
|
||||
warn(
|
||||
`Computed property "${key}" was assigned to but it has no setter.`,
|
||||
this
|
||||
)
|
||||
}
|
||||
}
|
||||
Object.defineProperty(target, key, sharedPropertyDefinition)
|
||||
}
|
||||
|
||||
@@ -213,28 +257,36 @@ function createComputedGetter (key) {
|
||||
}
|
||||
|
||||
function initMethods (vm: Component, methods: Object) {
|
||||
process.env.NODE_ENV !== 'production' && checkOptionType(vm, 'methods')
|
||||
const props = vm.$options.props
|
||||
for (const key in methods) {
|
||||
vm[key] = methods[key] == null ? noop : bind(methods[key], vm)
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (methods[key] == null) {
|
||||
warn(
|
||||
`method "${key}" has an undefined value in the component definition. ` +
|
||||
`Method "${key}" has an undefined value in the component definition. ` +
|
||||
`Did you reference the function correctly?`,
|
||||
vm
|
||||
)
|
||||
}
|
||||
if (props && hasOwn(props, key)) {
|
||||
warn(
|
||||
`method "${key}" has already been defined as a prop.`,
|
||||
`Method "${key}" has already been defined as a prop.`,
|
||||
vm
|
||||
)
|
||||
}
|
||||
if ((key in vm) && isReserved(key)) {
|
||||
warn(
|
||||
`Method "${key}" conflicts with an existing Vue instance method. ` +
|
||||
`Avoid defining component methods that start with _ or $.`
|
||||
)
|
||||
}
|
||||
}
|
||||
vm[key] = methods[key] == null ? noop : bind(methods[key], vm)
|
||||
}
|
||||
}
|
||||
|
||||
function initWatch (vm: Component, watch: Object) {
|
||||
process.env.NODE_ENV !== 'production' && checkOptionType(vm, 'watch')
|
||||
for (const key in watch) {
|
||||
const handler = watch[key]
|
||||
if (Array.isArray(handler)) {
|
||||
@@ -247,8 +299,12 @@ function initWatch (vm: Component, watch: Object) {
|
||||
}
|
||||
}
|
||||
|
||||
function createWatcher (vm: Component, key: string, handler: any) {
|
||||
let options
|
||||
function createWatcher (
|
||||
vm: Component,
|
||||
keyOrFn: string | Function,
|
||||
handler: any,
|
||||
options?: Object
|
||||
) {
|
||||
if (isPlainObject(handler)) {
|
||||
options = handler
|
||||
handler = handler.handler
|
||||
@@ -256,7 +312,7 @@ function createWatcher (vm: Component, key: string, handler: any) {
|
||||
if (typeof handler === 'string') {
|
||||
handler = vm[handler]
|
||||
}
|
||||
vm.$watch(key, handler, options)
|
||||
return vm.$watch(keyOrFn, handler, options)
|
||||
}
|
||||
|
||||
export function stateMixin (Vue: Class<Component>) {
|
||||
@@ -287,10 +343,13 @@ export function stateMixin (Vue: Class<Component>) {
|
||||
|
||||
Vue.prototype.$watch = function (
|
||||
expOrFn: string | Function,
|
||||
cb: Function,
|
||||
cb: any,
|
||||
options?: Object
|
||||
): Function {
|
||||
const vm: Component = this
|
||||
if (isPlainObject(cb)) {
|
||||
return createWatcher(vm, expOrFn, cb, options)
|
||||
}
|
||||
options = options || {}
|
||||
options.user = true
|
||||
const watcher = new Watcher(vm, expOrFn, cb, options)
|
||||
|
||||
+1
-10
@@ -23,21 +23,12 @@ export const arrayMethods = Object.create(arrayProto)
|
||||
.forEach(function (method) {
|
||||
// cache original method
|
||||
const original = arrayProto[method]
|
||||
def(arrayMethods, method, function mutator () {
|
||||
// avoid leaking arguments:
|
||||
// http://jsperf.com/closure-with-arguments
|
||||
let i = arguments.length
|
||||
const args = new Array(i)
|
||||
while (i--) {
|
||||
args[i] = arguments[i]
|
||||
}
|
||||
def(arrayMethods, method, function mutator (...args) {
|
||||
const result = original.apply(this, args)
|
||||
const ob = this.__ob__
|
||||
let inserted
|
||||
switch (method) {
|
||||
case 'push':
|
||||
inserted = args
|
||||
break
|
||||
case 'unshift':
|
||||
inserted = args
|
||||
break
|
||||
|
||||
+17
-16
@@ -4,11 +4,12 @@ import Dep from './dep'
|
||||
import { arrayMethods } from './array'
|
||||
import {
|
||||
def,
|
||||
warn,
|
||||
hasOwn,
|
||||
hasProto,
|
||||
isObject,
|
||||
isPlainObject,
|
||||
hasProto,
|
||||
hasOwn,
|
||||
warn,
|
||||
isValidArrayIndex,
|
||||
isServerRendering
|
||||
} from '../util/index'
|
||||
|
||||
@@ -21,8 +22,7 @@ const arrayKeys = Object.getOwnPropertyNames(arrayMethods)
|
||||
* under a frozen data structure. Converting it would defeat the optimization.
|
||||
*/
|
||||
export const observerState = {
|
||||
shouldConvert: true,
|
||||
isSettingProps: false
|
||||
shouldConvert: true
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,7 +80,7 @@ export class Observer {
|
||||
* Augment an target Object or Array by intercepting
|
||||
* the prototype chain using __proto__
|
||||
*/
|
||||
function protoAugment (target, src: Object) {
|
||||
function protoAugment (target, src: Object, keys: any) {
|
||||
/* eslint-disable no-proto */
|
||||
target.__proto__ = src
|
||||
/* eslint-enable no-proto */
|
||||
@@ -132,7 +132,8 @@ export function defineReactive (
|
||||
obj: Object,
|
||||
key: string,
|
||||
val: any,
|
||||
customSetter?: Function
|
||||
customSetter?: ?Function,
|
||||
shallow?: boolean
|
||||
) {
|
||||
const dep = new Dep()
|
||||
|
||||
@@ -145,7 +146,7 @@ export function defineReactive (
|
||||
const getter = property && property.get
|
||||
const setter = property && property.set
|
||||
|
||||
let childOb = observe(val)
|
||||
let childOb = !shallow && observe(val)
|
||||
Object.defineProperty(obj, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
@@ -155,9 +156,9 @@ export function defineReactive (
|
||||
dep.depend()
|
||||
if (childOb) {
|
||||
childOb.dep.depend()
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
dependArray(value)
|
||||
if (Array.isArray(value)) {
|
||||
dependArray(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return value
|
||||
@@ -177,7 +178,7 @@ export function defineReactive (
|
||||
} else {
|
||||
val = newVal
|
||||
}
|
||||
childOb = observe(newVal)
|
||||
childOb = !shallow && observe(newVal)
|
||||
dep.notify()
|
||||
}
|
||||
})
|
||||
@@ -189,7 +190,7 @@ export function defineReactive (
|
||||
* already exist.
|
||||
*/
|
||||
export function set (target: Array<any> | Object, key: any, val: any): any {
|
||||
if (Array.isArray(target) && typeof key === 'number') {
|
||||
if (Array.isArray(target) && isValidArrayIndex(key)) {
|
||||
target.length = Math.max(target.length, key)
|
||||
target.splice(key, 1, val)
|
||||
return val
|
||||
@@ -198,7 +199,7 @@ export function set (target: Array<any> | Object, key: any, val: any): any {
|
||||
target[key] = val
|
||||
return val
|
||||
}
|
||||
const ob = (target : any).__ob__
|
||||
const ob = (target: any).__ob__
|
||||
if (target._isVue || (ob && ob.vmCount)) {
|
||||
process.env.NODE_ENV !== 'production' && warn(
|
||||
'Avoid adding reactive properties to a Vue instance or its root $data ' +
|
||||
@@ -219,11 +220,11 @@ export function set (target: Array<any> | Object, key: any, val: any): any {
|
||||
* Delete a property and trigger change if necessary.
|
||||
*/
|
||||
export function del (target: Array<any> | Object, key: any) {
|
||||
if (Array.isArray(target) && typeof key === 'number') {
|
||||
if (Array.isArray(target) && isValidArrayIndex(key)) {
|
||||
target.splice(key, 1)
|
||||
return
|
||||
}
|
||||
const ob = (target : any).__ob__
|
||||
const ob = (target: any).__ob__
|
||||
if (target._isVue || (ob && ob.vmCount)) {
|
||||
process.env.NODE_ENV !== 'production' && warn(
|
||||
'Avoid deleting properties on a Vue instance or its root $data ' +
|
||||
|
||||
+2
-2
@@ -81,7 +81,7 @@ function flushSchedulerQueue () {
|
||||
|
||||
// call component updated and activated hooks
|
||||
callActivatedHooks(activatedQueue)
|
||||
callUpdateHooks(updatedQueue)
|
||||
callUpdatedHooks(updatedQueue)
|
||||
|
||||
// devtool hook
|
||||
/* istanbul ignore if */
|
||||
@@ -90,7 +90,7 @@ function flushSchedulerQueue () {
|
||||
}
|
||||
}
|
||||
|
||||
function callUpdateHooks (queue) {
|
||||
function callUpdatedHooks (queue) {
|
||||
let i = queue.length
|
||||
while (i--) {
|
||||
const watcher = queue[i]
|
||||
|
||||
+15
-14
@@ -94,22 +94,23 @@ export default class Watcher {
|
||||
pushTarget(this)
|
||||
let value
|
||||
const vm = this.vm
|
||||
if (this.user) {
|
||||
try {
|
||||
value = this.getter.call(vm, vm)
|
||||
} catch (e) {
|
||||
handleError(e, vm, `getter for watcher "${this.expression}"`)
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
value = this.getter.call(vm, vm)
|
||||
} catch (e) {
|
||||
if (this.user) {
|
||||
handleError(e, vm, `getter for watcher "${this.expression}"`)
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
} finally {
|
||||
// "touch" every property so they are all tracked as
|
||||
// dependencies for deep watching
|
||||
if (this.deep) {
|
||||
traverse(value)
|
||||
}
|
||||
popTarget()
|
||||
this.cleanupDeps()
|
||||
}
|
||||
// "touch" every property so they are all tracked as
|
||||
// dependencies for deep watching
|
||||
if (this.deep) {
|
||||
traverse(value)
|
||||
}
|
||||
popTarget()
|
||||
this.cleanupDeps()
|
||||
return value
|
||||
}
|
||||
|
||||
|
||||
+6
-4
@@ -15,10 +15,12 @@ if (process.env.NODE_ENV !== 'production') {
|
||||
.replace(/[-_]/g, '')
|
||||
|
||||
warn = (msg, vm) => {
|
||||
if (hasConsole && (!config.silent)) {
|
||||
console.error(`[Vue warn]: ${msg}` + (
|
||||
vm ? generateComponentTrace(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}`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-3
@@ -17,6 +17,9 @@ export const isAndroid = UA && UA.indexOf('android') > 0
|
||||
export const isIOS = UA && /iphone|ipad|ipod|ios/.test(UA)
|
||||
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 {
|
||||
@@ -26,7 +29,7 @@ if (inBrowser) {
|
||||
/* istanbul ignore next */
|
||||
supportsPassive = true
|
||||
}
|
||||
} : Object)) // https://github.com/facebook/flow/issues/285
|
||||
}: Object)) // https://github.com/facebook/flow/issues/285
|
||||
window.addEventListener('test-passive', null, opts)
|
||||
} catch (e) {}
|
||||
}
|
||||
@@ -96,13 +99,13 @@ export const nextTick = (function () {
|
||||
// "force" the microtask queue to be flushed by adding an empty timer.
|
||||
if (isIOS) setTimeout(noop)
|
||||
}
|
||||
} else if (typeof MutationObserver !== 'undefined' && (
|
||||
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
|
||||
isNative(MutationObserver) ||
|
||||
// PhantomJS and iOS 7.x
|
||||
MutationObserver.toString() === '[object MutationObserverConstructor]'
|
||||
)) {
|
||||
// use MutationObserver where native Promise is not available,
|
||||
// e.g. PhantomJS IE11, iOS7, Android 4.4
|
||||
// e.g. PhantomJS, iOS7, Android 4.4
|
||||
var counter = 1
|
||||
var observer = new MutationObserver(nextTickHandler)
|
||||
var textNode = document.createTextNode(String(counter))
|
||||
|
||||
+48
-16
@@ -2,6 +2,7 @@
|
||||
|
||||
import config from '../config'
|
||||
import { warn } from './debug'
|
||||
import { nativeWatch } from './env'
|
||||
import { set } from '../observer/index'
|
||||
|
||||
import {
|
||||
@@ -63,7 +64,7 @@ function mergeData (to: Object, from: ?Object): Object {
|
||||
/**
|
||||
* Data
|
||||
*/
|
||||
strats.data = function (
|
||||
export function mergeDataOrFn (
|
||||
parentVal: any,
|
||||
childVal: any,
|
||||
vm?: Component
|
||||
@@ -73,15 +74,6 @@ strats.data = function (
|
||||
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
|
||||
}
|
||||
@@ -92,8 +84,8 @@ strats.data = function (
|
||||
// it has to be a function to pass previous merges.
|
||||
return function mergedDataFn () {
|
||||
return mergeData(
|
||||
childVal.call(this),
|
||||
parentVal.call(this)
|
||||
typeof childVal === 'function' ? childVal.call(this) : childVal,
|
||||
typeof parentVal === 'function' ? parentVal.call(this) : parentVal
|
||||
)
|
||||
}
|
||||
} else if (parentVal || childVal) {
|
||||
@@ -104,7 +96,7 @@ strats.data = function (
|
||||
: childVal
|
||||
const defaultData = typeof parentVal === 'function'
|
||||
? parentVal.call(vm)
|
||||
: undefined
|
||||
: parentVal
|
||||
if (instanceData) {
|
||||
return mergeData(instanceData, defaultData)
|
||||
} else {
|
||||
@@ -114,6 +106,28 @@ strats.data = function (
|
||||
}
|
||||
}
|
||||
|
||||
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.call(this, parentVal, childVal)
|
||||
}
|
||||
|
||||
return mergeDataOrFn(parentVal, childVal, vm)
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks and props are merged as arrays.
|
||||
*/
|
||||
@@ -159,6 +173,9 @@ ASSET_TYPES.forEach(function (type) {
|
||||
* another, so we merge them as arrays.
|
||||
*/
|
||||
strats.watch = function (parentVal: ?Object, childVal: ?Object): ?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 (!parentVal) return childVal
|
||||
@@ -172,7 +189,7 @@ strats.watch = function (parentVal: ?Object, childVal: ?Object): ?Object {
|
||||
}
|
||||
ret[key] = parent
|
||||
? parent.concat(child)
|
||||
: [child]
|
||||
: Array.isArray(child) ? child : [child]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
@@ -182,14 +199,15 @@ strats.watch = function (parentVal: ?Object, childVal: ?Object): ?Object {
|
||||
*/
|
||||
strats.props =
|
||||
strats.methods =
|
||||
strats.inject =
|
||||
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)
|
||||
if (childVal) extend(ret, childVal)
|
||||
return ret
|
||||
}
|
||||
strats.provide = mergeDataOrFn
|
||||
|
||||
/**
|
||||
* Default strategy.
|
||||
@@ -247,6 +265,19 @@ function normalizeProps (options: Object) {
|
||||
options.props = res
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize all injections into Object-based format
|
||||
*/
|
||||
function normalizeInject (options: Object) {
|
||||
const inject = options.inject
|
||||
if (Array.isArray(inject)) {
|
||||
const normalized = options.inject = {}
|
||||
for (let i = 0; i < inject.length; i++) {
|
||||
normalized[inject[i]] = inject[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize raw function directives into object format.
|
||||
*/
|
||||
@@ -280,6 +311,7 @@ export function mergeOptions (
|
||||
}
|
||||
|
||||
normalizeProps(child)
|
||||
normalizeInject(child)
|
||||
normalizeDirectives(child)
|
||||
const extendsFrom = child.extends
|
||||
if (extendsFrom) {
|
||||
|
||||
+14
-3
@@ -1,8 +1,14 @@
|
||||
/* @flow */
|
||||
|
||||
import { hasOwn, isObject, isPlainObject, capitalize, hyphenate } from 'shared/util'
|
||||
import { observe, observerState } from '../observer/index'
|
||||
import { warn } from './debug'
|
||||
import { observe, observerState } from '../observer/index'
|
||||
import {
|
||||
hasOwn,
|
||||
isObject,
|
||||
hyphenate,
|
||||
capitalize,
|
||||
isPlainObject
|
||||
} from 'shared/util'
|
||||
|
||||
type PropOptions = {
|
||||
type: Function | Array<Function> | null,
|
||||
@@ -139,7 +145,12 @@ function assertType (value: any, type: Function): {
|
||||
let valid
|
||||
const expectedType = getType(type)
|
||||
if (simpleCheckRE.test(expectedType)) {
|
||||
valid = typeof value === expectedType.toLowerCase()
|
||||
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') {
|
||||
|
||||
+27
-9
@@ -15,6 +15,7 @@ import {
|
||||
|
||||
import {
|
||||
resolveAsyncComponent,
|
||||
createAsyncPlaceholder,
|
||||
extractPropsFromVNodeData
|
||||
} from './helpers/index'
|
||||
|
||||
@@ -97,7 +98,7 @@ const hooksToMerge = Object.keys(componentVNodeHooks)
|
||||
|
||||
export function createComponent (
|
||||
Ctor: Class<Component> | Function | Object | void,
|
||||
data?: VNodeData,
|
||||
data: ?VNodeData,
|
||||
context: Component,
|
||||
children: ?Array<VNode>,
|
||||
tag?: string
|
||||
@@ -123,21 +124,30 @@ export function createComponent (
|
||||
}
|
||||
|
||||
// async component
|
||||
let asyncFactory
|
||||
if (isUndef(Ctor.cid)) {
|
||||
Ctor = resolveAsyncComponent(Ctor, baseCtor, context)
|
||||
asyncFactory = Ctor
|
||||
Ctor = resolveAsyncComponent(asyncFactory, baseCtor, context)
|
||||
if (Ctor === undefined) {
|
||||
// return nothing if this is indeed an async component
|
||||
// wait for the callback to trigger parent update.
|
||||
return
|
||||
// return a placeholder node for async component, which is rendered
|
||||
// as a comment node but preserves all the raw information for the node.
|
||||
// the information will be used for async server-rendering and hydration.
|
||||
return createAsyncPlaceholder(
|
||||
asyncFactory,
|
||||
data,
|
||||
context,
|
||||
children,
|
||||
tag
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data = data || {}
|
||||
|
||||
// resolve constructor options in case global mixins are applied after
|
||||
// component constructor creation
|
||||
resolveConstructorOptions(Ctor)
|
||||
|
||||
data = data || {}
|
||||
|
||||
// transform component v-model data into props & events
|
||||
if (isDef(data.model)) {
|
||||
transformModel(Ctor.options, data)
|
||||
@@ -155,12 +165,19 @@ export function createComponent (
|
||||
// child component listeners instead of DOM listeners
|
||||
const listeners = data.on
|
||||
// replace with listeners with .native modifier
|
||||
// so it gets processed during parent component patch.
|
||||
data.on = data.nativeOn
|
||||
|
||||
if (isTrue(Ctor.options.abstract)) {
|
||||
// abstract components do not keep anything
|
||||
// other than props & listeners
|
||||
// other than props & listeners & slot
|
||||
|
||||
// work around flow
|
||||
const slot = data.slot
|
||||
data = {}
|
||||
if (slot) {
|
||||
data.slot = slot
|
||||
}
|
||||
}
|
||||
|
||||
// merge component management hooks onto the placeholder node
|
||||
@@ -171,7 +188,8 @@ export function createComponent (
|
||||
const vnode = new VNode(
|
||||
`vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
|
||||
data, undefined, undefined, undefined, context,
|
||||
{ Ctor, propsData, listeners, tag, children }
|
||||
{ Ctor, propsData, listeners, tag, children },
|
||||
asyncFactory
|
||||
)
|
||||
return vnode
|
||||
}
|
||||
|
||||
+15
-1
@@ -57,10 +57,24 @@ export function _createElement (
|
||||
)
|
||||
return createEmptyVNode()
|
||||
}
|
||||
// object syntax in v-bind
|
||||
if (isDef(data) && isDef(data.is)) {
|
||||
tag = data.is
|
||||
}
|
||||
if (!tag) {
|
||||
// in case of component :is set to falsy value
|
||||
return createEmptyVNode()
|
||||
}
|
||||
// warn against non-primitive key
|
||||
if (process.env.NODE_ENV !== 'production' &&
|
||||
isDef(data) && isDef(data.key) && !isPrimitive(data.key)
|
||||
) {
|
||||
warn(
|
||||
'Avoid using non-primitive value as key, ' +
|
||||
'use string/number value instead.',
|
||||
context
|
||||
)
|
||||
}
|
||||
// support single function children as default scoped slot
|
||||
if (Array.isArray(children) &&
|
||||
typeof children[0] === 'function'
|
||||
@@ -77,7 +91,7 @@ export function _createElement (
|
||||
let vnode, ns
|
||||
if (typeof tag === 'string') {
|
||||
let Ctor
|
||||
ns = config.getTagNamespace(tag)
|
||||
ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
|
||||
if (config.isReservedTag(tag)) {
|
||||
// platform built-in elements
|
||||
vnode = new VNode(
|
||||
|
||||
+3
-2
@@ -8,6 +8,7 @@ import { resolveSlots } from '../instance/render-helpers/resolve-slots'
|
||||
import {
|
||||
isDef,
|
||||
camelize,
|
||||
emptyObject,
|
||||
validateProp
|
||||
} from '../util/index'
|
||||
|
||||
@@ -22,7 +23,7 @@ export function createFunctionalComponent (
|
||||
const propOptions = Ctor.options.props
|
||||
if (isDef(propOptions)) {
|
||||
for (const key in propOptions) {
|
||||
props[key] = validateProp(key, propOptions, propsData || {})
|
||||
props[key] = validateProp(key, propOptions, propsData || emptyObject)
|
||||
}
|
||||
} else {
|
||||
if (isDef(data.attrs)) mergeProps(props, data.attrs)
|
||||
@@ -37,7 +38,7 @@ export function createFunctionalComponent (
|
||||
props,
|
||||
children,
|
||||
parent: context,
|
||||
listeners: data.on || {},
|
||||
listeners: data.on || emptyObject,
|
||||
injections: resolveInject(Ctor.options.inject, context),
|
||||
slots: () => resolveSlots(children, context)
|
||||
})
|
||||
|
||||
+2
-1
@@ -1,12 +1,13 @@
|
||||
/* @flow */
|
||||
|
||||
import { isDef } from 'shared/util'
|
||||
import { isAsyncPlaceholder } from './is-async-placeholder'
|
||||
|
||||
export function getFirstComponentChild (children: ?Array<VNode>): ?VNode {
|
||||
if (Array.isArray(children)) {
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const c = children[i]
|
||||
if (isDef(c) && isDef(c.componentOptions)) {
|
||||
if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) {
|
||||
return c
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -6,3 +6,4 @@ export * from './update-listeners'
|
||||
export * from './normalize-children'
|
||||
export * from './resolve-async-component'
|
||||
export * from './get-first-component-child'
|
||||
export * from './is-async-placeholder'
|
||||
|
||||
+18
@@ -9,12 +9,30 @@ import {
|
||||
isObject
|
||||
} from 'core/util/index'
|
||||
|
||||
import { createEmptyVNode } from 'core/vdom/vnode'
|
||||
|
||||
function ensureCtor (comp, base) {
|
||||
if (comp.__esModule && comp.default) {
|
||||
comp = comp.default
|
||||
}
|
||||
return isObject(comp)
|
||||
? base.extend(comp)
|
||||
: comp
|
||||
}
|
||||
|
||||
export function createAsyncPlaceholder (
|
||||
factory: Function,
|
||||
data: ?VNodeData,
|
||||
context: Component,
|
||||
children: ?Array<VNode>,
|
||||
tag: ?string
|
||||
): VNode {
|
||||
const node = createEmptyVNode()
|
||||
node.asyncFactory = factory
|
||||
node.asyncMeta = { data, context, children, tag }
|
||||
return node
|
||||
}
|
||||
|
||||
export function resolveAsyncComponent (
|
||||
factory: Function,
|
||||
baseCtor: Class<Component>,
|
||||
|
||||
+25
-4
@@ -5,9 +5,11 @@ import { cached, isUndef } from 'shared/util'
|
||||
|
||||
const normalizeEvent = cached((name: string): {
|
||||
name: string,
|
||||
plain: boolean,
|
||||
once: boolean,
|
||||
capture: boolean,
|
||||
passive: boolean
|
||||
passive: boolean,
|
||||
handler?: Function
|
||||
} => {
|
||||
const passive = name.charAt(0) === '&'
|
||||
name = passive ? name.slice(1) : name
|
||||
@@ -15,8 +17,10 @@ const normalizeEvent = cached((name: string): {
|
||||
name = once ? name.slice(1) : name
|
||||
const capture = name.charAt(0) === '!'
|
||||
name = capture ? name.slice(1) : name
|
||||
const plain = !(passive || once || capture)
|
||||
return {
|
||||
name,
|
||||
plain,
|
||||
once,
|
||||
capture,
|
||||
passive
|
||||
@@ -27,8 +31,9 @@ export function createFnInvoker (fns: Function | Array<Function>): Function {
|
||||
function invoker () {
|
||||
const fns = invoker.fns
|
||||
if (Array.isArray(fns)) {
|
||||
for (let i = 0; i < fns.length; i++) {
|
||||
fns[i].apply(null, arguments)
|
||||
const cloned = fns.slice()
|
||||
for (let i = 0; i < cloned.length; i++) {
|
||||
cloned[i].apply(null, arguments)
|
||||
}
|
||||
} else {
|
||||
// return handler return value for single handlers
|
||||
@@ -39,6 +44,11 @@ export function createFnInvoker (fns: Function | Array<Function>): Function {
|
||||
return invoker
|
||||
}
|
||||
|
||||
// #6552
|
||||
function prioritizePlainEvents (a, b) {
|
||||
return a.plain ? -1 : b.plain ? 1 : 0
|
||||
}
|
||||
|
||||
export function updateListeners (
|
||||
on: Object,
|
||||
oldOn: Object,
|
||||
@@ -47,10 +57,13 @@ export function updateListeners (
|
||||
vm: Component
|
||||
) {
|
||||
let name, cur, old, event
|
||||
const toAdd = []
|
||||
let hasModifier = false
|
||||
for (name in on) {
|
||||
cur = on[name]
|
||||
old = oldOn[name]
|
||||
event = normalizeEvent(name)
|
||||
if (!event.plain) hasModifier = true
|
||||
if (isUndef(cur)) {
|
||||
process.env.NODE_ENV !== 'production' && warn(
|
||||
`Invalid handler for event "${event.name}": got ` + String(cur),
|
||||
@@ -60,12 +73,20 @@ export function updateListeners (
|
||||
if (isUndef(cur.fns)) {
|
||||
cur = on[name] = createFnInvoker(cur)
|
||||
}
|
||||
add(event.name, cur, event.once, event.capture, event.passive)
|
||||
event.handler = cur
|
||||
toAdd.push(event)
|
||||
} else if (cur !== old) {
|
||||
old.fns = cur
|
||||
on[name] = old
|
||||
}
|
||||
}
|
||||
if (toAdd.length) {
|
||||
if (hasModifier) toAdd.sort(prioritizePlainEvents)
|
||||
for (let i = 0; i < toAdd.length; i++) {
|
||||
const event = toAdd[i]
|
||||
add(event.name, event.handler, event.once, event.capture, event.passive)
|
||||
}
|
||||
}
|
||||
for (name in oldOn) {
|
||||
if (isUndef(on[name])) {
|
||||
event = normalizeEvent(name)
|
||||
|
||||
+4
-3
@@ -32,10 +32,11 @@ export function registerRef (vnode: VNodeWithData, isRemoval: ?boolean) {
|
||||
}
|
||||
} else {
|
||||
if (vnode.data.refInFor) {
|
||||
if (Array.isArray(refs[key]) && refs[key].indexOf(ref) < 0) {
|
||||
refs[key].push(ref)
|
||||
} else {
|
||||
if (!Array.isArray(refs[key])) {
|
||||
refs[key] = [ref]
|
||||
} else if (refs[key].indexOf(ref) < 0) {
|
||||
// $flow-disable-line
|
||||
refs[key].push(ref)
|
||||
}
|
||||
} else {
|
||||
refs[key] = ref
|
||||
|
||||
+103
-42
@@ -6,8 +6,6 @@
|
||||
*
|
||||
* modified by Evan You (@yyx990803)
|
||||
*
|
||||
|
||||
/*
|
||||
* Not type-checking this because this file is perf-critical and the cost
|
||||
* of making flow understand it is not worth it.
|
||||
*/
|
||||
@@ -17,6 +15,7 @@ import config from '../config'
|
||||
import { SSR_ATTR } from 'shared/constants'
|
||||
import { registerRef } from './modules/ref'
|
||||
import { activeInstance } from '../instance/lifecycle'
|
||||
import { isTextInputType } from 'web/util/element'
|
||||
|
||||
import {
|
||||
warn,
|
||||
@@ -33,22 +32,27 @@ const hooks = ['create', 'activate', 'update', 'remove', 'destroy']
|
||||
|
||||
function sameVnode (a, b) {
|
||||
return (
|
||||
a.key === b.key &&
|
||||
a.tag === b.tag &&
|
||||
a.isComment === b.isComment &&
|
||||
isDef(a.data) === isDef(b.data) &&
|
||||
sameInputType(a, b)
|
||||
a.key === b.key && (
|
||||
(
|
||||
a.tag === b.tag &&
|
||||
a.isComment === b.isComment &&
|
||||
isDef(a.data) === isDef(b.data) &&
|
||||
sameInputType(a, b)
|
||||
) || (
|
||||
isTrue(a.isAsyncPlaceholder) &&
|
||||
a.asyncFactory === b.asyncFactory &&
|
||||
isUndef(b.asyncFactory.error)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Some browsers do not support dynamically changing type for <input>
|
||||
// so they need to be treated as different nodes
|
||||
function sameInputType (a, b) {
|
||||
if (a.tag !== 'input') return true
|
||||
let i
|
||||
const typeA = isDef(i = a.data) && isDef(i = i.attrs) && i.type
|
||||
const typeB = isDef(i = b.data) && isDef(i = i.attrs) && i.type
|
||||
return typeA === typeB
|
||||
return typeA === typeB || isTextInputType(typeA) && isTextInputType(typeB)
|
||||
}
|
||||
|
||||
function createKeyToOldIdx (children, beginIdx, endIdx) {
|
||||
@@ -397,10 +401,11 @@ export function createPatchFunction (backend) {
|
||||
newStartVnode = newCh[++newStartIdx]
|
||||
} else {
|
||||
if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
|
||||
idxInOld = isDef(newStartVnode.key) ? oldKeyToIdx[newStartVnode.key] : null
|
||||
idxInOld = isDef(newStartVnode.key)
|
||||
? oldKeyToIdx[newStartVnode.key]
|
||||
: findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
|
||||
if (isUndef(idxInOld)) { // New element
|
||||
createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm)
|
||||
newStartVnode = newCh[++newStartIdx]
|
||||
} else {
|
||||
elmToMove = oldCh[idxInOld]
|
||||
/* istanbul ignore if */
|
||||
@@ -413,14 +418,13 @@ export function createPatchFunction (backend) {
|
||||
if (sameVnode(elmToMove, newStartVnode)) {
|
||||
patchVnode(elmToMove, newStartVnode, insertedVnodeQueue)
|
||||
oldCh[idxInOld] = undefined
|
||||
canMove && nodeOps.insertBefore(parentElm, newStartVnode.elm, oldStartVnode.elm)
|
||||
newStartVnode = newCh[++newStartIdx]
|
||||
canMove && nodeOps.insertBefore(parentElm, elmToMove.elm, oldStartVnode.elm)
|
||||
} else {
|
||||
// same key but different element. treat as new element
|
||||
createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm)
|
||||
newStartVnode = newCh[++newStartIdx]
|
||||
}
|
||||
}
|
||||
newStartVnode = newCh[++newStartIdx]
|
||||
}
|
||||
}
|
||||
if (oldStartIdx > oldEndIdx) {
|
||||
@@ -431,10 +435,29 @@ export function createPatchFunction (backend) {
|
||||
}
|
||||
}
|
||||
|
||||
function findIdxInOld (node, oldCh, start, end) {
|
||||
for (let i = start; i < end; i++) {
|
||||
const c = oldCh[i]
|
||||
if (isDef(c) && sameVnode(node, c)) return i
|
||||
}
|
||||
}
|
||||
|
||||
function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) {
|
||||
if (oldVnode === vnode) {
|
||||
return
|
||||
}
|
||||
|
||||
const elm = vnode.elm = oldVnode.elm
|
||||
|
||||
if (isTrue(oldVnode.isAsyncPlaceholder)) {
|
||||
if (isDef(vnode.asyncFactory.resolved)) {
|
||||
hydrate(oldVnode.elm, vnode, insertedVnodeQueue)
|
||||
} else {
|
||||
vnode.isAsyncPlaceholder = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// reuse element for static trees.
|
||||
// note we only do this if the vnode is cloned -
|
||||
// if the new node is not cloned it means the render functions have been
|
||||
@@ -444,16 +467,16 @@ export function createPatchFunction (backend) {
|
||||
vnode.key === oldVnode.key &&
|
||||
(isTrue(vnode.isCloned) || isTrue(vnode.isOnce))
|
||||
) {
|
||||
vnode.elm = oldVnode.elm
|
||||
vnode.componentInstance = oldVnode.componentInstance
|
||||
return
|
||||
}
|
||||
|
||||
let i
|
||||
const data = vnode.data
|
||||
if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {
|
||||
i(oldVnode, vnode)
|
||||
}
|
||||
const elm = vnode.elm = oldVnode.elm
|
||||
|
||||
const oldCh = oldVnode.children
|
||||
const ch = vnode.children
|
||||
if (isDef(data) && isPatchable(vnode)) {
|
||||
@@ -498,6 +521,11 @@ export function createPatchFunction (backend) {
|
||||
|
||||
// Note: this is a browser-only function so we can assume elms are DOM nodes.
|
||||
function hydrate (elm, vnode, insertedVnodeQueue) {
|
||||
if (isTrue(vnode.isComment) && isDef(vnode.asyncFactory)) {
|
||||
vnode.elm = elm
|
||||
vnode.isAsyncPlaceholder = true
|
||||
return true
|
||||
}
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (!assertNodeMatch(elm, vnode)) {
|
||||
return false
|
||||
@@ -519,27 +547,46 @@ export function createPatchFunction (backend) {
|
||||
if (!elm.hasChildNodes()) {
|
||||
createChildren(vnode, children, insertedVnodeQueue)
|
||||
} else {
|
||||
let childrenMatch = true
|
||||
let childNode = elm.firstChild
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
if (!childNode || !hydrate(childNode, children[i], insertedVnodeQueue)) {
|
||||
childrenMatch = false
|
||||
break
|
||||
// v-html and domProps: innerHTML
|
||||
if (isDef(i = data) && isDef(i = i.domProps) && isDef(i = i.innerHTML)) {
|
||||
if (i !== elm.innerHTML) {
|
||||
/* istanbul ignore if */
|
||||
if (process.env.NODE_ENV !== 'production' &&
|
||||
typeof console !== 'undefined' &&
|
||||
!bailed
|
||||
) {
|
||||
bailed = true
|
||||
console.warn('Parent: ', elm)
|
||||
console.warn('server innerHTML: ', i)
|
||||
console.warn('client innerHTML: ', elm.innerHTML)
|
||||
}
|
||||
return false
|
||||
}
|
||||
childNode = childNode.nextSibling
|
||||
}
|
||||
// if childNode is not null, it means the actual childNodes list is
|
||||
// longer than the virtual children list.
|
||||
if (!childrenMatch || childNode) {
|
||||
if (process.env.NODE_ENV !== 'production' &&
|
||||
typeof console !== 'undefined' &&
|
||||
!bailed
|
||||
) {
|
||||
bailed = true
|
||||
console.warn('Parent: ', elm)
|
||||
console.warn('Mismatching childNodes vs. VNodes: ', elm.childNodes, children)
|
||||
} else {
|
||||
// iterate and compare children lists
|
||||
let childrenMatch = true
|
||||
let childNode = elm.firstChild
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
if (!childNode || !hydrate(childNode, children[i], insertedVnodeQueue)) {
|
||||
childrenMatch = false
|
||||
break
|
||||
}
|
||||
childNode = childNode.nextSibling
|
||||
}
|
||||
// if childNode is not null, it means the actual childNodes list is
|
||||
// longer than the virtual children list.
|
||||
if (!childrenMatch || childNode) {
|
||||
/* istanbul ignore if */
|
||||
if (process.env.NODE_ENV !== 'production' &&
|
||||
typeof console !== 'undefined' &&
|
||||
!bailed
|
||||
) {
|
||||
bailed = true
|
||||
console.warn('Parent: ', elm)
|
||||
console.warn('Mismatching childNodes vs. VNodes: ', elm.childNodes, children)
|
||||
}
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -630,14 +677,28 @@ export function createPatchFunction (backend) {
|
||||
// component root element replaced.
|
||||
// update parent placeholder node element, recursively
|
||||
let ancestor = vnode.parent
|
||||
const patchable = isPatchable(vnode)
|
||||
while (ancestor) {
|
||||
ancestor.elm = vnode.elm
|
||||
ancestor = ancestor.parent
|
||||
}
|
||||
if (isPatchable(vnode)) {
|
||||
for (let i = 0; i < cbs.create.length; ++i) {
|
||||
cbs.create[i](emptyNode, vnode.parent)
|
||||
for (let i = 0; i < cbs.destroy.length; ++i) {
|
||||
cbs.destroy[i](ancestor)
|
||||
}
|
||||
ancestor.elm = vnode.elm
|
||||
if (patchable) {
|
||||
for (let i = 0; i < cbs.create.length; ++i) {
|
||||
cbs.create[i](emptyNode, ancestor)
|
||||
}
|
||||
// #6513
|
||||
// invoke insert hooks that may have been merged by create hooks.
|
||||
// e.g. for directives that uses the "inserted" hook.
|
||||
const insert = ancestor.data.hook.insert
|
||||
if (insert.merged) {
|
||||
// start at index 1 to avoid re-invoking component mounted hook
|
||||
for (let i = 1; i < insert.fns.length; i++) {
|
||||
insert.fns[i]()
|
||||
}
|
||||
}
|
||||
}
|
||||
ancestor = ancestor.parent
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+19
-7
@@ -19,6 +19,10 @@ export default class VNode {
|
||||
isComment: boolean; // empty comment placeholder?
|
||||
isCloned: boolean; // is a cloned node?
|
||||
isOnce: boolean; // is a v-once node?
|
||||
asyncFactory: Function | void; // async component factory function
|
||||
asyncMeta: Object | void;
|
||||
isAsyncPlaceholder: boolean;
|
||||
ssrContext: Object | void;
|
||||
|
||||
constructor (
|
||||
tag?: string,
|
||||
@@ -27,7 +31,8 @@ export default class VNode {
|
||||
text?: string,
|
||||
elm?: Node,
|
||||
context?: Component,
|
||||
componentOptions?: VNodeComponentOptions
|
||||
componentOptions?: VNodeComponentOptions,
|
||||
asyncFactory?: Function
|
||||
) {
|
||||
this.tag = tag
|
||||
this.data = data
|
||||
@@ -47,6 +52,9 @@ export default class VNode {
|
||||
this.isComment = false
|
||||
this.isCloned = false
|
||||
this.isOnce = false
|
||||
this.asyncFactory = asyncFactory
|
||||
this.asyncMeta = undefined
|
||||
this.isAsyncPlaceholder = false
|
||||
}
|
||||
|
||||
// DEPRECATED: alias for componentInstance for backwards compat.
|
||||
@@ -56,9 +64,9 @@ export default class VNode {
|
||||
}
|
||||
}
|
||||
|
||||
export const createEmptyVNode = () => {
|
||||
export const createEmptyVNode = (text: string = '') => {
|
||||
const node = new VNode()
|
||||
node.text = ''
|
||||
node.text = text
|
||||
node.isComment = true
|
||||
return node
|
||||
}
|
||||
@@ -71,7 +79,7 @@ export function createTextVNode (val: string | number) {
|
||||
// used for static nodes and slot nodes because they may be reused across
|
||||
// multiple renders, cloning them avoids errors when DOM manipulations rely
|
||||
// on their elm reference.
|
||||
export function cloneVNode (vnode: VNode): VNode {
|
||||
export function cloneVNode (vnode: VNode, deep?: boolean): VNode {
|
||||
const cloned = new VNode(
|
||||
vnode.tag,
|
||||
vnode.data,
|
||||
@@ -79,21 +87,25 @@ export function cloneVNode (vnode: VNode): VNode {
|
||||
vnode.text,
|
||||
vnode.elm,
|
||||
vnode.context,
|
||||
vnode.componentOptions
|
||||
vnode.componentOptions,
|
||||
vnode.asyncFactory
|
||||
)
|
||||
cloned.ns = vnode.ns
|
||||
cloned.isStatic = vnode.isStatic
|
||||
cloned.key = vnode.key
|
||||
cloned.isComment = vnode.isComment
|
||||
cloned.isCloned = true
|
||||
if (deep && vnode.children) {
|
||||
cloned.children = cloneVNodes(vnode.children)
|
||||
}
|
||||
return cloned
|
||||
}
|
||||
|
||||
export function cloneVNodes (vnodes: Array<VNode>): Array<VNode> {
|
||||
export function cloneVNodes (vnodes: Array<VNode>, deep?: boolean): Array<VNode> {
|
||||
const len = vnodes.length
|
||||
const res = new Array(len)
|
||||
for (let i = 0; i < len; i++) {
|
||||
res[i] = cloneVNode(vnodes[i])
|
||||
res[i] = cloneVNode(vnodes[i], deep)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user