2
0
Эх сурвалжийг харах

avoid triggering watcher on unchanged default props (fix #4090) (26 seconds ago)

Evan You 9 жил өмнө
parent
commit
c67a710b3c

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

@@ -132,6 +132,7 @@ export function lifecycleMixin (Vue: Class<Component>) {
       if (process.env.NODE_ENV !== 'production') {
         observerState.isSettingProps = false
       }
+      vm.$options.propsData = propsData
     }
     // update listeners
     if (listeners) {

+ 9 - 2
src/core/util/props.js

@@ -47,7 +47,7 @@ export function validateProp (
 /**
  * Get the default value of a prop.
  */
-function getPropDefaultValue (vm: ?Component, prop: PropOptions, name: string): any {
+function getPropDefaultValue (vm: ?Component, prop: PropOptions, key: string): any {
   // no default, return undefined
   if (!hasOwn(prop, 'default')) {
     return undefined
@@ -56,12 +56,19 @@ function getPropDefaultValue (vm: ?Component, prop: PropOptions, name: string):
   // warn against non-factory defaults for Object & Array
   if (isObject(def)) {
     process.env.NODE_ENV !== 'production' && warn(
-      'Invalid default value for prop "' + name + '": ' +
+      'Invalid default value for prop "' + key + '": ' +
       'Props with type Object/Array must use a factory function ' +
       'to return the default value.',
       vm
     )
   }
+  // the raw prop value was also undefined from previous render,
+  // 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]
+  }
   // call factory function for non-Function types
   return typeof def === 'function' && prop.type !== Function
     ? def.call(vm)

+ 42 - 0
test/unit/features/options/props.spec.js

@@ -399,4 +399,46 @@ describe('Options props', () => {
       expect(spy).not.toHaveBeenCalled()
     }).then(done)
   })
+
+  // #4090
+  it('should not trigger wathcer on default value', done => {
+    const spy = jasmine.createSpy()
+    const vm = new Vue({
+      template: `<test :value="a" :test="b"></test>`,
+      data: {
+        a: 1,
+        b: undefined
+      },
+      components: {
+        test: {
+          template: '<div>{{ value }}</div>',
+          props: {
+            value: { type: Number },
+            test: {
+              type: Object,
+              default: () => ({})
+            }
+          },
+          watch: {
+            test: spy
+          }
+        }
+      }
+    }).$mount()
+
+    vm.a++
+    waitForUpdate(() => {
+      expect(spy).not.toHaveBeenCalled()
+      vm.b = {}
+    }).then(() => {
+      expect(spy.calls.count()).toBe(1)
+    }).then(() => {
+      vm.b = undefined
+    }).then(() => {
+      expect(spy.calls.count()).toBe(2)
+      vm.a++
+    }).then(() => {
+      expect(spy.calls.count()).toBe(2)
+    }).then(done)
+  })
 })