计算属性
在某些情况,我们可能需要对数据进行一些转化后再显示,或者需要将多个数据结合起来进行显示
对于任何包含响应式数据的复杂交互逻辑,都应该使用计算属性
计算属性会被混入到组件实例中,计算属性的getter和setter中的this会自动被绑定为当前的组件实例对象
模板中书写表达式
<template id="template">
<h2>{{ `${user.firstName} ---- ${user.lastName}` }}</h2>
<h2>{{ `${user.firstName} ---- ${user.lastName}` }}</h2>
<h2>{{ `${user.firstName} ---- ${user.lastName}` }}</h2>
</template>
<script>
Vue.createApp({
template: '#template',
data() {
return {
user: {
firstName: 'Klaus',
lastName: 'Wang'
}
}
}
}).mount('#app')
</script>
缺点:
-
模板中存在大量的复杂逻辑,不便于维护(模板中表达式的初衷是用于简单的计算)
-
当有多次一样的逻辑时,存在重复的代码
-
多次使用的时候,很多运算也需要多次执行,没有缓存;
methods中的实现
<template id="template">
<h2>{{ getFullname() }}</h2>
<h2>{{ getFullname() }}</h2>
<h2>{{ getFullname() }}</h2>
</template>
<script>
Vue.createApp({
template: '#template',
data() {
return {
user: {
firstName: 'Klaus',
lastName: 'Wang'
}
}
},
methods: {
getFullname() {
return `${this.user.firstName} ---- ${this.user.lastName}`
}
}
}).mount('#app')
</script>
缺点:
- 我们仅仅只是想要在界面上展示对应的值,但是变成了多次的方法调用
- 多次使用方法的时候,没有缓存,也需要多次计算
computed
<template id="template">
<h2>{{ fullName }}</h2>
<h2>{{ fullName }}</h2>
<h2>{{ fullName }}</h2>
</template>
<script>
Vue.createApp({
template: '#template',
data() {
return {
user: {
firstName: 'Klaus',
lastName: 'Wang'
}
}
},
computed: {
fullName() {
return `${this.user.firstName} ---- ${this.user.lastName}`
}
}
}).mount('#app')
</script>
计算属性 VS Methods
计算属性是有缓存的
计算属性只有在
初次调用
或者依赖发生改变
的时候 才会进行计算
<template id="template">
<h2>{{ fullName }}</h2>
<h2>{{ fullName }}</h2>
<h2>{{ fullName }}</h2>
<h2>{{ getFullname() }}</h2>
<h2>{{ getFullname() }}</h2>
<h2>{{ getFullname() }}</h2>
</template>
<script>
Vue.createApp({
template: '#template',
data() {
return {
user: {
firstName: 'Klaus',
lastName: 'Wang'
}
}
},
computed: {
fullName() {
return `${this.user.firstName} ---- ${this.user.lastName}`
}
},
methods: {
getFullname() {
return `${this.user.firstName} ---- ${this.user.lastName}`
}
}
}).mount('#app')
</script>
计算属性本质上是一个对象
完整写法
computed: {
fullName: {
get() {
return `${this.user.firstName} ---- ${this.user.lastName}`
},
set(v) {
const names = v.split(' --- ')
this.firstName = names[0].trim()
this.lastName = names[1].trim()
}
}
}
methods: {
// 写成方法的时候,其实是计算属性的语法糖,即只实现了get方法,而没有实现set方法
getFullname() {
return `${this.user.firstName} ---- ${this.user.lastName}`
}
}
watch
开发中我们在data返回的对象中定义了数据,这个数据通过插值语法等方式绑定到template中
当数据变化时,template会自动进行更新来显示最新的数据
但是在某些情况下,我们希望在代码逻辑中监听某个数据的变化,并实现相应的处理逻辑
,
这个时候就需要用侦听器watch来完成
watch: {
// 参数1: 新值 参数2: 修改之前的旧值
fullName(newV, oldV) {
console.log(newV, oldV)
}
}
配置选项
watch: {
fullName: {
// 深度监听,默认vue对于复杂数据类型只会对引用地址进行监听
// 所以当一个对象的属性发生改变的时候,是没有办法被监听到的
// 如果需要被监听到,就必须使用深度监听
deep: true,
// 监听器的执行是在渲染完毕以后,对应值发生改变的时候
// 如果我们希望初次渲染的时候,即刻执行,可以设置immediate为true
// 此时侦听器就会在初次渲染的时候被执行(也就意味着oldV的值是undefined)
immediate: true,
handler(newV, oldV) {
console.log(newV, oldV)
}
}
}
其它写法
字符串形式
watch: {
// 即将handler函数抽离到methods中进行定义
fullName: 'handleWatch'
},
methods: {
handleWatch(newV, oldV) {
console.log(newV, oldV)
}
}
数组写法
watch: {
fullName: [
'handlefullNameChange',
// vue会自动遍历数组中的值并执行,所以这里的函数可以是一个匿名函数
function(newV, oldV) {
console.log(newV, oldV)
},
{
immediate: true,
handler(newV, oldV) {
console.log('immediate', newV, oldV)
}
}
]
},
监听属性
watch: {
'user.name'(newV, oldV) {
console.log('immediate', newV, oldV)
}
}
但是vue是无法监听数组对象成员的变化的
// 下面这种写法是错误的, 值会发生改变,但是并不会触发监听操作
watch: {
'user[0].name'(newV, oldV) {
console.log('immediate', newV, oldV)
}
}
深度监听对象
Vue.createApp({
template: '#template',
data() {
return {
users: [
{name:'Klaus'},
{name:'Steven'},
{name:'James'}
]
}
},
watch: {
users: {
deep: true,
handler(newV, oldV) {
// 在引用数据类型的监听中 oldV是没有任何的意义的,
// 对于引用数据类型中的旧值和新值, 仅仅只是引用地址上的赋值,并没有做浅拷贝或深拷贝
console.log(newV === oldV) // => true
}
}
},
methods: {
handleClick() {
this.users[0].name = 'Alex'
}
}
}).mount('#app')
$watch
created() {
// $watch 的设置方式和在watch option中的设置 效果是一致的
// 这里第二个handler函数可以是箭头函数,因为外层作用域是created函数,其内部this就是当前组件data的proxy对象
// $watch 返回一个取消侦听函数,用来停止监听
this.unwatch = this.$watch('fullName', (newV, oldV) => console.log(newV, oldV), {
immediate: true,
deep: true
})
},
beforeUnmount() {
this.unwatch()
}
Tips: vue3已经移除了侦听器,可以使用计算属性,方法(定义在组件中的局部方法或定义在app.config.globalProperies的全局方法)替代
// 使用计算属性替换过滤器,来为价格前面加上¥
filterBooks() {
return this.books.map(item => {
// 为了避免需要展示的数据污染旧的实际进行逻辑判断的数据,
// 需要对这些复杂类型数据进行浅拷贝
const newItem = Object.assign({}, item);
newItem.price = "¥" + item.price;
return newItem;
})
}