• vue.js 源代码学习笔记 ----- instance state


    /* @flow */
    
    import Dep from '../observer/dep'
    import Watcher from '../observer/watcher'
    
    import {
      set,
      del,
      observe,
      observerState,
      defineReactive
    } from '../observer/index'
    
    import {
      warn,
      bind,
      noop,
      hasOwn,
      isReserved,
      handleError,
      validateProp,
      isPlainObject
    } 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) initWatch(vm, opts.watch)
    }
    
    const isReservedProp = { key: 1, ref: 1, slot: 1 }
    
    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
      observerState.shouldConvert = isRoot
      for (const key in propsOptions) {
        keys.push(key)
        const value = validateProp(key, propsOptions, propsData, vm)
        /* 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(props, key, value, () => {
            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(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)
        }
      }
      observerState.shouldConvert = 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:
    ' +
          '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 if (!isReserved(keys[i])) {
          proxy(vm, `_data`, keys[i])
        }
      }
      // observe data
      observe(data, true /* asRootData */)
    }
    
    function getData (data: Function, vm: Component): any {
      try {
        return data.call(vm)
      } catch (e) {
        handleError(e, vm, `data()`)
        return {}
      }
    }
    
    const computedWatcherOptions = { lazy: true }
    
    function initComputed (vm: Component, computed: Object) {
      const watchers = vm._computedWatchers = Object.create(null)
    
      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
          }
        }
        // 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
        // at instantiation here.
        if (!(key in vm)) {
          defineComputed(vm, key, userDef)
        }
      }
    }
    
    export function defineComputed (target: any, key: string, userDef: Object | Function) {
      if (typeof userDef === 'function') {
        sharedPropertyDefinition.get = createComputedGetter(key)
        sharedPropertyDefinition.set = noop
      } else {
        sharedPropertyDefinition.get = userDef.get
          ? userDef.cache !== false
            ? createComputedGetter(key)
            : userDef.get
          : noop
        sharedPropertyDefinition.set = userDef.set
          ? userDef.set
          : noop
      }
      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) {
        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. ` +
              `Did you reference the function correctly?`,
              vm
            )
          }
          if (props && hasOwn(props, key)) {
            warn(
              `method "${key}" has already been defined as a prop.`,
              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, 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 }
      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: 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()
        }
      }
    }
  • 相关阅读:
    Climbing Stairs
    交换排序
    解析Ceph: Snapshot
    OpenStack Hacker养成指南
    从UnitedStack OS 1.0 Preview试用申请问卷调查学习OpenStack
    Tun/Tap interface tutorial
    Linux下的TUN/TAP编程
    基于KVM的虚拟化研究及应用
    Linux 上的基础网络设备详解
    linux下TUN/TAP虚拟网卡的使用
  • 原文地址:https://www.cnblogs.com/dhsz/p/7116363.html
Copyright © 2020-2023  润新知