Просмотр исходного кода

fix(watch): fix watch option merging from mixins

fix #3966
Evan You 4 лет назад
Родитель
Сommit
9b607fe409

+ 61 - 0
packages/runtime-core/__tests__/apiOptions.spec.ts

@@ -279,6 +279,67 @@ describe('api: options', () => {
     assertCall(spyC, 0, [{ qux: 4 }, { qux: 4 }])
   })
 
+  // #3966
+  test('watch merging from mixins', async () => {
+    const mixinA = {
+      data() {
+        return {
+          fromMixinA: ''
+        }
+      },
+      watch: {
+        obj: {
+          handler(this: any, to: any) {
+            this.fromMixinA = to
+          }
+        }
+      }
+    }
+
+    const mixinB = {
+      data() {
+        return {
+          fromMixinB: ''
+        }
+      },
+      watch: {
+        obj: 'setMixinB'
+      },
+      methods: {
+        setMixinB(this: any, to: any) {
+          this.fromMixinB = to
+        }
+      }
+    }
+
+    let vm: any
+    const Comp = {
+      render() {},
+      mixins: [mixinA, mixinB],
+      data: () => ({
+        obj: 'foo',
+        fromComp: ''
+      }),
+      watch: {
+        obj(this: any, to: any) {
+          this.fromComp = to
+        }
+      },
+      mounted() {
+        vm = this
+      }
+    }
+
+    const root = nodeOps.createElement('div')
+    render(h(Comp), root)
+
+    vm.obj = 'bar'
+    await nextTick()
+    expect(vm.fromComp).toBe('bar')
+    expect(vm.fromMixinA).toBe('bar')
+    expect(vm.fromMixinB).toBe('bar')
+  })
+
   test('provide/inject', () => {
     const symbolKey = Symbol()
     const Root = defineComponent({

+ 28 - 20
packages/runtime-core/src/componentOptions.ts

@@ -972,25 +972,23 @@ export const internalOptionMergeStrats: Record<string, Function> = {
   methods: mergeObjectOptions,
   computed: mergeObjectOptions,
   // lifecycle
-  beforeCreate: mergeHook,
-  created: mergeHook,
-  beforeMount: mergeHook,
-  mounted: mergeHook,
-  beforeUpdate: mergeHook,
-  updated: mergeHook,
-  beforeDestroy: mergeHook,
-  destroyed: mergeHook,
-  activated: mergeHook,
-  deactivated: mergeHook,
-  errorCaptured: mergeHook,
-  serverPrefetch: mergeHook,
+  beforeCreate: mergeAsArray,
+  created: mergeAsArray,
+  beforeMount: mergeAsArray,
+  mounted: mergeAsArray,
+  beforeUpdate: mergeAsArray,
+  updated: mergeAsArray,
+  beforeDestroy: mergeAsArray,
+  destroyed: mergeAsArray,
+  activated: mergeAsArray,
+  deactivated: mergeAsArray,
+  errorCaptured: mergeAsArray,
+  serverPrefetch: mergeAsArray,
   // assets
   components: mergeObjectOptions,
   directives: mergeObjectOptions,
-  // watch has special merge behavior in v2, but isn't actually needed in v3.
-  // since we are only exposing these for compat and nobody should be relying
-  // on the watch-specific behavior, just expose the object merge strat.
-  watch: mergeObjectOptions,
+  // watch
+  watch: mergeWatchOptions,
   // provide / inject
   provide: mergeDataFn,
   inject: mergeInject
@@ -1038,13 +1036,23 @@ function normalizeInject(
   return raw
 }
 
-function mergeHook(
-  to: Function[] | Function | undefined,
-  from: Function | Function[]
-) {
+function mergeAsArray<T = Function>(to: T[] | T | undefined, from: T | T[]) {
   return to ? [...new Set([].concat(to as any, from as any))] : from
 }
 
 function mergeObjectOptions(to: Object | undefined, from: Object | undefined) {
   return to ? extend(extend(Object.create(null), to), from) : from
 }
+
+function mergeWatchOptions(
+  to: ComponentWatchOptions | undefined,
+  from: ComponentWatchOptions | undefined
+) {
+  if (!to) return from
+  if (!from) return to
+  const merged = extend(Object.create(null), to)
+  for (const key in from) {
+    merged[key] = mergeAsArray(to[key], from[key])
+  }
+  return merged
+}