Przeglądaj źródła

props refactor

- No need to merge props into the data object. they should just be
  defined as reactive properties on the vm instance.

- They should not observe the value when set. They should already be reactive
  if needed.
Evan You 10 lat temu
rodzic
commit
d1788b98ac

+ 5 - 7
src/instance/internal/init.js

@@ -80,13 +80,6 @@ export default function (Vue) {
       this.$parent.$children.push(this)
     }
 
-    // save raw constructor data before merge
-    // so that we know which properties are provided at
-    // instantiation.
-    if (process.env.NODE_ENV !== 'production') {
-      this._runtimeData = options.data
-    }
-
     // merge options.
     options = this.$options = mergeOptions(
       this.constructor.options,
@@ -101,6 +94,11 @@ export default function (Vue) {
     // it will be filled up in _initScope().
     this._data = {}
 
+    // save raw constructor data before merge
+    // so that we know which properties are provided at
+    // instantiation.
+    this._runtimeData = options.data
+
     // call init hook
     this._callHook('init')
 

+ 22 - 29
src/instance/internal/state.js

@@ -10,7 +10,6 @@ import {
   warn,
   query,
   hasOwn,
-  set,
   isReserved,
   bind
 } from '../../util/index'
@@ -76,41 +75,35 @@ export default function (Vue) {
    */
 
   Vue.prototype._initData = function () {
-    var propsData = this._data
-    var optionsDataFn = this.$options.data
-    var optionsData = optionsDataFn && optionsDataFn()
-    var runtimeData
-    if (process.env.NODE_ENV !== 'production') {
-      runtimeData = (typeof this._runtimeData === 'function'
+    var dataFn = this.$options.data
+    var data = this._data = dataFn ? dataFn() : {}
+    var props = this._props
+    var runtimeData = this._runtimeData
+      ? typeof this._runtimeData === 'function'
         ? this._runtimeData()
-        : this._runtimeData) || {}
-      this._runtimeData = null
-    }
-    if (optionsData) {
-      this._data = optionsData
-      for (var prop in propsData) {
-        if (process.env.NODE_ENV !== 'production' &&
-            hasOwn(optionsData, prop) &&
-            !hasOwn(runtimeData, prop)) {
-          warn(
-            'Data field "' + prop + '" is already defined ' +
-            'as a prop. Use prop default value instead.'
-          )
-        }
-        if (this._props[prop].raw !== null ||
-            !hasOwn(optionsData, prop)) {
-          set(optionsData, prop, propsData[prop])
-        }
-      }
-    }
-    var data = this._data
+        : this._runtimeData
+      : null
     // proxy data on instance
     var keys = Object.keys(data)
     var i, key
     i = keys.length
     while (i--) {
       key = keys[i]
-      this._proxy(key)
+      // there are two scenarios where we can proxy a data key:
+      // 1. it's not already defined as a prop
+      // 2. it's provided via a instantiation option AND there are no
+      //    template prop present
+      if (
+        !props || !hasOwn(props, key) ||
+        (runtimeData && hasOwn(runtimeData, key) && props[key].raw === null)
+      ) {
+        this._proxy(key)
+      } else if (process.env.NODE_ENV !== 'production') {
+        warn(
+          'Data field "' + key + '" is already defined ' +
+          'as a prop. Use prop default value instead.'
+        )
+      }
     }
     // observe data
     observe(data, this)

+ 4 - 3
src/util/component.js

@@ -2,6 +2,7 @@ import { warn } from './debug'
 import { resolveAsset } from './options'
 import { getAttr, getBindAttr } from './dom'
 import { isArray, isPlainObject, isObject, hasOwn } from './lang'
+import { defineReactive } from '../observer/index'
 
 export const commonTagRE = /^(div|p|span|img|a|b|i|br|ul|ol|li|h1|h2|h3|h4|h5|h6|code|pre|table|th|td|tr|form|label|input|select|option|nav|article|section|header|footer)$/i
 export const reservedTagRE = /^(slot|partial|component)$/i
@@ -103,9 +104,9 @@ export function initProp (vm, prop, value) {
   if (value === undefined) {
     value = getPropDefaultValue(vm, prop.options)
   }
-  vm[key] = vm._data[key] = assertProp(prop, value)
-    ? value
-    : undefined
+  if (assertProp(prop, value)) {
+    defineReactive(vm, key, value, true /* doNotObserve */)
+  }
 }
 
 /**

+ 2 - 8
test/unit/specs/compiler/compile_spec.js

@@ -299,18 +299,12 @@ describe('Compile', function () {
     expect(vm._bindDir.calls.count()).toBe(4)
     // literal
     expect(vm.testLiteral).toBe('1')
-    expect(vm._data.testLiteral).toBe('1')
     expect(vm.testBoolean).toBe(true)
-    expect(vm._data.testBoolean).toBe(true)
     expect(vm.optimizeLiteral).toBe(1)
-    expect(vm._data.optimizeLiteral).toBe(1)
     expect(vm.optimizeLiteralStr).toBe('true')
-    expect(vm._data.optimizeLiteralStr).toBe('true')
     expect(vm.optimizeLiteralNegativeNumber).toBe(-1)
-    expect(vm._data.optimizeLiteralNegativeNumber).toBe(-1)
     // one time
     expect(vm.testOneTime).toBe('from parent: a')
-    expect(vm._data.testOneTime).toBe('from parent: a')
     // normal
     var args = vm._bindDir.calls.argsFor(0)
     var prop = args[0].prop
@@ -345,8 +339,8 @@ describe('Compile', function () {
     el.setAttribute(':b', '[1,2,3]')
     compiler.compileAndLinkProps(vm, el, { a: null, b: null })
     expect(vm._bindDir.calls.count()).toBe(0)
-    expect(vm._data.a).toBe('hi')
-    expect(vm._data.b.join(',')).toBe('1,2,3')
+    expect(vm.a).toBe('hi')
+    expect(vm.b.join(',')).toBe('1,2,3')
     // restore parent mock
     vm._context = context
   })

+ 0 - 25
test/unit/specs/directives/internal/prop_spec.js

@@ -491,31 +491,6 @@ describe('prop', function () {
     expect(el.textContent).toBe('AAA')
   })
 
-  it('should not overwrite default value for an absent Boolean prop', function () {
-    var vm = new Vue({
-      el: el,
-      template: '<test></test>',
-      components: {
-        test: {
-          props: {
-            prop: Boolean
-          },
-          data: function () {
-            return {
-              prop: true
-            }
-          },
-          template: '{{prop}}'
-        }
-      }
-    })
-    expect(vm.$children[0].prop).toBe(true)
-    expect(vm.$el.textContent).toBe('true')
-    expect(JSON.stringify(vm.$children[0].$data)).toBe(JSON.stringify({
-      prop: true
-    }))
-  })
-
   it('should respect default value of a Boolean prop', function () {
     var vm = new Vue({
       el: el,

+ 0 - 2
test/unit/specs/instance/state_spec.js

@@ -72,14 +72,12 @@ describe('Instance state initialization', function () {
         props: ['c'],
         data: function () {
           expect(this.c).toBe(2)
-          expect(this._data.c).toBe(2)
           return {
             d: this.c + 1
           }
         },
         created: function () {
           expect(this.c).toBe(2)
-          expect(this._data.c).toBe(2)
         }
       })
       expect(vm.d).toBe(3)