Vue3——计算属性和监听属性

计算属性和监听属性

1、计算属性

1.1、计算属性简介

在模板中绑定表达式的形式使用起来虽然便利,但是它通常被用于简单的运算。如果在模板中放入过多的逻辑就会变得难以维护。例如,在模板中使用表达式,对明日科技企业邮箱地址中的QQ号码进行截取,代码如下:

html 复制代码
<div id="app">
    <span>{{str.substr(0, str.indexOf('@'))}}</span>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
    const vm = Vue.createApp({
        data() {
            return {
                str: '123423423@qq.com'
            }
        }
    }).mount('#app');
</script>

上例中,模板中定义的表达式包含了多个操作,结构比较复杂。因此,为了使模板的结构清晰,对于比较复杂的逻辑,可以使用Vue.js提供的计算属性。对上述代码使用计算属性进行改写,代码如下:

html 复制代码
<div id="app">
    <span>{{intercept}}</span>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
    const vm = Vue.createApp({
        data() {
            return {
                str: '123423423@qq.com'
            }
        },
        computed: {
            intercept() {
                return this.str.substr(0, this.str.indexOf('@'));
            }
        }
    }).mount('#app');
</script>

上述代码中,intercept就是定义的计算属性。由此可见,计算属性需要以函数的形式定义在computed选项中,最后返回一个计算结果,该结果可以在插值时进行调用并渲染出来。

1.2、计算属性的应用

通过计算属性可以实现各种复杂的逻辑,包括运算、函数调用等,只要最后返回一个计算结果就可以。当计算属性依赖的数据发生变化时,计算属性的值会自动更新,所有依赖该计算属性的数据绑定也会同步进行更新。

示例:每个单词首字母大写。

html 复制代码
<div id="app">
    <p>原字符串:{{str}}</p>
    <p>新字符串:{{newstr}}</p>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
    const vm = Vue.createApp({
        data() {
            return {
                str: 'my heart will go on'
            }
        },
        computed: {
            newstr: function() {
                let arr = this.str.split(' ');
                for(let i = 0; i < arr.length; i++) {
                    arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].substr(1).toLowerCase();
                }
                return arr.join(' ');
            }
        }
    }).mount('#app');
</script>

上述代码中,定义的计算属性newstr的值依赖于data选项中str属性的值。当str属性的值发生变化时,newstr属性的值也会随之变化。

计算属性可以依赖Vue实例中的多个数据,只要其中任一数据发生变化,计算属性就会随之变化,视图也会随之更新。

1.3、计算属性的getter和setter

1.3.1、使用getter读取属性值

每一个计算属性都包含一个getter和一个setter。上面的示例中都是计算属性的默认用法,只是使用getter来读取数据。例如,定义一个获取人物姓名的计算属性,代码如下:

html 复制代码
<div id="app">
    <span>{{fullname}}</span>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
    const vm = Vue.createApp({
        data() {
            return {
                surnname: 'Jim',
                lastname: 'Carrey'
            }
        },
        computed: {
            fullname: function() {
                return this.surnname + ' ' + this.lastname;
            }
        }
    }).mount('#app');
</script>

上述代码中,fullname是定义的计算属性,为该属性定义的函数将默认作为fullname属性的getter。将上述代码修改为使用getter的形式,代码如下:

html 复制代码
<div id="app">
    <span>{{fullname}}</span>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
    const vm = Vue.createApp({
        data() {
            return {
                surnname: 'Jim',
                lastname: 'Carrey'
            }
        },
        computed: {
            fullname: {
                //getter
                get() {
                    console.log('getter...')
                    return this.surnname + ' ' + this.lastname;
                }
            }
        }
    }).mount('#app');
</script>
1.3.2、使用setter设置属性值

计算属性默认只有getter。除了getter,还可以设置计算属性的setter。getter主要用来读取值,而setter主要用来设置值。当手动更新计算属性的值时,就会触发setter,执行一些自定义的操作。例如,使用setter重新设置人物姓名,代码如下:

html 复制代码
<div id="app">
    <span>{{fullname}}</span>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
    const vm = Vue.createApp({
        data() {
            return {
                surnname: 'Jim',
                lastname: 'Carrey'
            }
        },
        computed: {
            fullname: {
                //getter
                get() {
                    console.log('getter...')
                    return this.surnname + ' ' + this.lastname;
                },
                //setter
                set(value) {
                    console.log('setter...');
                    let names = value.split(' ');
                    this.surnname = names[0];
                    this.lastname = names[1];
                }
            }
        }
    }).mount('#app');
    setTimeout(()=> {
        vm.fullname = 'Will Smith';
    }, 2000);
