1、计算属性computed
对于复杂逻辑我们可以使用计算属性进行处理;
1.1、vue2计算属性介绍
bash
//vue2 index.vue
<template>
<div class="index-main">
<P>我是首页{{title}}</p>
<!-- Hello -->
<div>{{msg}}</div>
<!-- olleH -->
<div>{{reversedMeg}}</div>
</div>
</template>
<script>
export default {
name: 'Home',
data(){
return {
title:'计算属性和侦听器',
msg:'hello',
count:1
}
},
computed:{
//仅读取
reversedMeg: function(){
return this.msg.split('').reverse().join('');
}
},
mounted(){}
}
</script>
计算属性默认只有 getter,不过在需要时你也可以提供一个 setter:
建议:只读取,不修改;以下为html中创建vue实例修改计算属性时对原值的影响
bash
//vue2 index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="app">
<div>a: {{a}}</div>
<div>aPlus: {{aPlus}}</div>
</div>
script src="https://cdn.jsdelivr.net/npm/vue@2.7.16"></script>
<script>
var vm = new Vue({
el: '#app',
components: {},
data:{
a: 3
},
computed:{
aPlus: {
get: function(){
return this.a + 5
},
set: function(newValue){
console.log('newValue',newValue);
this.a = newValue - 2
}
}
},
mounted(){},
methods:{}
})
console.log('vm',vm);
//运行此代码后,a的值将变为0
vm.aPlus = 2;
</script>
</body>
</html>
1.2、vue3计算属性介绍
bash
//vue3 index.vue
<script setup>
import { ref,reactive,computed } from 'vue';
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
function countHandle(){
author.books = [];
//清空书籍时,hasBook会实时更改为No
}
const hasBook= computed(()=>{
return author.books.length>0 ? 'Yes' : 'No'
})
/*
定义方法实现上述计算属性结果
function hasBookHandle(){
return author.books.length>0 ? 'Yes' : 'No'
}
*/
</script>
<template>
<div>是否有书籍:<span>{{hasBook}}</span></div>
<!-- <div>是否有书籍:<span>{{ hasBookHandle() }}</span></div> -->
<button @click="countHandle()">清空书籍</button>
</template>
可写计算属性,避免直接修改计算属性
计算属性默认是只读的。当你尝试修改一个计算属性时,你会收到一个运行时警告。只在某些特殊场景中你可能才需要用到"可写"的属性,你可以通过同时提供 getter 和 setter 来创建:
bash
<script setup>
import { ref,computed } from 'vue';
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed({
/* getter 计算属性的 getter 应只做计算;不要改变其他状态、在 getter 中做异步请求或者更改 DOM */
get() {
return firstName.value + ' ' + lastName.value
},
// setter
set(newValue) {
// 注意:我们这里使用的是解构赋值语法
[firstName.value, lastName.value] = newValue.split(' ')
}
})
const full = computed(()=>{
return firstName.value + ' ' + lastName.value
})
function buttonClick(){
fullName.value = 'Jone Sum'; //无警告及报错
// full.value = 'Jone Sum';//警告提示:computed value is readonly
}
</script>
<template>
<div>全名:<span>{{fullName}}</span></div>
<div>全名:<span>{{full}}</span></div>
<button @click="buttonClick">修改全名</button>
</template>
setter 会被调用而 firstName 和 lastName 会随之更新。
bash
//vue3 index.vue
<script setup>
import { ref,computed } from 'vue';
</script>
<template>
</template>
2、侦听器
2.1、vue2
当需要在数据变化时执行异步或开销较大的操作时建议使用侦听器;
bash
//vue2 index.vue
<template>
<div class="index-main">
<div>count: {{count}}</div>
<div>countDouble: {{countDouble}}</div>
<input v-model="inputVal" placeholder="请输入修改值"/>
<button @click="changeHandle">修改</button>
</div>
</template>
<script>
export default {
name: 'Home',
data(){
return {
count: 3,
inputVal:''
}
},
computed:{
countDouble: function(){
return this.count * 2
}
},
watch:{
/*监听count的变化,点击按钮时,会触发此事件,可获取新修改值及旧值*/
count(newVal,oldVal){
console.log('newVal',newVal);
console.log('oldVal',oldVal);
}
},
methods(){
/*修改count值*/
changeHandle(){
this.count = this.inputVal;
},
}
}
</script>
vue2中depp及immediate的用法
bash
//obj:{a:1,b:2}
watch:{
//immediate设置后 侦听开始之后被立即调用
//设置immediate,不调取changeHandle方法也会触发count侦听器
count:{
handler: function(newVal,oldVal){
console.log('newVal',newVal);
this.sum = this.count*1 + 5
},
immediate: true
},
//调取changeHandle方法时sum修改,触发sum侦听器
sum(x){
console.log('x',x);
},
//使用deep,obj中任意一项修改都会触发侦听器
obj:{
handler: function(val){
console.log('obj',val);
},
deep: true
}
},
methods:{
changeHandle(){
this.count = this.inputVal;
this.obj.a++;
},
}
注意,不应该使用箭头函数来定义 watcher 函数 (例如 searchQuery: newValue => this.updateAutocomplete(newValue))。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,
2.2、vue3
计算属性允许我们声明性地计算衍生值。然而在有些情况下,我们需要在状态变化时执行一些"副作用":例如更改 DOM,或是根据异步操作的结果去修改另一处的状态。
在组合式 API 中,我们可以使用 watch 函数在每次响应式状态发生变化时触发回调函数
bash
//vue3 index.vue
<script setup>
import {ref, computed, watch } from 'vue';
const inputVal = ref('');
/*侦听单个ref*/
watch(inputVal,(newVal,oldVal)=>{
console.log('inputVal',newVal);
})
const x = ref(1);
const y = ref(2);
const sum = ref();
/*getter函数侦听*/
watch(()=> x.value*1 + y.value*1,(val)=>{
//x,y任意一个变量发生变化,都会被侦听
console.log('val',val);
sum.value = val;
})
//多个来源组成的数组
watch([x,()=> y.value],(newVal,oldVal)=>{
console.log('新值',newVal);//[]数组格式,第一个值对应x,第二个值对应y
console.log('旧值',oldVal);
})
watch([x,y],([newX,newY],[oldX, oldY])=>{
console.log('新x:',newX);
console.log('新y:',newY);
console.log('旧x:',oldX);
console.log('旧y:',oldY);
})
function submitHandle(){
// firstName.value = '李';
firstName.value = inputVal.value;
// x.value++;
y.value++
}
</script>
<template>
<div class="index-main">
<input v-model="inputVal" placeholder="请输入新的姓" />
<div>x:{{x}}</div>
<div>y:{{y}}</div>
<div>sum:{{sum}}</div>
<input v-model="x" placeholder="请输入内容" />
<button @click="submitHandle">修改</button>
</div>
</template>
ES6箭头函数
const x = ()=> x.value1 + y.value 1
等价于ES5函数表达式
const x = function(){
return x.value1 + y.value 1
}
响应式对象的监听
bash
//错误写法
const obj = reactive({num:0});
watch( obj.num,(numVal)=>{
console.log('num:',numVal);
})
bash
//正确写法
const obj = reactive({num:0});
watch( ()=> obj.num,(numVal)=>{
console.log('num:',numVal);
})
深层侦听器
当直接传入一个响应式对象,会隐式的创建一个深层侦听器;
例如下面第二个watch的写法,直接侦听obj;
bash
//vue3 index.vue
<script setup>
import {ref, reactive, watch } from 'vue';
const obj = reactive({num:0,age:18});
/*只能监听obj.num,age变化时不会触发*/
watch( () => obj.num,(numVal)=>{
console.log('num:',numVal);
})
watch(obj,(newObj,oldObj)=>{
console.log('obj',newObj,oldObj);
})
function submitHandle(){
// obj.num++;
obj.age++
}
</script>
<template>
<div class="index-main">
<button @click="submitHandle">修改</button>
</div>
</template>
深度侦听需要遍历被侦听对象中的所有嵌套的属性,当用于大型数据结构时,开销很大。因此请只在必要时才使用它,并且要留意性能。
即时回调侦听器
watch 默认是懒执行的:仅当数据源变化时,才会执行回调。但在某些场景中,我们希望在创建侦听器时,立即执行一遍回调。举例来说,我们想请求一些初始数据,然后在相关状态更改时重新请求数据。
我们可以通过传入 immediate: true 选项来强制侦听器的回调立即执行
bash
/*首次加载时sum值为空,只有触发一次submitHandle方法修改x或者y值时sum才会有值*/
watch(()=> x.value*1 + y.value*1,(val)=>{
//x,y任意一个变量发生变化,都会被侦听
console.log('val',val);
sum.value = val;
})
/*传入 immediate: true 选项来强制侦听器的回调立即执行*/
/* **
watch(()=> x.value*1 + y.value*1,(val)=>{
console.log('val',val);
sum.value = val;
},{
immediate: true
})
** */
一次性侦听器
每当被侦听源发生变化时,侦听器的回调就会执行。如果希望回调只在源变化时触发一次,请使用 once: true 选项
bash
watch(()=> x.value*1 + y.value*1,(val)=>{
console.log('val',val);
sum.value = val;
},{
once: true
})
计算属性 VS 侦听器
以下这种情况,建议使用计算属性,使用侦听器时需监听两个变量;
bash
//vue3 index.vue
<script setup>
import { ref, computed, watch } from 'vue';
const firstName = ref('张');
const lastName = ref('三');
const fullName = ref('张三');
/*计算属性*/
const full2 = computed(()=>{
return firstName.value + ' ' + lastName.value
})
/*侦听器*/
watch(firstName, (newVal)=>{
fullName.value = newVal + ' ' + lastName.value
})
watch(lastName, (newVal)=>{
fullName.value = firstName.value + ' ' + newVal
})
function submit(){
firstName.value = '李';
}
function submit2(){
lastName.value = '四';
}
</script>
<template>
<div class="index-main">
<div>firstName:{{firstName}}</div>
<div>lastName:{{lastName}}</div>
<div>FullName:{{fullName}}</div>
<div>full2:{{full2}}</div>
<div>full3:{{full3}}</div>
<button @click="submit">姓修改</button>
<button @click="submit2">名修改</button>
</div>
</template>
计算属性 VS 方法
虽然使用方法可以得到计算属性相同的结果,但是方法无法缓存;
计算属性值会基于其响应式依赖被缓存。一个计算属性仅会在其响应式依赖更新时才重新计算。这意味着只要 author.books 不改变,无论多少次访问 publishedBooksMessage 都会立即返回先前的计算结果,而不用重复执行 getter 函数。
方法调用总是会在重渲染发生时再次执行函数;