aboutsummaryrefslogtreecommitdiff
path: root/node_modules/vue/src/core/instance/state.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/vue/src/core/instance/state.js')
-rw-r--r--node_modules/vue/src/core/instance/state.js360
1 files changed, 360 insertions, 0 deletions
diff --git a/node_modules/vue/src/core/instance/state.js b/node_modules/vue/src/core/instance/state.js
new file mode 100644
index 00000000..b1549b0d
--- /dev/null
+++ b/node_modules/vue/src/core/instance/state.js
@@ -0,0 +1,360 @@
+/* @flow */
+
+import config from '../config'
+import Watcher from '../observer/watcher'
+import Dep, { pushTarget, popTarget } from '../observer/dep'
+import { isUpdatingChildComponent } from './lifecycle'
+
+import {
+ set,
+ del,
+ observe,
+ defineReactive,
+ toggleObserving
+} from '../observer/index'
+
+import {
+ warn,
+ bind,
+ noop,
+ hasOwn,
+ hyphenate,
+ isReserved,
+ handleError,
+ nativeWatch,
+ validateProp,
+ isPlainObject,
+ isServerRendering,
+ isReservedAttribute
+} from '../util/index'
+
+const sharedPropertyDefinition = {
+ enumerable: true,
+ configurable: true,
+ get: noop,
+ set: noop
+}
+
+export function proxy (target: Object, sourceKey: string, key: string) {
+ sharedPropertyDefinition.get = function proxyGetter () {
+ return this[sourceKey][key]
+ }
+ sharedPropertyDefinition.set = function proxySetter (val) {
+ this[sourceKey][key] = val
+ }
+ Object.defineProperty(target, key, sharedPropertyDefinition)
+}
+
+export function initState (vm: Component) {
+ vm._watchers = []
+ const opts = vm.$options
+ if (opts.props) initProps(vm, opts.props)
+ if (opts.methods) initMethods(vm, opts.methods)
+ if (opts.data) {
+ initData(vm)
+ } else {
+ observe(vm._data = {}, true /* asRootData */)
+ }
+ if (opts.computed) initComputed(vm, opts.computed)
+ if (opts.watch && opts.watch !== nativeWatch) {
+ initWatch(vm, opts.watch)
+ }
+}
+
+function initProps (vm: Component, propsOptions: Object) {
+ const propsData = vm.$options.propsData || {}
+ const props = vm._props = {}
+ // cache prop keys so that future props updates can iterate using Array
+ // instead of dynamic object key enumeration.
+ const keys = vm.$options._propKeys = []
+ const isRoot = !vm.$parent
+ // root instance props should be converted
+ if (!isRoot) {
+ toggleObserving(false)
+ }
+ for (const key in propsOptions) {
+ keys.push(key)
+ const value = validateProp(key, propsOptions, propsData, vm)
+ /* istanbul ignore else */
+ if (process.env.NODE_ENV !== 'production') {
+ const hyphenatedKey = hyphenate(key)
+ if (isReservedAttribute(hyphenatedKey) ||
+ config.isReservedAttr(hyphenatedKey)) {
+ warn(
+ `"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`,
+ vm
+ )
+ }
+ defineReactive(props, key, value, () => {
+ if (vm.$parent && !isUpdatingChildComponent) {
+ warn(
+ `Avoid mutating a prop directly since the value will be ` +
+ `overwritten whenever the parent component re-renders. ` +
+ `Instead, use a data or computed property based on the prop's ` +
+ `value. Prop being mutated: "${key}"`,
+ vm
+ )
+ }
+ })
+ } else {
+ defineReactive(props, key, value)
+ }
+ // static props are already proxied on the component's prototype
+ // during Vue.extend(). We only need to proxy props defined at
+ // instantiation here.
+ if (!(key in vm)) {
+ proxy(vm, `_props`, key)
+ }
+ }
+ toggleObserving(true)
+}
+
+function initData (vm: Component) {
+ let data = vm.$options.data
+ data = vm._data = typeof data === 'function'
+ ? getData(data, vm)
+ : data || {}
+ if (!isPlainObject(data)) {
+ data = {}
+ process.env.NODE_ENV !== 'production' && warn(
+ 'data functions should return an object:\n' +
+ 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
+ vm
+ )
+ }
+ // 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--) {
+ 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 "${key}" is already declared as a prop. ` +
+ `Use prop default value instead.`,
+ vm
+ )
+ } else if (!isReserved(key)) {
+ proxy(vm, `_data`, key)
+ }
+ }
+ // observe data
+ observe(data, true /* asRootData */)
+}
+
+export function getData (data: Function, vm: Component): any {
+ // #7573 disable dep collection when invoking data getters
+ pushTarget()
+ try {
+ return data.call(vm, vm)
+ } catch (e) {
+ handleError(e, vm, `data()`)
+ return {}
+ } finally {
+ popTarget()
+ }
+}
+
+const computedWatcherOptions = { lazy: true }
+
+function initComputed (vm: Component, computed: Object) {
+ // $flow-disable-line
+ 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]
+ 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
+ )
+ }
+
+ // component-defined computed properties are already defined on the
+ // component prototype. We only need to define computed properties defined
+ // at instantiation here.
+ if (!(key in vm)) {
+ defineComputed(vm, key, userDef)
+ } else if (process.env.NODE_ENV !== 'production') {
+ if (key in vm.$data) {
+ warn(`The computed property "${key}" is already defined in data.`, vm)
+ } else if (vm.$options.props && key in vm.$options.props) {
+ warn(`The computed property "${key}" is already defined as a prop.`, vm)
+ }
+ }
+ }
+}
+
+export function defineComputed (
+ target: any,
+ key: string,
+ userDef: Object | Function
+) {
+ const shouldCache = !isServerRendering()
+ if (typeof userDef === 'function') {
+ sharedPropertyDefinition.get = shouldCache
+ ? createComputedGetter(key)
+ : userDef
+ sharedPropertyDefinition.set = noop
+ } else {
+ sharedPropertyDefinition.get = userDef.get
+ ? shouldCache && userDef.cache !== false
+ ? createComputedGetter(key)
+ : userDef.get
+ : noop
+ sharedPropertyDefinition.set = userDef.set
+ ? 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)
+}
+
+function createComputedGetter (key) {
+ return function computedGetter () {
+ const watcher = this._computedWatchers && this._computedWatchers[key]
+ if (watcher) {
+ if (watcher.dirty) {
+ watcher.evaluate()
+ }
+ if (Dep.target) {
+ watcher.depend()
+ }
+ return watcher.value
+ }
+ }
+}
+
+function initMethods (vm: Component, methods: Object) {
+ const props = vm.$options.props
+ for (const key in methods) {
+ if (process.env.NODE_ENV !== 'production') {
+ if (methods[key] == null) {
+ warn(
+ `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.`,
+ 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) {
+ for (const key in watch) {
+ const handler = watch[key]
+ if (Array.isArray(handler)) {
+ for (let i = 0; i < handler.length; i++) {
+ createWatcher(vm, key, handler[i])
+ }
+ } else {
+ createWatcher(vm, key, handler)
+ }
+ }
+}
+
+function createWatcher (
+ vm: Component,
+ expOrFn: string | Function,
+ handler: any,
+ options?: Object
+) {
+ if (isPlainObject(handler)) {
+ options = handler
+ handler = handler.handler
+ }
+ if (typeof handler === 'string') {
+ handler = vm[handler]
+ }
+ return vm.$watch(expOrFn, handler, options)
+}
+
+export function stateMixin (Vue: Class<Component>) {
+ // flow somehow has problems with directly declared definition object
+ // when using Object.defineProperty, so we have to procedurally build up
+ // the object here.
+ const dataDef = {}
+ dataDef.get = function () { return this._data }
+ const propsDef = {}
+ propsDef.get = function () { return this._props }
+ if (process.env.NODE_ENV !== 'production') {
+ dataDef.set = function (newData: Object) {
+ warn(
+ 'Avoid replacing instance root $data. ' +
+ 'Use nested data properties instead.',
+ this
+ )
+ }
+ propsDef.set = function () {
+ warn(`$props is readonly.`, this)
+ }
+ }
+ Object.defineProperty(Vue.prototype, '$data', dataDef)
+ Object.defineProperty(Vue.prototype, '$props', propsDef)
+
+ Vue.prototype.$set = set
+ Vue.prototype.$delete = del
+
+ Vue.prototype.$watch = function (
+ expOrFn: string | 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)
+ if (options.immediate) {
+ cb.call(vm, watcher.value)
+ }
+ return function unwatchFn () {
+ watcher.teardown()
+ }
+ }
+}