1. 计算属性computed
1)为什么要使用计算属性?
模板内使用表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如:
<div id='root'>
<span> {{ message.split('').reverse().join('') }} </span>
</div>
上面代码,模板不再是简单的声明式逻辑,当模板中的逻辑较为复杂时,就会难以处理。
2)计算属性的使用
姓名示例:实现输入姓和名,计算全名的效果:
<div id='root'>
姓:<input type="text" v-model="firstName" /><br><br>
名:<input type="text" v-model="lastName" /><br><br>
全名:<span>{{ fullName }}</span>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三'
},
computed: {
fullName: {
get() {
console.log('get被调用了')
//此处this是vm
return this.firstName +'-'+ this.lastName;
},
set(value) {
console.log('set被调用了', value)
const arr = value.split('-');
this.firstName = arr[0];
this.lastName = arr[1];
}
}
}
});
</script>
结果:

原理:底层借助了Object.defineProperty()方法提供的getter和setter。计算属性默认只有 getter,不过在需要时你也可以提供一个 setter。
备注:
- 计算属性最终会出现在vm上,直接读取使用即可
- 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引用计算时依赖的数据发生改变
- 计算属性中的get和set不能使用箭头函数
3)计算属性简写
绝大多数情况下,我们只会用默认的getter 方法来读取一个计算属性,在业务中很少用到setter,所以在声明一个计算属性时,可以直接使用默认的写法,不必将getter 和setter 都声明。
<div id='root'>
姓:<input type="text" v-model="firstName" /><br><br>
名:<input type="text" v-model="lastName" /><br><br>
全名:<span>{{ fullName }}</span>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三'
},
computed: {
fullName() {
console.log('get被调用了')
//此处this是vm
return this.firstName +'-'+ this.lastName;
}
}
});
</script>
4)计算属性缓存 和methods方法
我们可以通过在表达式中调用方法来达到同样的效果。
<div id='root'>
姓:<input type="text" v-model="firstName" /><br><br>
名:<input type="text" v-model="lastName" /><br><br>
全名:<span>{{ fullName }}</span><br><br>
<!-- 全名:<span>{{ fullName() }}</span> -->
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三'
},
methods: {
// fullName() {
// return this.firstName +'-'+ this.lastName;
// }
},
computed: {
fullName() {
console.log('get被调用了')
//此处this是vm
return this.firstName +'-'+ this.lastName;
}
}
});
</script>
我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。
然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 firstName和lastName还没有发生改变,多次访问 fullName, 计算属性会立即返回之前的计算结果,而不必再次执行函数。
相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。
2. 监听属性 watch
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。watch 可以监听数据的变化,并在数据变化时执行所指定的回调函数。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
1)简单变量的监听
天气示例:点击按钮切换天气状态效果:
<div id='root'>
<h2>今天天气很{{ info }}</h2>
<button @click="changeWeather">点击切换天气</button>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
isHot: true
},
computed: {
info() {
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
this.isHot = !this.isHot;
}
},
// 监听属性
watch: {
//监听的属性名
isHot: {
//初始化时是否立即调用handler,默认false
immediate: true,
//handler什么时候调用?isHot发生改变时
handler(newValue, oldValue) {
console.log('isHot被修改了',newValue, oldValue);
}
}
}
});
</script>
其中,handler是监听属性对应的回调函数,第一个参数是监听属性的新值,第二个参数是属性的旧值。immediate控制是否立即执行handler回调函数。
watch可以监听Vue实例上的任何数据的变化,包括简单数据类型,对象,数组和计算属性等。
除了watch选项外,还可以使用命令式的vm.$watch API监听:
//另一种写法
vm.$watch('isHot', {
//初始化时是否立即调用handler,默认false
immediate: true,
//handler什么时候调用?isHot发生改变时
handler(newValue, oldValue) {
console.log('isHot被修改了',newValue, oldValue);
}
});

2)深度监听
A. 监听对象的某个属性。比如监听numbers中a属性的变化:
<div id='root'>
<h3>a的值是:{{ numbers.a }}</h3>
<button @click="numbers.a++">点我+1</button>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
numbers: {
a: 1,
b: 2
}
},
// 监听属性
watch: {
//监听对象属性
'numbers.a': {
handler() {
console.log('a变了')
}
}
}
});
</script>
B. 监听某个对象中所有属性的变化
<div id='root'>
<h3>a的值是:{{ numbers.a }}</h3>
<button @click="numbers.a++">点我+1</button>
<h3>b的值是:{{ numbers.b }}</h3>
<button @click="numbers.b++">点我+1</button>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
numbers: {
a: 1,
b: 2
}
},
// 监听属性
watch: {
//监听对象中所有属性的变化
numbers: {
//深度监听
deep: true, //开启深度监视
handler() {
console.log('numbers变化了')
}
}
}
});
</script>
深度监视:
- vue中的watch默认不监测对象内部值的改变
- 配置 deep:true 可以监听对象内部值的改变(多层)
3)监听属性简写形式
<div id='root'>
<h2>今天天气很{{ info }}</h2>
<button @click="changeWeather">点击切换天气</button>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
isHot: true
},
computed: {
info() {
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
this.isHot = !this.isHot;
}
},
// 监听属性
watch: {
// 简写 - 监听值为处理函数
isHot(newValue, oldValue) {
console.log('isHot被修改了',newValue, oldValue);
}
}
});
</script>
也可用另一种写法:
// 或
vm.$watch('isHot', function(newValue, oldValue) {
console.log('isHot被修改了',newValue, oldValue);
});
