Vue 3. 计算属性和侦听器

本文主要包含以下知识点:

  • 计算属性
    • 计算属性缓存 vs 方法
    • 计算属性的 setter
  • 数据观察

计算属性

模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。

例如:

...
<div id="example">
    {{ message.split('').reverse().join('') }}
</div>
...

在这个地方,模板不再是简单的声明式逻辑。我们必须看一段时间才能意识到,这里是想要显示变量 message 的翻转字符串。当我们想要在模板中多次引用此处的翻转字符串时,就会更加难以处理。

所以,对于任何复杂逻辑,都应当使用计算属性。

计算属性对应的数据选项为 computed,类似于 data,只不过 data 对应的是一个值,而计算属性对应的是一个方法。在方法里面就可以书写复杂的计算,然后将结果进行返回。

下面是一个计算属性的示例:

<body>
    <div id="app">
        <p>{{ message }}</p>
        <!-- Hello Vue! -->
        <p>{{ reverseMess }}</p>
        <!-- !euV olleH -->
    </div>
    <script src="https://unpkg.com/vue/dist/vue.min.js"></script>
    <script>
        const data = {
            message: 'Hello Vue!'
        }
        const vm = new Vue({
            el: '#app',
            data: data,
            computed: {
                reverseMess: function () {
                    return this.message.split('').reverse().join('')
                }
            }
        });
    </script>
</body>

效果:计算属性的值依赖于数据,如果数据发生变化,计算属性也会发生变化
在这里插入图片描述

计算属性缓存 vs 方法

Vue 的实例中,还存在一个数据选项 methods,用来书写方法。

例如上面的例子如果将计算属性修改为方法,代码如下:

<body>
    <div id="app">
        <p>{{ message }}</p>
        <!-- Hello Vue! -->
        <p>{{ reverseMess() }}</p>
        <!-- !euV olleH -->
    </div>
    <script src="https://unpkg.com/vue/dist/vue.min.js"></script>
    <script>
        const data = {
            message: 'Hello Vue!'
        }
        const vm = new Vue({
            el: '#app',
            data: data,
            methods: {
                reverseMess: function () {
                    return this.message.split('').reverse().join('')
                }
            }
        });
    </script>
</body>

效果:
在这里插入图片描述
这个时候,给人的感觉计算属性和方法仿佛是同一个东西。

但是实际上,计算属性只有在它的相关依赖发生改变时才会重新求值。相比而言,只要发生重新渲染,methods 调用总会执行所有函数。

下面我们用一个示例来进行说明:

<body>
    <div id="example">
        <button v-on:click="a++">A++</button>
        <button v-on:click="b++">B++</button>
        <!-- computedA 计算属性依赖于数据 a , 当 a 的值发生变化时 , computedA 会重新进行计算 -->
        <p>{{ computedA }}</p>
        <!-- computedB 计算属性依赖于数据 b , 当 b 的值发生变化时 , computedB 会重新进行计算 -->
        <p>{{ computedB }}</p>
        <!-- 只要发生重新渲染 , 所有函数就会被执行 -->
        <p>{{ methodA() }}</p>
        <p>{{ methodB() }}</p>
    </div>
    <script src="https://unpkg.com/vue/dist/vue.min.js"></script>
    <script>
        const vm = new Vue({
            el: '#example',
            data: {
                a: 1,
                b: 1
            },
            computed: {
                computedA: function () {
                    console.log('computedA has done');
                    return this.a;
                },
                computedB: function () {
                    console.log('computedB has done');
                    return this.b;
                }
            },
            methods: {
                methodA: function () {
                    console.log('methodA has done');
                    return this.a;
                },
                methodB: function () {
                    console.log('methodB has done');
                    return this.b;
                }
            }
        });
    </script>
</body>

效果:
在这里插入图片描述
那么,什么时候使用计算属性,什么时候使用方法呢?

假设有一个性能开销比较大的操作,它需要遍历一个极大的数组和做大量的计算。这个时候建议使用计算属性来返回结果!即使其他地方发生了局部渲染,也不会影响该计算属性的值,不用再次进行计算。而如果性能开销不大,并且不希望存在缓存,则建议使用方法来返回结果。

计算属性的 setter

一般情况下,计算属性提供一个 getter 即可,也就是模板中直接获取计算属性的值。但是如果涉及到要设置计算属性的值的话,也可以给计算属性设置一个 setter,在 setter 中来对计算属性设置时进行一定的限制。

下面是关于计算属性 setter 的一个示例:

