Vue的计算属性和侦听器
# Vue的计算属性和侦听器
# 1. 计算属性computed
# 1.1 compute基本使用
如果模板中的表达式存在过多的逻辑,那么模板会变得臃肿不堪,维护起来也异常困难,因此为了简化逻辑出现了计算属性。
一个数据, 依赖另外一些数据计算而来的结果
语法:
computed: {
"计算属性名" () {
return "值"
}
}
- 注意1: 计算属性和data属性都是变量-不能重名. 用法和data相同
- 注意2: 函数内变量变化, 会自动重新计算结果返回
需求:
- 需求1: 求2个数的和显示到页面上
- 需求2: 字符串翻转
<!--这里是VUE3的页面-->
<template>
<div>
<p>和为: {{ num }}</p>
<p>{{ reverseMessage }}</p>
</div>
</template>
<script>
export default {
data() {
return {
a: 10,
b: 20,
message: "我是个字符串",
};
},
computed: {
num() {
return this.a + this.b;
},
reverseMessage() {
return this.message.split("").reverse().join("");
},
},
};
</script>
# 1.2 computed-缓存
计算属性是基于它们的依赖项的值结果进行缓存的,只要依赖的变量不变, 都直接从缓存取结果。
<template>
<div>
<p>{{ reverseMessage }}</p>
<p>{{ reverseMessage }}</p>
<p>{{ reverseMessage }}</p>
<p>{{ getMessage() }}</p>
<p>{{ getMessage() }}</p>
<p>{{ getMessage() }}</p>
</div>
</template>
<script>
export default {
data(){
return {
msg: "Hello, Vue"
}
},
// 计算属性优势:
// 带缓存
// 计算属性对应函数执行后, 会把return值缓存起来
// 依赖项不变, 多次调用都是从缓存取值
// 依赖项值-变化, 函数会"自动"重新执行-并缓存新的值
computed: {
reverseMessage(){
console.log("计算属性执行了");
return this.msg.split("").reverse().join("")
}
},
methods: {
getMessage(){
console.log("函数执行了");
return this.msg.split("").reverse().join("")
}
}
}
</script>
<style>
</style>
总结一下:
- 计算属性根据依赖变量结果缓存, 依赖变化重新计算结果存入缓存, 比普通方法性能更高
# 1.3 computed-完整写法
计算属性也是变量, 如果想要直接赋值, 需要使用完整写法
- 好处: 带缓存,多次调用只执行函数一次
- 缓存更新:函数里的依赖项,发生改变,函数"自动" 重新计算,重新缓存起来
注意:计算属性和data里的变量名都是变量,不能重复
语法:
完整语法:
computed: {
// "计算属性名" (){},
"计算属性名": {
set(值){
},
get(){
return 值
}
}
}
需求:
- 计算属性给v-model使用
<template>
<div>
<div>
<span>姓名:</span>
<input type="text" v-model="full">
</div>
</div>
</template>
<script>
// 问题: 给计算属性赋值 - 需要setter
// 解决:
/*
完整语法:
computed: {
"计算属性名" (){},
"计算属性名": {
set(值){
},
get(){
return 值
}
}
}
*/
export default {
computed: {
full: {
// 给full赋值触发set方法
set(val){
console.log(val)
},
// 使用full的值触发get方法
get(){
return "无名氏"
}
}
}
}
</script>
<style>
</style>
# 1.4 计算机属性小结
- 定义:要用的属性不存在,要通过已有属性计算得来
- 原理:借助了
object.defineproperty()
的getter
和setter
- get()什么时候执行
- 1、初次读取时会执行一次
- 2、当依赖的数据发生改变时会再次被调用
- 优势:与methods相比,内部有缓存机制( 复用 ),效率更高、调试方便
- 注意:
- 1、计算属性最终都会在vm( Vue实例 )上,直接读取即可
- 2、若计算属性要被修改,那必须写set()去响应修改,且set()中要引起计算时依赖的数据发生改变( 例子中没有做这一步,在控制台输出后面再执行一步让依赖数据发生改变就可以了【 使用
value.split('-')
进行拆分嘛,然后把得到的数据对应元素赋给name和sex即可 】 )
- 另外:计算属性是可以简写的
- 就是把set()去掉,因为:更多时候是读取,并不修改,同时修改还要去控制台( 刷新就又没了 ),因此:把set()去掉就是计算属性的简写
- 多提一嘴:react的核心思想就是虚拟DOM,而它的原理我个人认为就是计算属性
# 2. 侦听器watch
# 2.1 computed和watch
computed和watch区别?
watch和computed都是以Vue的依赖追踪机制为基础的,它们都试图处理这样一件事情:当某一个数据(称它为依赖数据)发生变化的时候,所有依赖这个数据的“相关”数据“自动”发生变化,也就是自动调用相关的函数去实现数据的变动。
computed: 计算属性是依赖的值改变会重新执行函数,计算属性是取返回值作为最新结果,所以里面不能异步的返回结果。不能写异步逻辑。计算属性基于响应式依赖进行缓存。如其中的任意一个值未发生变化,它调用的就是上一次计算缓存的数据,因此提高了程序的性能。
watch computed和watch都是依赖数据发生变化后而执行的操作,但是计算属性初始化的时候就可以被监听到并且计算, 但是watch是发生改变的时候才会触发。当你有一些数据需要随着其它数据变动而变动时,或者当需要在数据变化时执行异步或开销较大的操作时,你可以使用 watch。侦听属性是侦听的值改变会重新执行函数,将一个值重新赋值作为最新结果,所以赋值的时候可以进行一些异步操作。
watch和computed各自处理的数据关系场景不同
- 1.watch擅长处理的场景:一个数据影响多个数据
- 2.computed擅长处理的场景:一个数据受多个数据影响
# 2.2 watch基本使用
一个对象,键是需要观察的表达式 (opens new window),值是对应回调函数。值也可以是方法名,或者包含选项的对象。Vue 实例将会在实例化时调用$watch()
,遍历 watch 对象的每一个属性。
<body>
<div id="app">
<input type="text" v-model="num">
</div>
<script src="vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
num: ''
},
watch: {
num(newVal, oldVal) {
// 监听 num 属性的数据变化
// 作用 : 只要 num 的值发生变化,这个方法就会被调用
// 第一个参数 : 新值
// 第二个参数 : 旧值,之前的值
console.log('oldVal:',oldVal)
console.log('newVal:',newVal)
}
}
})
</script>
</body>
# 2.3深度监听怎么做
watch 里面还有一个属性 deep,默认值是 false,代表是否深度监听,设置为true时,可进行深度监听,任何属性发生变化,都可以监听到
deep的意思就是深入观察,监听器会一层层的往下遍历,给对象的所有属性都加上这个监听器,但是这样性能开销就会非常大了,任何修改obj里面任何一个属性都会触发这个监听器里的 handler。但是可以适当优化,我们可以对个别属性进行监听,使用字符串形式监听。 例如:
watch: {
'obj.a': {
handler(newName, oldName) {
// ...
},
// 深度监听
deep: true,
// 立即执行
immediate: true
}
}
<body>
<div id="app">
<input type="button" value="更改名字" @click="change">
</div>
<script src="vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
food: {
id: 1,
name: '冰激凌'
}
},
methods: {
change() {
this.food.name = '棒棒糖'
}
},
watch: {
// 第一种方式:监听整个对象,每个属性值的变化都会执行handler
// 注意:属性值发生变化后,handler执行后获取的 newVal 值和 oldVal 值是一样的
food: {
// 每个属性值发生变化就会调用这个函数
handler(newVal, oldVal) {
console.log('oldVal:', oldVal)
console.log('newVal:', newVal)
},
// 立即处理 进入页面就触发
immediate: true,
// 深度监听 属性的变化
deep: true
},
// 第二种方式:监听对象的某个属性,被监听的属性值发生变化就会执行函数
// 函数执行后,获取的 newVal 值和 oldVal 值不一样
'food.name'(newVal, oldVal) {
console.log('oldVal:', oldVal) // 冰激凌
console.log('newVal:', newVal) // 棒棒糖
}
}
})
</script>
</body>
# 2.4 filter 过滤器
过滤器是数据通过处理后,得到另外的数据。在 vue 中可以定义全局过滤器和组件 过滤器,以下是全局过滤器。
<div id="app">
<h3>定义只有一个参数的 filter 过滤器:</h3>
<div>{{msg}}{{price | RMB}}</div>
<h3>定义多个参数的 filter 过滤器(不传第一个参数):</h3>
<div>{{msg}}{{price | MyRMB(10000,"万")}}</div>
</div>
<script>
//filter 过滤器
Vue.filter("RMB",value => {
value = value.toFixed(2);
return value+"元";
});
//第一个参数是要过滤的处理的数据
Vue.filter("MyRMB",(value,v1,v2) => {
console.log(value,v1,v2);
value = value/v1;
value = value.toFixed(2);
return value+v2;
});
let vue = new Vue({
el: "#app",
data: {
price: 256869.65500,
msg: "人民币:"
}
});
</script>