Vue(三)——computed和watch

89 篇文章 7 订阅
本文详细介绍了Vue.js中的computed计算属性和watch观察者在处理派生数据时的区别和应用场景。通过实例展示了如何使用computed进行数据筛选、缓存特性以及getter和setter的使用。同时,对比了watch在处理异步操作时的优势,演示了如何通过watch监听数据变化并执行异步任务。此外,还探讨了多层监听和深度监听的配置方法。
摘要由CSDN通过智能技术生成

一、computed计算属性

在实际的应用中,我们会有一些原始数据,同时在应用中又会有一些数据是根据某些原始数据派生出来的,针对这样的一种情况,vue 定义了一个专门用来处理这种派生数据的选项:computed。

computed计算属性:根据一组已有数据派生出一组新的数据。计算属性需要写成函数(本质上是一个对象,包含了get和set方法的对象,没有set方法时可以直接写成函数)

//计算属性:根据一组已有数据派生出一组新的数据
computed: {
    //计算属性是一个包含了get和set方法的对象
    showUsers:{
        get(){
        
        },
        set(){
            //更改派生数据
        }
    }
}

需求:根据单选按钮,根据数据,进行男和女数据的分类展示

1.1普通方法实现

1.1.1单选按钮选中效果

  • 使用@click事件绑定点击事件;
  • methods里编写事件过程,并筛选数据;
  • 单选是否选中根据data中某个值决定this.gender = e.target.value,如, :checked="gender==='男'",并更改数据中gender的值
<body>
    <div id="app">
        <input type="radio" :checked="gender==''" @click="checkGender" value="" />全部
        <input type="radio" :checked="gender=='男'" @click="checkGender" value="男" />男
        <input type="radio" :checked="gender=='女'" @click="checkGender" value="女" />女
        <hr>
        <ul>
            <li v-for="user in users" :key="user.id">{{user.username}} ---- {{user.gender}}</li>
        </ul>
    </div>
    <script src="./js/vue.js"></script>
    <script>
        let app = new Vue({
            el: "#app",
            data: {
                gender:'',// 用于存储checked的性别
                users: [
                    { id: 1, username: 'baogege', gender: '男' },
                    { id: 2, username: 'mt', gender: '男' },
                    { id: 3, username: 'haigege', gender: '男' },
                    { id: 4, username: 'zMouse', gender: '男' },
                    { id: 5, username: 'reci', gender: '女' },
                    { id: 6, username: 'lisi', gender: '女' }
                ]
            },
            methods:{
                checkGender(e){
                    this.gender = e.target.value;
                }
            }
        });
    </script>
</body>

1.1.2实现数据筛选,男女分类显示

<body>
    <div id="app">
        <input type="radio" :checked="gender==''" @click="checkGender" value="" />全部
        <input type="radio" :checked="gender=='男'" @click="checkGender" value="男" />男
        <input type="radio" :checked="gender=='女'" @click="checkGender" value="女" />女
        <hr>
        <ul>
            <li v-for="user in showUsers" :key="user.id">{{user.username}} ---- {{user.gender}}</li>
        </ul>
    </div>
    <script src="./js/vue.js"></script>
    <script>
        // 数据单独放出来,gender=''时showUsers才能有数据
        let users = [
            { id: 1, username: 'baogege', gender: '男' },
            { id: 2, username: 'mt', gender: '男' },
            { id: 3, username: 'haigege', gender: '男' },
            { id: 4, username: 'zMouse', gender: '男' },
            { id: 5, username: 'reci', gender: '女' },
            { id: 6, username: 'lisi', gender: '女' }
        ];
        let app = new Vue({
            el: "#app",
            data: {
                gender: '',// 用于存储checked的性别
                showUsers: users, //(派生数据)因为不想改变原数据,通过变量储存更改后的数据,初始化数据为users
                users: users,
            },
            methods: {
                checkGender(e) {
                    this.gender = e.target.value;
                    // 对数据实现筛选
                    if(this.gender==""){
                        this.showUsers = this.users;
                    }else{
                        this.showUsers = this.users.filter(user => this.gender == user.gender);
                    }
                }
            }
        });
    </script>
</body>

1.2使用computed实现

1.2.1使用v-model双绑实现radio选中效果

 v-model="gender"直接和gender值进行绑定;

