VUE中对象与数组监听问题

578 阅读2分钟

这是我参与8月更文挑战的第31天,活动详情查看:8月更文挑战

问题起源

起源于最近的生产项目,父组件向子组件传递一个Object数据,因为这个Object数据太多,于是我“偷懒”了一下,在子组件的data中是这样写的:

...
data () {
  return {
    content: {}
  }
}
...

没想到,就是因为这一行代码花费了一个下午来找响应问题:怎么数据更新了以后,子组件数据没有变化呢?

由此引导我重新认识VUE的深度响应原理。原文文档比我写的要详细。下面我们介绍一些实用的内容,即在实际工作中能用到的东西。

话说现在VUE3都上线了,我VUE2还没学太会,是不是太菜了。

因此,我们来重新认识一下关于VUE中的Object与数组监听问题。

实验

Object监听问题重现

在VUE官方文档中说:

由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。

那么,什么叫“不能检测”呢?

请看下面的实验代码:

<!DOCTYPE html>

<head>
    <script src="http://unpkg.com/vue/dist/vue.js"></script>
    <script src="http://unpkg.com/element-ui@2.15.5/lib/index.js"></script>
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
</head>

<body>
    <div id="app">
        <template>
            <div>
                {{message}}
            </div>
            <div>
                对象: {{obj}}
            </div>
            <div>
                computed: obj.a = {{objA}}
            </div>
            <div>
                computed: obj.c = {{objC}}
            </div>
            <el-button type="primary" @click="changeData">更新数据</el-button>
        </template>
    </div>
    <script>
        var Main = {
            data() {
                return {
                    message: '数据修改前:',
                    obj: {
                        a: 1,
                        b: 2
                    }
                }
            },
            computed: {
                objA() {
                    return this.obj.a
                },
                objC() {
                    return this.obj.c
                }
            },
            methods: {
                changeData() {
                    this.obj.c = '2';
                    this.message = '数据修改后:'
                }
            }
        }
        var Ctor = Vue.extend(Main)
        new Ctor().$mount('#app')
    </script>
</body>

</html>

我们先看一下效果:

8fd45e75-51d8-4b40-8c6f-3e77c760da20.gif

可以看到,obj.a的属性值变化影响到了computed中的objA,而obj.c的赋值操作却没有影响。

原因在哪里呢?

原因就在于其中的data,我在定义data中的obj时,只给了两个属性:a与b,而在后续点击操作更新数据的时候,我们是这样设置obj.c的:

this.obj.c = '2'

没有处罚computed更新对应的objC的值。

我们根据官方提示,使用另外一种方式设置尝试一下: 即将上述更新c值的方式修改为:

this.$set(this.obj, 'c', 'cvalue')

再来看看效果:

c13a7f73-3149-48fd-b8fe-68379a994e0b.gif

可以明显的看到,相对应的objC也响应了变化。

数组的变化了类似,对于数据,一方面是注意监听的问题,另一方面是需要注意,数组某些方法会触发视图监听:在VUE的文档中列的很详细:如push等系列函数,而通过list[i]修改元素值不会触发侦听,这里就不再赘言。