Vue 中的 $set() 方法的使用

183 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第25天,点击查看活动详情

之前的那个,搜索框,搜索条件过多时,“折叠”/“展开” 按钮不会主动加载,而且当我切换表的时候,所有表共用一组 “折叠”/“展开”,也就是说如果表1,搜索条件很多,应该展示 “折叠” 按钮,页面不会主动加载,我还要鼠标点击一下页面,才会出现;而这时,如果我切换到查询条件较少的表2,按理说不用折叠,但 “折叠” 按钮还是在,必须鼠标点一下该按钮,才会触发事件,使按钮消失;这时如果再切换回表1,仍然是没有 “折叠” 按钮的......具体的问题描述可以看这里。不过今天,将这个 “折叠” 按钮问题解决了,请教了前辈,大佬帮忙解决的🙂 过程中学到一点东西,复盘一下。

问题的解决

其实在这篇文章里,我分析的也差不多,所有表共用一个 searchBar,是因为这些表都是在一个页面内,我参考的页面代码,他们都是在不同的页面中,有不同的 searchBar,所以不会有这个问题,所以写法得变一变。

看问题过程中,大佬提到,问题关键就是,我在使用 searchBar 组件时,我的值在父组件中是改变了的,但子组件 searchBar 并不知道这些值已经变了,所以也就没有区分开来,所有表都用的同一个。

解决方法就是监控到我放在 searchBar 中的查询条件,如果有发生改变,及时反馈给 searchBar。这里监控,就用到了 $set()

$set() 方法

场景

当生成 Vue 实例后,再次给数据赋值时,有时候并不会自动更新到视图上面,即,如果在实例创建后添加新的属性到实例上,不会触发视图更新。

案例

<template>
    <div class="home">
    <div>{{student}}</div>
    <div v-for="(item,index) in items" :key="index">{{item}}</div>
        <button @click="btn()">修改</button>
    </div>
</template>

<script>
    export default {
        name: 'Home',
        data(){
            return{
                student:{
                    name:'张三',
                },
                items:[1, 2, 3],
            }
        },
        methods:{
            btn(){
                this.student.age = 18;
                this.items[1] = 'two';
                console.log(this.student,this.items);
            }
        }
    }
</script>

点击“修改”按钮后,页面上:

image.png

点击“修改”按钮后,控制台中:

image.png

可以看出,元素的内容其实是改变了的,但页面中视图并没有及时更新。

出现这种现象的原因:

受 ES5 的限制,Vue.js 不能检测到对象属性的添加或删除。因为 Vuew.js 在初始化实例时将属性转为 getter/setter,所以属性必须在 data 对象上才能让 Vue.js 转换它,才能使它是响应的。

其他一些类似操作&&使用set()解决

  • 利用索引直接设置一个元素时,如:vm.items[indexOfItem] = newValue;
  • 修改数组长度时,如:vm.items.length = newLength;
  • 使用 this.arr[0] 去更新数组 array 的元素,视图不会刷新;
  • 使用 Vue.set(this.arr, 0, !this.arr[0]) 去更新数组 array 的内容,视图会刷新;
  • 使用 this.arr[0] = !this.arr[0] 和 this.obj.a = !this.obj.a 同时更新,视图会刷新;

总结:

如果方法里面单纯的更新数组 array 的话,要使用 Vue.set(); 如果方法里面同时又数组和对象的更新,直接操作 data 即可;

使用 Vue.set() && $set() 改写上述案例

使用 Vue.set()

Vue.set() 语法
Vue.set( target, propertyName/index, value )
参数:
    {Object | Array} target
    {string | number} propertyName/index
    {any} value
返回值:设置的值。
用法:
    向响应式对象中添加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新。
    它必须用于向响应式对象上添加新 property,因为 Vue 无法探测普通的新增 property (比如 this.myObject.newProperty = 'hi')
注意:
    对象不能是 Vue 实例,或者 Vue 实例的根数据对象。
案例:
<template>
    <div class="home">
    <div>{{student}}</div>
    <div v-for="(item,index) in items" :key="index">{{item}}</div>
        <button @click="btn()">修改</button>
    </div>
</template>

<script>
    import Vue from 'vue'
    export default {
        data(){
            return{
                student:{
                    name:'张三',
                },
                items:[1, 2, 3],
            }
        },
        methods:{
            btn(){
                Vue.set(this.student, 'age', 18);
                Vue.set(this.items, 1, 'two');
                console.log(this.student,this.items);
            }
        }
    }
</script>
点击“修改”按钮后,页面上:

image.png

点击“修改”按钮后,控制台中:

image.png

使用 $set()

$set() 语法
vm.$set( target, propertyName/index, value )
参数:
    {Object | Array} target
    {string | number} propertyName/index
    {any} value
返回值:设置的值。
用法:
    这是全局 Vue.set 的别名。
    参考:Vue.set
案例
<template>
    <div class="home">
    <div>{{student}}</div>
    <div v-for="(item,index) in items" :key="index">
        <span>年龄:{{item.age}}</span>
        <span>地点:{{item.address}}</span>
    </div>
        <button @click="btn()">修改</button>
    </div>
</template>

<script>
    import Vue from 'vue'
    export default {
        data(){
            return{
                student:{
                    name:'张三',
                },
                items:[
                    {
                        age:12,
                        address:北京,
                    },
                    {
                        age:13,
                        address:上海,
                    },
                    {
                        age:14,
                        address:广州,
                    }
                ],
            }
        },
        methods:{
            btn(){
                this.$set(this.student, 'age', 18);
                this.$set(this.items, 1, {age:22, address:'深圳'});
                console.log(this.student,this.items);
            }
        }
    }
</script>
点击“修改”按钮后,页面上:

image.png

点击“修改”按钮后,控制台中:

image.png

总之,感觉今天因为一个 $set() 不懂,大佬在帮忙看问题时,我都是一脸懵,本来还想着趁着大佬指导,学习一下他解决问题的思路,但发现思维完全跟不上,Vue!!!我恨......
希望本文能够帮到你,如有错误,望指正!
我向你敬礼啊,Salute!