一、computed 计算属性
1、如果 data 要进行复杂转换并在模板中展示,假设直接使用 mustach 语法会臃肿且难以维护,那怎么办呢?
image.png
2、计算属性使用于什么场景?
- 对于任何包含响应式数据的复杂逻辑,你都应该使用【计算属性】
image.png
3、分别使用 mustache、methods、computed 实现一份需求?
image.png
image.png
image.png
4、计算属性 vs methods 有什么优点?计算属性的缓存在什么时候会刷新?
image.png
image.png
5、计算属性如何仅仅只写一个方法,默认是 getter,那么它的完整写法是什么?
computed: {
fullName() {
console.log("computed getFullName")
return this.firstName + this.lastName;
}
}
// 计算属性,使用起来比较优雅,而且能提升性能
computed: {
fullName: {
get : function() {
console.log("computed getFullName")
return this.firstName + this.lastName;
}
}
},
6、从源码的角度,看一下 Vue 对 computed 属性的解析过程,理解也就更加深刻
image.png
image.png
二、 watch 侦听器
1、什么情况下会需要使用侦听器?
- 在某些情况下,我们希望在 【代码逻辑】 中监听某个数据变化,这个时候就需要用【侦听器 watch】 来完成了;
image.png
image.png
2、默认情况下,侦听器只会监听数据本身的改变(数据内部发送的改变是不能被监听到的)对吗?怎么办?
image.png
3、默认情况下,侦听器需要 data 后面值改变了才会生效,如果需要侦听器一进入页面就生效怎么办?
image.png
data: function () {
return {
question: "Hello World",
person: {
firstName: "Kobe",
lastName: "Bryant",
score: 80,
}
}
},
watch: {
person: {
handler: function (newValue, oldValue) {
console.log("newValue=", newValue, " oldValue=", oldValue);
},
deep: true,
immediate: true
}
},
4、如果内部数据监听,即使是深度监听,也会出现下图的问题,就是【新值】和【旧值】相同的问题,为什么呢?
image.png
- 因为是同一个对象,【新值】和【旧值】指向同一块内存区域,所以无法拿到【旧值】
- 也可以理解为是浅拷贝的问题
5、侦听器 watch 的一些其他写法(了解即可)
image.png
image.png
- $watch 的写法,好处就是可以拿到返回值,在需要的时候进行【取消监听】
6、侦听器还可以监听对象的某一属性值,那么可以解决上述遇到的【新值===旧值】的问题吗?
- 可以,监听复杂对象内的具体值,是可以拿到新值和旧值的
image.png
三、 综合案例
1、综合案例需求描述?
image.png
2、代码实现?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div id="app"></div>
<template id="my-app">
<template v-if="books.length > 0">
<table>
<thead>
<th>序号</th>
<th>书籍名称</th>
<th>出版日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>
</thead>
<tbody>
<tr v-for="(book, index) in books">
<td>{{index + 1}}</td>
<td>{{book.name}}</td>
<td>{{book.date}}</td>
<td>{{formatPrice(book.price)}}</td>
<td>
<button :disabled="book.count <= 1" @click="decrement(index)">-</button>
<span class="counter">{{book.count}}</span>
<button @click="increment(index)">+</button>
</td>
<td>
<button @click="removeBook(index)">移除</button>
</td>
</tr>
</tbody>
</table>
<h2>总价格: {{formatPrice(totalPrice)}}</h2>
</template>
<template v-else>
<h2>购物车为空~</h2>
</template>
</template>
<script src="../js/vue.js"></script>
<script src="./index.js"></script>
</body>
</html>
Vue.createApp({
template: "#my-app",
data() {
return {
books: [
{
id: 1,
name: '《算法导论》',
date: '2006-9',
price: 85.00,
count: 1
},
{
id: 2,
name: '《UNIX编程艺术》',
date: '2006-2',
price: 59.00,
count: 1
},
{
id: 3,
name: '《编程珠玑》',
date: '2008-10',
price: 39.00,
count: 1
},
{
id: 4,
name: '《代码大全》',
date: '2006-3',
price: 128.00,
count: 1
},
]
}
},
computed: {
// vue2: filter/map/reduce
totalPrice() {
let finalPrice = 0;
for (let book of this.books) {
finalPrice += book.count * book.price;
}
return finalPrice;
},
// Vue3不支持过滤器了, 推荐两种做法: 使用计算属性/使用全局的方法
filterBooks() {
return this.books.map(item => {
const newItem = Object.assign({}, item);
newItem.price = "¥" + item.price;
return newItem;
})
}
},
methods: {
increment(index) {
// 通过索引值获取到对象
this.books[index].count++
},
decrement(index) {
this.books[index].count--
},
removeBook(index) {
this.books.splice(index, 1);
},
formatPrice(price) {
return "¥" + price;
}
}
}).mount("#app");
table {
border: 1px solid #e9e9e9;
border-collapse: collapse;
border-spacing: 0;
}
th, td {
padding: 8px 16px;
border: 1px solid #e9e9e9;
text-align: left;
}
th {
background-color: #f7f7f7;
color: #5c6b77;
font-weight: 600;
}
.counter {
margin: 0 5px;
}
网友评论