不再需要click事件,可以直接实现数据双绑(当v-model的值即gender的值和value的值相等时就会被选中;改变checked属性时又会反向更新data中gender的值);

<body>
    <div id="app">
        <input type="radio" v-model="gender" value="" />全部
        <input type="radio" v-model="gender" value="男" />男
        <input type="radio" v-model="gender" value="女" />女
        <hr>
        <ul>
            <li v-for="user in users" :key="user.id">{{user.username}} ---- {{user.gender}}</li>
        </ul>
    </div>
    <script src="./js/vue.js"></script>
    <script>
        let app = new Vue({
            el: "#app",
            data: {
                gender:'',// 用于存储checked的性别
                users: [
                    { id: 1, username: 'baogege', gender: '男' },
                    { id: 2, username: 'mt', gender: '男' },
                    { id: 3, username: 'haigege', gender: '男' },
                    { id: 4, username: 'zMouse', gender: '男' },
                    { id: 5, username: 'reci', gender: '女' },
                    { id: 6, username: 'lisi', gender: '女' }
                ]
            },
        });
    </script>
</body>

 1.2.2使用computed实现数据筛选

需求:根据选中的值,分类显示男和女的数据信息

  • showUsers会监听users数据的变化,showUsers会依赖users数据的变化,users变化后,showUsers就会变化
  • 如果计算属性只有get逻辑,没有set逻辑时则可以简写
  • 最后showUsers的数据,相当于get()方法执行后返回的结果(类似于Object.defineProperty(obj,'x',{}}数据劫持后,obj.x执行的结果))
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>计算属性</title>
</head>

<body>
    <div id="app">
        <label><input type="radio" v-model="gender" value="" />全部</label>
        <label><input type="radio" v-model="gender" value="男" />男</label>
        <label><input type="radio" v-model="gender" value="女" />女</label>
        <hr>
        <ul>
            <li v-for="user in showUsers" :key="user.id">{{user.username}} ---- {{user.gender}}</li>
        </ul>
    </div>
    <script src="./js/vue.js"></script>
    <script>
        let app = new Vue({
            el: "#app",
            data: {
                gender: '',// 用于存储checked的性别
                users: [
                    { id: 1, username: 'baogege', gender: '男' },
                    { id: 2, username: 'mt', gender: '男' },
                    { id: 3, username: 'haigege', gender: '男' },
                    { id: 4, username: 'zMouse', gender: '男' },
                    { id: 5, username: 'reci', gender: '女' },
                    { id: 6, username: 'lisi', gender: '女' }
                ]
            },
            // 注意:computed是vue实例的一个属性,不是data中的属性
            computed: {
                // 会监听users数据的变化,showUsers会依赖users数据的变化,users变化后,showUsers就会变化
                // showUsers: {
                //     get() {
                //         // 返回true即返回所有数据
                //         return this.gender === "" ? this.users : this.users.filter(user => this.gender == user.gender);
                //     },
                //     set() {

                //     }
                // }
                
                // 如果计算属性只有get逻辑,没有set逻辑时则可以简写
                // 最后showUsers的数据,相当于get()方法执行后返回的结果(类似于Object.defineProperty(obj,'x',{}}数据劫持后,obj.x执行的结果))
                showUsers(){
                    return this.gender === "" ? this.users : this.users.filter(user => this.gender == user.gender);
                }
            }
        });
    </script>
</body>

</html>

 1.3 computed特性及缓存

普通函数也能解决以上问题,为什么还要使用computed?主要是因为computed可以缓存

  •  计算属性类似 gettersetter ,当访问某个计算属性的时候,就会调用 computed 中同名的函数,函数的返回值将作为该计算属性的值
  • 计算属性的值依赖计算函数中依赖的其它响应式数据
  • 计算属性的值可以缓存,如果依赖的其它响应式数据没有发生变化,但多次访问该计算属性,得到结果是最近一次变化产生的值(相对于调用方法得到结果在某些时候性能要好一些)
  • 计算属性不支持异步

需求:普通方法和computed分别获取时间戳

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>计算属性</title>
</head>

