1
votes

there are two components in div,When the two components were rendered together, I clicked the button to switch properly, but in the case of rendering only one component, the switch becomes abnormal.

this is my code

Base.vue

<template>
  <div :id="id">{{msg}}</div>
</template>

<script lang='ts'>
import { Component, Prop } from "vue-property-decorator";
import Vue from "vue";

@Component
export default class Base extends Vue {
  id!: string;
  msg = "this is Base";
}
</script>

child.vue(no template)

<script lang='ts'>
import Base from "@/components/Base.vue";
import { Prop, Component } from "vue-property-decorator";
@Component
export default class extends Base {
  @Prop({ default: "helloWorld" })
  childId!: string;
  constructor() {
    super();
    this.id = this.childId;
    this.msg = "this is Child " + this.childId;
  }
}
</script>

App.vue(display these components)

<template>
  <div id="app">
    <Child v-show="!show" childId="child1" style="color:#f00;"/>
    <button @click="click">change</button>
    <Child v-show="show" childId="child2" style="color:#f0f;"/>
  </div>
</template>

<script lang="ts">
import Vue from "vue";
import Child from "@/components/Child.vue";
import Component from "vue-class-component";

@Component({
  components:{
    Child,
  }
})
export default class App extends Vue {
  show= false;
  click() {
    this.show = !this.show;
  }
}
</script>

begin

and click the button the result is

click button

These results are expected. But if all the v-show in the app. vue above are changed to v-if, the result is confusing

begin after change

then click the button the result is

after click

In our expectation it should display child2 here. So why does this happen?

3

3 Answers

2
votes

Your first click creates the the show-property which didn't exist because you didn't create your data() properly.

I'll not speculate exactly in the reasons why, but I assume there might be some funny boolean casts, and the property might not be reactive since it's not in data. Either way, just create it and it'll work as you intended:

export default class App extends Vue {
  data(){ 
     return {
       show: false
     }
  },
  click() {
    this.show = !this.show;
  }
}
0
votes

Thanks!!

I solved this problem when I added different keys to the two Child components

<Child v-if="!show" childId="child1" key="hello1" style="color:#f00;" />
<Child v-if="show" childId="child2" key="hello2" style="color:#f0f;" />

I think the reason is Vue's diff algorithm, Vue considers these two components to be the same component

0
votes

Because when you use v-if, it will use the single same Child component. The this.msg will only change once in the constructor. The msg will not change when the childId props changed, so you need the Watch. When the childId changed, then to update the msg

Child.vue

<script lang='ts'>
import Base from "@/components/Base.vue";
import { Prop, Component, Watch } from "vue-property-decorator";
@Component
export default class extends Base {
  @Prop({ default: "helloWorld" })
  childId!: string;
  @Watch('childId')
  onChildIdChanged(val: any) {this.msg = "this is Child " + val}
  constructor() {
    super();
    this.id = this.childId;
    this.msg = "this is Child " + this.childId;
  }
}
</script>