</script>

上述代码中,在为fullname属性重新赋值时,Vue.js会自动调用setter,并将新值作为参数传递给set()方法,surname属性和lastname属性会相应进行更新,视图也会随之更新。如果未设置setter而对计算属性重新赋值,就不会触发视图更新。

1.3.3、计算属性缓存

通过上面的示例可以发现,computed选项中的计算属性完全可以用methods选项中的方法代替。例如,使用方法实现获取人物姓名的功能,代码如下:

html 复制代码
<div id="app">
    <span>{{fullname()}}</span>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
    const vm = Vue.createApp({
        data() {
            return {
                surnname: 'Jim',
                lastname: 'Carrey'
            }
        },
        methods: {
            fullname: function() {
                return this.surnname + ' ' + this.lastname;
            }
        }
    }).mount('#app');
    setTimeout(()=> {
        vm.fullname = 'Will Smith';
    }, 2000);
</script>

将相同的操作定义为一个方法,或者定义为一个计算属性,两种方式的结果完全相同。那么为什么还需要计算属性呢?因为计算属性是基于它们的依赖进行缓存的。当页面重新渲染时,如果依赖的数据未发生改变,使用计算属性获取的值就一直是缓存值。只有依赖的数据发生改变时才会重新获取值。如果使用的是方法,在页面重新渲染时,方法中的函数总会被重新调用。

下面通过一个示例来说明计算属性的缓存。代码如下:

html 复制代码
<head>
    <meta charset="UTF-8">
    <title>第一个Vue.js程序</title>
</head>
<body>
<div id="app">
    <input v-model="message">
    <p>{{message}}</p>
    <p>{{getNowTimeC}}</p>
    <p>{{getNowTimeM()}}</p>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
    const vm = Vue.createApp({
        data() {
            return {
                message: '',
                text1: '通过计算属性获取的当前时间:',
                text2: '通过方法获取的当前时间:'
            }
        },
        computed: {
            getNowTimeC: function() {
                let hour = new Date().getHours();
                let minute = new Date().getMinutes();
                let second = new Date().getSeconds();
                return this.text1 + hour + ':' + minute + ':' + second;
            }
        },
        methods: {
            getNowTimeM: function() {
                let hour = new Date().getHours();
                let minute = new Date().getMinutes();
                let second = new Date().getSeconds();
                return this.text1 + hour + ':' + minute + ':' + second;
            }
        }
    }).mount('#app');
    setTimeout(()=> {
        vm.fullname = 'Will Smith';
    }, 2000);
</script>


运行上述代码,页面中会输出一个文本框,下面分别输出通过计算属性和方法获取的当前时间,结果如图6.4所示。在文本框中输入内容后,页面进行了重新渲染,这时,通过计算属性获取的当前时间是缓存的时间,而通过方法获取的当前时间是最新的时间。结果如图所示。

在该示例中,getNowTimeC计算属性依赖于text1属性。当页面重新渲染时,只要text1属性未发生改变,getNowTimeC计算属性就会立即返回之前的计算结果,因此会输出缓存的时间。而在页面重新渲染时,每次调用getNowTimeM()方法总是会再次执行函数,因此会输出最新的时间。

2、监听属性

2.1、监听属性简介

监听属性是Vue.js提供的一种用来监听和响应数据变化的方式。在监听data选项中的属性时,如果监听的属性发生变化,就会执行特定的操作。监听属性可以定义在watch选项中。监听属性对应的函数可以接收一个或两个参数。如果只有一个参数,则该参数表示监听属性的新值;如果有两个参数,第一个参数表示监听属性的新值,第二个参数表示监听属性的原值。

例如,在watch选项中定义监听属性,输出属性的原值和新值,代码如下:

html 复制代码
<div id="app">
    <p>商品名称:{{name}}</p>
    <p>{{text}}</p>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
    const vm = Vue.createApp({
        data() {
            return {
                name: '智能电饭煲',
                price: 999,
                text:  ''
            }
        },
        watch: {
            price(newValue, oldValue) {
                this.text = '原价格:' + oldValue + ' 新价格:' + newValue;
            }
        }
    }).mount('#app');
    setTimeout(()=> {
        vm.price = 333;
    }, 2000);
</script>

上述代码中,在watch选项中对price属性进行了监听。当改变price属性值时,会执行为监听price属性定义的回调函数,函数中有两个参数newValue和oldValue,这两个参数分别表示监听属性的新值和旧值。

2.2、监听属性的应用

