| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247 |
- /* @flow */
- import Watcher from '../observer/watcher'
- import Dep from '../observer/dep'
- import {
- set,
- del,
- observe,
- defineReactive,
- observerState
- } from '../observer/index'
- import {
- warn,
- hasOwn,
- isReserved,
- isPlainObject,
- bind,
- validateProp,
- noop
- } from '../util/index'
- export function initState (vm: Component) {
- vm._watchers = []
- initProps(vm)
- initMethods(vm)
- initData(vm)
- initComputed(vm)
- initWatch(vm)
- }
- const isReservedProp = { key: 1, ref: 1, slot: 1 }
- function initProps (vm: Component) {
- const props = vm.$options.props
- if (props) {
- const propsData = vm.$options.propsData || {}
- const keys = vm.$options._propKeys = Object.keys(props)
- const isRoot = !vm.$parent
- // root instance props should be converted
- observerState.shouldConvert = isRoot
- for (let i = 0; i < keys.length; i++) {
- const key = keys[i]
- /* istanbul ignore else */
- if (process.env.NODE_ENV !== 'production') {
- if (isReservedProp[key]) {
- warn(
- `"${key}" is a reserved attribute and cannot be used as component prop.`,
- vm
- )
- }
- defineReactive(vm, key, validateProp(key, props, propsData, vm), () => {
- if (vm.$parent && !observerState.isSettingProps) {
- 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(vm, key, validateProp(key, props, propsData, vm))
- }
- }
- observerState.shouldConvert = true
- }
- }
- function initData (vm: Component) {
- let data = vm.$options.data
- data = vm._data = typeof data === 'function'
- ? data.call(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
- let i = keys.length
- while (i--) {
- if (props && hasOwn(props, keys[i])) {
- process.env.NODE_ENV !== 'production' && warn(
- `The data property "${keys[i]}" is already declared as a prop. ` +
- `Use prop default value instead.`,
- vm
- )
- } else {
- proxy(vm, keys[i])
- }
- }
- // observe data
- observe(data)
- data.__ob__ && data.__ob__.vmCount++
- }
- const computedSharedDefinition = {
- enumerable: true,
- configurable: true,
- get: noop,
- set: noop
- }
- function initComputed (vm: Component) {
- const computed = vm.$options.computed
- if (computed) {
- for (const key in computed) {
- const userDef = computed[key]
- if (typeof userDef === 'function') {
- computedSharedDefinition.get = makeComputedGetter(userDef, vm)
- computedSharedDefinition.set = noop
- } else {
- computedSharedDefinition.get = userDef.get
- ? userDef.cache !== false
- ? makeComputedGetter(userDef.get, vm)
- : bind(userDef.get, vm)
- : noop
- computedSharedDefinition.set = userDef.set
- ? bind(userDef.set, vm)
- : noop
- }
- Object.defineProperty(vm, key, computedSharedDefinition)
- }
- }
- }
- function makeComputedGetter (getter: Function, owner: Component): Function {
- const watcher = new Watcher(owner, getter, noop, {
- lazy: true
- })
- return function computedGetter () {
- if (watcher.dirty) {
- watcher.evaluate()
- }
- if (Dep.target) {
- watcher.depend()
- }
- return watcher.value
- }
- }
- function initMethods (vm: Component) {
- const methods = vm.$options.methods
- if (methods) {
- for (const key in methods) {
- vm[key] = methods[key] == null ? noop : bind(methods[key], vm)
- if (process.env.NODE_ENV !== 'production' && methods[key] == null) {
- warn(
- `method "${key}" has an undefined value in the component definition. ` +
- `Did you reference the function correctly?`,
- vm
- )
- }
- }
- }
- }
- function initWatch (vm: Component) {
- const watch = vm.$options.watch
- if (watch) {
- 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, key: string, handler: any) {
- let options
- if (isPlainObject(handler)) {
- options = handler
- handler = handler.handler
- }
- if (typeof handler === 'string') {
- handler = vm[handler]
- }
- vm.$watch(key, 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
- }
- if (process.env.NODE_ENV !== 'production') {
- dataDef.set = function (newData: Object) {
- warn(
- 'Avoid replacing instance root $data. ' +
- 'Use nested data properties instead.',
- this
- )
- }
- }
- Object.defineProperty(Vue.prototype, '$data', dataDef)
- Vue.prototype.$set = set
- Vue.prototype.$delete = del
- Vue.prototype.$watch = function (
- expOrFn: string | Function,
- cb: Function,
- options?: Object
- ): Function {
- const vm: Component = this
- 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()
- }
- }
- }
- function proxy (vm: Component, key: string) {
- if (!isReserved(key)) {
- Object.defineProperty(vm, key, {
- configurable: true,
- enumerable: true,
- get: function proxyGetter () {
- return vm._data[key]
- },
- set: function proxySetter (val) {
- vm._data[key] = val
- }
- })
- }
- }
|