<body>
    <div id="app">
        <p>{{now}}</p>
        <button @click="showDate=!showDate">showDate</button>
        <!-- 此处打印的是缓存后的 now-->
        <p v-if="showDate">{{now}}</p>
        <hr>

        <p>{{getNow()}}</p>
        <button @click="showDate = !showDate">getNow</button>
        <p v-if="showDate">{{getNow()}}</p>
    </div>
    <script src="./js/vue.js"></script>
    <script>
        let a = 1;
        let app = new Vue({
            el: "#app",
            data: {
                b:1,
                showDate: false
            },
            computed: {
                now() {
                    // 如果计算属性依赖的数据没有发生任何变化,该计算属性的get是不会调用的(即缓存)
                    // 这里监听的数据是其依赖的data里的showDate数据,如果showDate数据变了就会重新计算(并不是data里的任何数据变了都会computed监听)
                    // 如果监听的是let a =1这种数据,此处now()方法里根本没有依赖这个数据,即使a和b改变了,也不会重新计算
                    return Date.now();
                }
            },
            methods:{
                getNow(){
                    return Date.now();
                }
            }
        });
    </script>
</body>

</html>

computed缓存:按钮点击时根据showDate的值显示当前时间戳(每次点击显示的都是缓存的时间戳数据)

普通方法:按钮每次点击后显示的时间戳都会重新获取

 

1.4 计算属性的getter与setter方法

默认情况下,计算属性函数是一个 getter 函数,如果计算属性只有 get 需求,则可以简写

computed: {
  now() {
    return Date.now();
  }
  // 等于
  now: {
    get() {
      return Date.now();
    }
  }
}

1.4.1计算属性computed的set需求

但是有的时候,这种派生数据既有 get 需求,也有 set 需求

需求:全选功能,当点击全选按钮时,所有复选框选中;依次点击了所有复选框后,全选按钮也被选中

  • 如果使用v-bind:checked="checkAll"不会反向绑定;
  • v-model会双向绑定,computed如果没有set()方法,但是使用v-model设置值就会报错;
  • 有set()方法时会自动将当前最新的值传入,注意不能直接通过this.checkAll得到值
  • 当this.users里面的数据发生改变时出发get()方法,产生新的checkAll的值;当对checkAll进行赋值时,触发set();
  • 可以通过foreach或者map方法修改数据
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>计算属性</title>
</head>

<body>
    <div id="app">
        <label><input type="radio" v-model="gender" value="" />全部</label>
        <label><input type="radio" v-model="gender" value="男" />男</label>
        <label><input type="radio" v-model="gender" value="女" />女</label>
        <hr>
        <ul>
            <!-- 使用v-model将复选框和数据进行双绑定 -->
            <li v-for="user in showUsers" :key="user.id"><input type="checkbox" v-model="user.checked"/>{{user.username}} ---- {{user.gender}}</li>
        </ul>
        <!-- 使用v-model将checkAll和复选框进行双绑 -->
        <input type="checkbox" v-model="checkAll"/> 全选
    </div>
    <script src="./js/vue.js"></script>
    <script>
        let app = new Vue({
            el: "#app",
            data: {
                gender: '',// 用于存储checked的性别
                users: [
                    { id: 1, username: 'baogege', gender: '男' ,checked:false},
                    { id: 2, username: 'mt', gender: '男' ,checked:false},
                    { id: 3, username: 'haigege', gender: '男' ,checked:false},
                    { id: 4, username: 'zMouse', gender: '男' ,checked:false},
                    { id: 5, username: 'reci', gender: '女' ,checked:false},
                    { id: 6, username: 'lisi', gender: '女' ,checked:false}
                ]
            },
            // 注意:computed是vue实例的一个属性,不是data中的属性
            computed: {
                showUsers(){
                    return this.gender === "" ? this.users : this.users.filter(user => this.gender == user.gender);
                },
                // checkAll是返回的派生数据(真假check)
                checkAll:{
                    // 当this.users里面的数据发生改变时出发get()方法,产生新的checkAll的值
                    get(){
                        // 全选函数every  user.checked==true可以直接写为user.checked
                        return this.users.every(user=>user.checked);
                    },
                    // 如果使用v-bind:checked="checkAll"不会反向绑定
                    // v-model会双向绑定,computed如果没有set()方法,但是使用v-model设置值就会报错;
                    // 有set()方法时会自动将当前最新的值传入,注意不能通过this.checkAll得到
                    // 当对checkAll进行赋值时,出发set()
                    set(newVal){
                        // 设置当全选按钮点击时,同时更新data数据,将所有checked取反
                        // this.users.forEach(user => {
                        //     user.checked = !user.checked;
                        // });

                        // map():接收一个函数,将原数组中的所有元素用这个函数处理后放入新数组返回。
                        this.users = this.users.map(user=>{
                            return {//...user生成新数组
                                ...user,checked:newVal
                            }
                        });
                    }
                }
            }
        });
    </script>