监听属性通常用来实现数据之间的换算,如长度单位之间的换算、速度单位之间的换算、汇率之间的换算等。下面通过监听属性实现一个速度换算的实例。

示例:实现速度换算。

应用监听属性实现速度单位"米/秒"和"千米/小时"之间的换算。在文本框中输入要换算的数字,下方会显示换算的结果。代码如下:

html 复制代码
<div id="app">
    <label for="meter">米/秒:</label>
    <input id="meter" type="number" v-model="meter">
    <p>
        <label for="kilometer">千米/小时:</label>
        <input id="kilometer" type="number" v-model="kilometer">
    </p>
    {{meter}}米/秒={{kilometer}}千米/小时
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
    const vm = Vue.createApp({
        data() {
            return {
                meter: 0,
                kilometer: 0
            }
        },
        watch: {
            meter: function(val) {
                this.kilometer = val * 3600 / 1000;
            },
            kilometer: function(val) {
                this.meter = val * 1000 / 3600;
            }
        }
    }).mount('#app');
    setTimeout(()=> {
        vm.price = 333;
    }, 2000);
</script>

2.3、监听对象

如果要监听的属性值是一个对象,要想监听对象内部值的变化,需要在监听属性的选项参数中设置deep选项的值为true。例如,对值是对象的属性进行监听,示例代码如下:

html 复制代码
<div id="app">
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
    const vm = Vue.createApp({
        data() {
            return {
                info: {
                    name: 'Tony',
                    positon: '前端工程师',
                    year: 5
                }
            }
        },
        watch: {
            info: {
                handler: function(val) {
                    alert('员工姓名:' + val.name + '\n新职位:' + val.position + '\n工作年限:' + val.year);
                },
                deep: true
            }
        }
    }).mount('#app');
    setTimeout(()=> {
        vm.info.position = '系统管理员';
    }, 2000);
</script>

当监听的数据是一个数组或者对象时,回调函数中的新值和旧值是相等的,因为这两个形参指向的是同一个数据对象。

3、计算属性和监听属性的比较

监听属性是Vue.js提供的一种用于监测和响应数据变化的更通用的方式。但是,使用监听属性的方式编写的代码是命令式的重复代码,所以在一般情况下,更好的做法是使用计算属性而不是命令式的监听属性。

例如,应用监听属性对人物姓名中的姓和名进行监听,代码如下:

html 复制代码
<div id="app">
    <span>{{fullname}}</span>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
    const vm = Vue.createApp({
        data() {
            return {
                surname: 'Jim',
                lastname: 'Carrey',
                fullname: 'Jim Carrey'
            }
        },
        watch: {
            surname(value) {
                this.fullname = value + ' ' + this.lastname;
            },
            lastname(value) {
                this.fullname = this.surname + ' ' + value;
            }
        }
    }).mount('#app');
</script>

上述代码中,对data选项中定义的surname和lastname属性进行了监听。当其中的一个属性发生变化时,人物姓名也会随之变化。下面使用计算属性对上述代码进行改写,对两种不同的写法进行比较,代码如下:

html 复制代码
<div id="app">
    <span>{{fullname}}</span>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script type="text/javascript">
    const vm = Vue.createApp({
        data() {
            return {
                surname: 'Jim',
                lastname: 'Carrey',
            }
        },
        computed: {
            fullname(value) {
                return this.surname + ' ' + this.lastname;
            },
        }
    }).mount('#app');
</script>

由此可见,使用计算属性同样可以实现响应数据变化的功能。虽然在大多数情况下使用计算属性会更合适,但是如果在响应数据变化时执行异步请求的操作,使用监听属性的方式还是很有用的。

相关推荐
誰在花里胡哨1 天前
Vue<前端页面装修组件>
前端·vue.js
张元清1 天前
Pareto 动态路由实战:[slug]、catch-all、嵌套布局
前端·javascript·面试
fix一个write十个1 天前
NativeWind v4 与 React Native UI Kit或三方库样式隔离指南
前端·react native
前端那点事1 天前
Vue组件通信全场景详解(Vue2+Vue3适配)| 实战必备,新手也能看懂
vue.js
懂懂tty1 天前
React中BeginWork和CompleteWork解析
前端·react.js
_下雨天.1 天前
HAProxy搭建Web群集
前端
梦想CAD控件1 天前
在线CAD开发包图纸转换功能使用指南
前端·javascript·vue.js
费曼学习法1 天前
虚拟 DOM 的 Diff 算法:Vue/React 如何实现高效更新
javascript·vue.js
亚空间仓鼠1 天前
Ansible之Playbook(三):变量应用
java·前端·ansible
invicinble1 天前
前端技术栈整理
前端