<body>
    <!-- 一般来讲 , 我们只会获取计算属性的值 , 如果要设置计算属性的值 , 可以为计算属性设置一个 setter -->
    <div id="app">
        <!-- fullname 为一个计算属性 -->
        <p>你的全名为:{{fullName}}</p>
        <!-- xing 和 ming 为数据 -->
        <p>你的姓氏为:{{xing}}</p>
        <p>你的名字为:{{ming}}</p>
    </div>
    <script src="https://unpkg.com/vue/dist/vue.min.js"></script>
    <script>
        const vm = new Vue({
            el: '#app',
            data: {
                xing: 'xie',
                ming: ' jie'
            },
            computed: {
                // 注意使用 getter 和 setter 的时候,fullName 对应一个对象,而不再是一个函数
                // get 或者 set 属性对应的才是一个函数
                fullName: {
                    // getter 用于读取的时候
                    get: function () {
                        return this.xing + this.ming;
                    },
                    // setter 写入时触发
                    // 例如在浏览器控制台执行 vm.fullName = 'song ya jing' , 这里就是对计算属性进行了值的设定
                    set: function (value) {
                        // 根据新设置的姓名来给 firstName 和 lastName 进行赋值
                        const newName = value.split(' ');
                        this.xing = newName[0]; // song
                        this.ming = ' '; // 首先重置this.ming,注意有一个空格
                        // 通过for循环将newName剩余的元素拼接到名字里面
                        for (let i = 1; i < newName.length; i++) {
                            this.ming += newName[i] + ' '; // ya jing
                        }
                    }
                }
            }
        });
    </script>
</body>

效果:
在这里插入图片描述

数据观察

Vue 提供了一个 watch 数据选项来观察和响应 data 数据的变动。watch 对象的键是需要观察的 data 数据,值对应一个回调函数,回调函数得到的参数为新值和旧值。

示例如下:

<body>
    <div id="app">
        <button @click="a++">a加1</button>
        <p>当前 a 的值为:{{ a }}</p>
        <p>{{ message }}</p>
    </div>
    <script src="https://unpkg.com/vue/dist/vue.min.js"></script>
    <script>
        const vm = new Vue({
            el: '#app',
            data: {
                a: 1,
                message: ''
            },
            watch: {
                // 对 data 数据中的 a 进行监控 
                // 发生改变时 , 新值和旧值会传入回调函数
                a: function (val, oldVal) {
                    this.message = 'a的旧值为' + oldVal + ',新值为' + val;
                }
            }
        })
    </script>
</body>

效果:
在这里插入图片描述
除了 watch 数据选项以外,实例对象的 $watch 方法也可以监听 data 数据的改变。

示例如下:

<body>
    <div id="app">
        <button @click="a++">a加1</button>
        <p>当前a的值:{{a}}</p>
        <p>{{ message }}</p>
    </div>
    <script src="https://unpkg.com/vue/dist/vue.min.js"></script>
    <script>
        const vm = new Vue({
            el: '#app',
            data: {
                a: 1,
                message: ''
            }
        });
        // 实例对象的 $watch 方法也可以监听 data 数据的改变
        // 将新值和旧值传入到回调函数里面
        vm.$watch('a', function (val, oldVal) {
            this.message = 'a的旧值为' + oldVal + ',新值为' + val;
        })
    </script>
</body>

效果同上。

虽然说 watch 数据选项和 $watch 都可以实现对 data 数据的监听,但是还是有一定的区别。

$watch 方法会返回一个取消观察函数,用来停止触发回调。

示例如下:

<body>
    <div id="app">
        <button @click="a++">a加1</button>
        <p>当前a的值:{{a}}</p>
        <p>{{ message }}</p>
    </div>
    <script src="https://unpkg.com/vue/dist/vue.min.js"></script>
    <script>
        const vm = new Vue({
            el: '#app',
            data: {
                a: 1,
                message: ''
            }
        });
        // 实例对象的 $watch 方法也可以监听 data 数据的改变
        // 将新值和旧值传入到回调函数里面
        // $watch 会返回一个函数 , 调用该函数可以取消观察
        const unwatch = vm.$watch('a', function (val, oldVal) {
            if (val === 10) {
                unwatch(); // 执行函数取消观察
            }
            this.message = 'a的旧值为' + oldVal + ',新值为' + val;
        })
    </script>
</body>

效果:
在这里插入图片描述

总结

  1. 在模板中放入太多的逻辑会让模板过重且难以维护。这个时候可以考虑使用计算属性。

  2. 计算属性对应的数据选项为 computed,类似于 data,只不过 data 对应的是一个值,而计算属性对应的是一个方法。在方法里面就可以书写复杂的计算,然后将结果进行返回。

  3. 计算属性与方法的区别在于,计算属性只有在它的相关依赖发生改变时才会重新求值。相比而言,只要发生重新渲染,methods 调用总会执行所有函数。

  4. 假设有一个性能开销比较大的操作,这个时候建议使用计算属性来返回结果!即使其他地方发生了局部渲染,也不会影响该计算属性的值,不用再次进行计算。而如果性能开销不大,并且不希望存在缓存,则建议使用方法来返回结果。

  5. 可以给计算属性设置一个 setter,在 setter 中来对计算属性设置时进行一定的限制。

  6. Vue 提供了一个 watch 数据选项来观察和响应 data 数据的变动。

  7. 除了 watch 数据选项以外,实例对象的 $watch 方法也可以监听 data 数据的改变,两者的区别在于后者会返回一个取消观察函数,用来停止触发回调。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值