</body>

</html>

二、watch(可实现异步)

有的时候,我们需要的派生数据是通过异步的方式处理的,这个时候,计算属性computed就不太好用了(不能处理异步)。我们可以使用另外一个选项:watch去处理异步需求。

  • watch没有返回值,只注重处理过程,watch中的方法可以通过参数获取新值和旧值;
  • watch中的方法名必须和v-model监听的值相同(如keyWord)
  • 当watch到值发生改变后直接处理逻辑,比如进行赋值操作,而没有返回值
  • 因为watch是只有执行过程没有返回,所以需要设置showUsers接收过滤后的数据(computed是直接返回到showUsers作为结果所以不需要)

需求:通过关键字搜索username(使用setTimeout模拟异步)

computed不能实现异步:即使写了new Promise()依然无法实现异步处理

<body>
    <div id="app">
        <input type="text" v-model="keyWord"/>
        <hr>
        <ul>
            <li v-for="user of showUsers" :key="user.id">{{user.username}}</li>
        </ul>
    </div>
    <script src="./js/vue.js"></script>
    <script>
        let app = new Vue({
            el: "#app",
            data: {
                keyWord:'',
                users: [
                    { id: 1, username: 'baogege', gender: '男'},
                    { id: 2, username: 'mt', gender: '男'},
                    { id: 3, username: 'haigege', gender: '男'},
                    { id: 4, username: 'zMouse', gender: '男'},
                    { id: 5, username: 'reci', gender: '女'},
                    { id: 6, username: 'lisi', gender: '女'}
                ],
            },
            computed: {
                showUsers: {
                    get(){
                        // 如果没有定时器模拟异步,可以正常显示,有异步后无法正常过滤
                        setTimeout(() => {
                            return this.keyWord==""?false:this.users.filter(user=>user.username.includes(this.keyWord));
                        }, 1000);
                    }
                }
            },
        });
    </script>
</body>

watch可以实现异步: 

<body>
    <div id="app">
        <input type="text" v-model="keyWord"/>
        <hr>
        <ul>
            <li v-for="user of showUsers" :key="user.id">{{user.username}}</li>
        </ul>
    </div>
    <script src="./js/vue.js"></script>
    <script>
        let app = new Vue({
            el: "#app",
            data: {
                keyWord:'',
                users: [
                    { id: 1, username: 'baogege', gender: '男'},
                    { id: 2, username: 'mt', gender: '男'},
                    { id: 3, username: 'haigege', gender: '男'},
                    { id: 4, username: 'zMouse', gender: '男'},
                    { id: 5, username: 'reci', gender: '女'},
                    { id: 6, username: 'lisi', gender: '女'}
                ],
                // 因为watch是只有执行过程没有返回,所以需要设置showUsers接收过滤后的数据(computed是直接返回到showUsers作为结果所以不需要)
                showUsers:[]
            },
            watch: {
                // 注意watch中方法名是v-model监听的值keyWord
                keyWord(newVal,oldVal){
                    console.log(newVal,oldVal);
                    setTimeout(() => {
                            if(this.keyWord!=""){
                                this.showUsers = this.users.filter(user=>user.username.includes(this.keyWord));
                            }
                        }, 1000);
                }
            }
        });
    </script>
</body>

2.1 多层监听

对于多层数据的监听,可以使用字符串+点语法

data数据中对象有多层,就需要使用'a.b.c'进行监听

data: {
    a:{ b: { c: 1 } }
},
watch: {
  'a.b.c': function() {
    //...
  }
}

2.2 深度监听

默认情况下,watch 只对当前指定的值进行一层监听,如果需要对对象进行深度监听:

如下默认只监听了a这一层,如果想监听深层的b或者c,就需要使用deep:true属性;此时a监听时不再是函数,而是对象,对象里会去执行handler()函数

data: {
    a:{ b: { c: 1 } }
},
watch: {
  a: {
    handler() {
      console.log('a deep');
    },
    deep: true
  }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值