vuejs / vue-class-component

ES / TypeScript decorator for class-style Vue components.
MIT License
5.81k stars 429 forks source link

[Bug]Serious problem when loading different classes that inherit the same parent class in vitejs #538

Open bitjjj opened 3 years ago

bitjjj commented 3 years ago

vue-class-component:^v8.0.0-rc.1 vue-property-decorator: ^v10.0.0-rc.3 vite: ^2.0.5 vue: ^3.0.5

The scenario is following: <--BaseDlg.ts-->

import { Options, Vue } from "vue-class-component";
import { Model, Watch } from "vue-property-decorator";

@Options({
  name: "BaseDlg",
})
export default class BaseDlg extends Vue {
  @Model("modelValue", { type: Boolean, default: false })
  visible!: boolean;

  @Watch("visible")
  visibleValueChanged(newVal: boolean): void {
    console.log("visible==?0", newVal);
  }
}

<--Test1.vue-->

<template>
  <div class="hello">
    <ul>{{ msg }}</ul>
  </div>
</template>

<script lang="ts">
import { Options } from "vue-class-component";
import { Prop, Watch } from "vue-property-decorator";
import BaseDlg from "BaseDlg";

@Options({
  name: "Test1",
})
export default class Test extends BaseDlg {
  @Prop(String) msg!: string;
  @Watch("msg")
  msgChanged(newVal: string): void {
    console.log("Test1-msg-changed", newVal);
  }

  beforeMount(): void {
    console.log("Test1==>", this.msg);
  }
}
</script>

<--Test2.vue-->

<template>
  <div class="hello">
    <ul>{{ msg }}</ul>
  </div>
</template>

<script lang="ts">
import { Options } from "vue-class-component";
import { Prop, Watch } from "vue-property-decorator";
import BaseDlg from "BaseDlg";

@Options({
  name: "Test2",
})
export default class Test extends BaseDlg {
  @Prop(String) msg!: string;
  @Watch("msg")
  msgChanged(newVal: string): void {
    console.log("Test2-msg-changed", newVal);
  }

  beforeMount(): void {
    console.log("Test2==>", this.msg);
  }
}
</script>

<--App.vue -->

<test1 v-if="showTest" :msg="msg2" />
<test2 v-if="showTest" :msg="msg2" />

================================ The issue is that decorators[@Prop / @Watch] in the last loaded [Test1.vue or Test2.vue] file will not take effect.

The reason is createDecorator function in helpers.ts line 37:

if (!Ctor.__d) { 
  Ctor.__d = [] 
}

When you use Ctor.__d for checking property if existed in object, 'in' operator will also check prototype chain.

But in vue.ts the following code use getOwn function to fetch __d property:

const decorators = getOwn(Ctor, '__d')

This will produce very strange behaviour in the scenario described above.

You should also use getOwn for checking __d property in Ctor.