对于vuejs中自定义组件v-model的一些疑问

代码比较简单,直接如下

<template>
    <div id="app">
        <button @click="_add">父组件按钮点击</button>
        <ul>
            <li v-for="(item,index) in message" :key="index">
                {{item.name}}
            </li>
        </ul>
        <hr>
        <children v-model="message"></children>
    </div>
</template>
<script>
import children from './components/children'
export default {
    name: 'app',
    components: {
        children
    },
    data(){
        return{
            message: []
        }
    },
    methods: {
        _add(){
            this.message.push({
                name:'name1'
            })
        },
    },
}
</script>
<template>
    <div class="children">
        <button @click="_add">子组件按钮点击</button>
        <ul>
            <li v-for="(item,index) in message" :key="index">
                {{item.name}}
            </li>
        </ul>
    </div>
</template>
<script>
export default {
    name: "children",
    props: {
        value: {
            type: Array
        },
    },
    data() {
        return {
            message: []
        }
    },
    watch:{
        value(newVal){
            this.message = newVal
        }
    },
    methods: {
        _add(){
            this.message.push({
                name:'name2'
            })
        },
    },
};
</script>

点击父按钮会正常传递数据到子组件,如图:
image
只点击子组件因为没有 emit 传值显示也是正常,如图:
image
但是问题来了,如果先点击一次父按钮,再点击子组件里面的按钮,这时为什么会触发父组件数据更新?如图:
image
其中的 name1 是第一次先点击父组件的结果,name2 是第二次点击子组件按钮出现的结果。
在官网的介绍当中,说如果要更新父组件的数据,需要手动 emit 触发更新,目前遇到的这种情况不是很明白为什么会自动触发更新数据。

请前辈们解答下这个疑问~~~

阅读 2.2k
3 个回答
数据类型的问题,当前数据类型是数组,子组件监听赋值的时候相当于共享了内存地址,可以试试楼上说的深拷贝,但是如果是基于数组对象等复杂数据机构,需要特殊的深拷贝方法;
// 子组件修改
<template>
  <div class="children">
    <button @click="_add">子组件按钮点击</button>
    <ul>
      <li v-for="(item, index) in message" :key="index">
        {{ item.name || item }}
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  name: "children",
  props: {
    value: {
      type: Array
    }
  },
  data() {
    return {
      message: []
    };
  },
  watch: {
    value(newVal) {
      this.message = newVal;
    }
  },
  methods: {
    _add() {
      // this.message.push({
      //   name: "name2"
      // });
      this.message += "¥子¥";
    }
  }
};
</script>
// 父组件修改
<template>
 <button @click="_add">父组件按钮点击</button>
    <ul>
      <li v-for="(item, index) in message" :key="index">
        {{ item.name || item }}
      </li>
    </ul>
    <hr />
    <children v-model="message"></children>
</template>
<script>
export default class App extends Vue {
  // private message: { name: string }[] = [];
  private message = "";

  public _add() {
    this.message += "$父$";
    // this.message.push({
    //   name: "name1"
    // });
  }
}
</script>

如上改用非复杂数据类型不会出现题主说的问题

这是因为子组件的 message 不是由父组件直接传递进来的参数,而是自己声明的属性,只是在 watch.value 函数执行后才由父组件传入的 value改写,在父组件改写message前,二者并不相关。
如果你在子组件的 mounted 函数里写:

console.log(this.message === this.value);

理应为 false
但如果在 watch.value 的末尾写同样的代码,输出理应为 true

如果你给父子组件的初始 message 传不同的值的话,他们在首屏的显示必然是不一样的,这也能证明此时二者还不是同一个东西。

如果想使二者始终一致的话,子组件里的 message应为 prop

 props: {
        message: {
            type: Array,
            default: () => []
        },
    },

兄弟我试了下,如果 v-model 绑定的不是一个数组,而是一个布尔值,就不会出现你的这种情况,可能是数组比较特殊吧!或者这是个 bug,或者你对数组进行深拷贝试下

<template>
 <div class="children">
 <button @click="_add">子组件按钮点击</button>
 <ul> <li v-for="(item,index) in message" :key="index">
 {{item.name}}
            </li>
 </ul> </div></template>
<script>
 export default {
    name: "children",
 props: {
      value: {
        type: Array
      },
 },
 data() {
      return {
        message: []
      }
    },
 watch:{
      value(newVal){
        this.message = JSON.parse(JSON.stringify(newVal));
 }
    },
 methods: {
      _add(){
        this.message.push({
          name:'name2'
 })
      },
 },
 };
</script>
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