Evan You 9 years ago
parent
commit
87ffd0da9f
4 changed files with 26 additions and 18 deletions
  1. 1 0
      flow/component.js
  2. 3 1
      src/core/instance/lifecycle.js
  3. 18 13
      src/core/instance/state.js
  4. 4 4
      src/core/util/props.js

+ 1 - 0
flow/component.js

@@ -53,6 +53,7 @@ declare interface Component {
   _watcher: Watcher;
   _watchers: Array<Watcher>;
   _data: Object;
+  _props: Object;
   _events: Object;
   _inactive: boolean;
   _isMounted: boolean;

+ 3 - 1
src/core/instance/lifecycle.js

@@ -143,15 +143,17 @@ export function lifecycleMixin (Vue: Class<Component>) {
       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++) {
         const key = propKeys[i]
-        vm[key] = validateProp(key, vm.$options.props, propsData, vm)
+        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

+ 18 - 13
src/core/instance/state.js

@@ -37,17 +37,18 @@ export function initState (vm: Component) {
 
 const isReservedProp = { key: 1, ref: 1, slot: 1 }
 
-function initProps (vm: Component, props: Object) {
+function initProps (vm: Component, propsOptions: Object) {
   const propsData = vm.$options.propsData || {}
-  vm.$props = {}
+  const props = vm._props = {}
   // cache prop keys so that future props updates can iterate using Array
   // instead of dyanmic 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 props) {
+  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]) {
@@ -56,7 +57,7 @@ function initProps (vm: Component, props: Object) {
           vm
         )
       }
-      defineReactive(vm.$props, key, validateProp(key, props, propsData, vm), () => {
+      defineReactive(props, key, value, () => {
         if (vm.$parent && !observerState.isSettingProps) {
           warn(
             `Avoid mutating a prop directly since the value will be ` +
@@ -68,9 +69,9 @@ function initProps (vm: Component, props: Object) {
         }
       })
     } else {
-      defineReactive(vm.$props, key, validateProp(key, props, propsData, vm))
+      defineReactive(props, key, value)
     }
-    proxy(vm, '$props', key)
+    proxy(vm, props, key)
   }
   observerState.shouldConvert = true
 }
@@ -100,7 +101,7 @@ function initData (vm: Component) {
         vm
       )
     } else if (!isReserved(keys[i])) {
-      proxy(vm, '_data', keys[i])
+      proxy(vm, data, keys[i])
     }
   }
   // observe data
@@ -200,9 +201,9 @@ export function stateMixin (Vue: Class<Component>) {
   // when using Object.defineProperty, so we have to procedurally build up
   // the object here.
   const dataDef = {}
-  dataDef.get = function () {
-    return this._data
-  }
+  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(
@@ -211,8 +212,12 @@ export function stateMixin (Vue: Class<Component>) {
         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
@@ -235,15 +240,15 @@ export function stateMixin (Vue: Class<Component>) {
   }
 }
 
-function proxy (vm: Component, proxyName: '$props' | '_data', key: string) {
+function proxy (vm: Component, source: Object, key: string) {
   Object.defineProperty(vm, key, {
     configurable: true,
     enumerable: true,
     get: function proxyGetter () {
-      return vm[proxyName][key]
+      return source[key]
     },
     set: function proxySetter (val) {
-      vm[proxyName][key] = val
+      source[key] = val
     }
   })
 }

+ 4 - 4
src/core/util/props.js

@@ -54,8 +54,8 @@ function getPropDefaultValue (vm: ?Component, prop: PropOptions, key: string): a
   }
   const def = prop.default
   // warn against non-factory defaults for Object & Array
-  if (isObject(def)) {
-    process.env.NODE_ENV !== 'production' && warn(
+  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.',
@@ -66,8 +66,8 @@ function getPropDefaultValue (vm: ?Component, prop: PropOptions, key: string): a
   // return previous default value to avoid unnecessary watcher trigger
   if (vm && vm.$options.propsData &&
     vm.$options.propsData[key] === undefined &&
-    vm[key] !== undefined) {
-    return vm[key]
+    vm._props[key] !== undefined) {
+    return vm._props[key]
   }
   // call factory function for non-Function types
   return typeof def === 'function' && prop.type !== Function