各位观众老爷,大家好!今天咱就来聊聊 Vue 里那些个“算计”和“偷听”的门道,看看怎么用计算属性和侦听器,把复杂的表单和购物车玩得溜溜的。
开场白:表单界的“华容道”和购物车里的“俄罗斯方块”
咱们搞前端的,谁还没跟表单和购物车打过交道啊?那玩意儿,看着简单,真要细抠起来,数据联动、实时校验,那简直就是表单界的“华容道”,购物车里的“俄罗斯方块”,一步走错,满盘皆输。
别怕!Vue 早就给咱准备好了两大法宝:计算属性 (computed properties) 和侦听器 (watchers)。这俩玩意儿,一个擅长“算计”,一个精于“偷听”,配合起来,保准把你的表单和购物车安排得明明白白的。
第一节:计算属性:数据“算计”大师
计算属性,顾名思义,就是通过计算得来的属性。它就像一个“懒人计算器”,只要依赖的数据一变,它就会自动重新计算,并把结果缓存起来。下次再访问的时候,直接拿缓存,省时省力。
1.1 计算属性的基本用法
先来个简单的例子:
<template>
<div>
<p>单价:{{ price }}</p>
<p>数量:{{ quantity }}</p>
<p>总价:{{ totalPrice }}</p>
</div>
</template>
<script>
export default {
data() {
return {
price: 10,
quantity: 2,
};
},
computed: {
totalPrice() {
return this.price * this.quantity;
},
},
};
</script>
在这个例子里,totalPrice
就是一个计算属性。它依赖于 price
和 quantity
这两个数据。只要 price
或 quantity
发生变化,totalPrice
就会自动重新计算。
1.2 计算属性的 Getter 和 Setter
计算属性默认只有 getter,也就是只能读取,不能设置。但如果你想让计算属性也能被设置,就可以使用 getter 和 setter。
<template>
<div>
<p>全名:{{ fullName }}</p>
<input v-model="fullName" />
</div>
</template>
<script>
export default {
data() {
return {
firstName: "张",
lastName: "三",
};
},
computed: {
fullName: {
get() {
return this.firstName + " " + this.lastName;
},
set(newValue) {
const names = newValue.split(" ");
this.firstName = names[0];
this.lastName = names[names.length - 1];
},
},
},
};
</script>
在这个例子里,fullName
既可以读取,也可以设置。当你修改 fullName
的值时,firstName
和 lastName
也会随之改变。
1.3 计算属性在表单中的应用
假设你有一个表单,需要根据用户输入的年龄来判断他是否成年。
<template>
<div>
<label>年龄:</label>
<input type="number" v-model.number="age" />
<p>是否成年:{{ isAdult }}</p>
</div>
</template>
<script>
export default {
data() {
return {
age: 18,
};
},
computed: {
isAdult() {
return this.age >= 18 ? "是" : "否";
},
},
};
</script>
在这个例子里,isAdult
会根据 age
的值自动判断用户是否成年。
1.4 计算属性在购物车中的应用
假设你有一个购物车,需要计算商品的总价和数量。
<template>
<div>
<ul>
<li v-for="item in cart" :key="item.id">
{{ item.name }} - {{ item.price }} x {{ item.quantity }}
</li>
</ul>
<p>总价:{{ totalPrice }}</p>
<p>总数量:{{ totalQuantity }}</p>
</div>
</template>
<script>
export default {
data() {
return {
cart: [
{ id: 1, name: "苹果", price: 5, quantity: 2 },
{ id: 2, name: "香蕉", price: 3, quantity: 3 },
],
};
},
computed: {
totalPrice() {
return this.cart.reduce((total, item) => total + item.price * item.quantity, 0);
},
totalQuantity() {
return this.cart.reduce((total, item) => total + item.quantity, 0);
},
},
};
</script>
在这个例子里,totalPrice
和 totalQuantity
会根据 cart
里的商品信息自动计算总价和总数量。
第二节:侦听器:数据“偷听”达人
侦听器,顾名思义,就是用来侦听数据变化的。它就像一个“情报员”,时刻关注着指定的数据,一旦发现变化,就会立即执行相应的操作。
2.1 侦听器的基本用法
<template>
<div>
<p>年龄:{{ age }}</p>
<button @click="age++">增长年龄</button>
</div>
</template>
<script>
export default {
data() {
return {
age: 18,
};
},
watch: {
age(newValue, oldValue) {
console.log(`年龄从 ${oldValue} 变成了 ${newValue}`);
},
},
};
</script>
在这个例子里,watch
对象中的 age
属性就是一个侦听器。它会侦听 age
数据的变化,并在 age
发生变化时,执行相应的回调函数。回调函数会接收两个参数:newValue
和 oldValue
,分别表示新的值和旧的值。
2.2 侦听器的选项:deep
和 immediate
侦听器还有两个常用的选项:deep
和 immediate
。
-
deep
: 用于侦听对象内部属性的变化。默认情况下,侦听器只能侦听对象本身的引用是否发生变化,而不能侦听对象内部属性的变化。如果需要侦听对象内部属性的变化,就需要将deep
设置为true
。 -
immediate
: 用于在组件初始化时立即执行一次回调函数。默认情况下,侦听器只会在数据发生变化时执行回调函数。如果需要在组件初始化时立即执行一次回调函数,就需要将immediate
设置为true
。
2.3 侦听器在表单中的应用
假设你有一个表单,需要根据用户输入的邮箱地址来判断邮箱格式是否正确。
<template>
<div>
<label>邮箱:</label>
<input type="email" v-model="email" />
<p v-if="emailError">{{ emailError }}</p>
</div>
</template>
<script>
export default {
data() {
return {
email: "",
emailError: "",
};
},
watch: {
email(newValue) {
if (!newValue) {
this.emailError = "";
return;
}
const emailRegex = /^[^s@]+@[^s@]+.[^s@]+$/;
if (!emailRegex.test(newValue)) {
this.emailError = "邮箱格式不正确";
} else {
this.emailError = "";
}
},
},
};
</script>
在这个例子里,email
侦听器会侦听 email
数据的变化,并根据邮箱格式是否正确来设置 emailError
的值。
2.4 侦听器在购物车中的应用
假设你有一个购物车,需要在商品数量发生变化时,更新总价和总数量。
<template>
<div>
<ul>
<li v-for="item in cart" :key="item.id">
{{ item.name }} - {{ item.price }} x
<input type="number" v-model.number="item.quantity" />
</li>
</ul>
<p>总价:{{ totalPrice }}</p>
<p>总数量:{{ totalQuantity }}</p>
</div>
</template>
<script>
export default {
data() {
return {
cart: [
{ id: 1, name: "苹果", price: 5, quantity: 2 },
{ id: 2, name: "香蕉", price: 3, quantity: 3 },
],
};
},
computed: {
totalPrice() {
return this.cart.reduce((total, item) => total + item.price * item.quantity, 0);
},
totalQuantity() {
return this.cart.reduce((total, item) => total + item.quantity, 0);
},
},
watch: {
cart: {
handler() {
// 触发 totalPrice 和 totalQuantity 的重新计算
// 这里只是为了演示,实际上 totalPrice 和 totalQuantity 会自动更新
console.log("购物车数据已更新");
},
deep: true,
},
},
};
</script>
在这个例子里,cart
侦听器会侦听 cart
数据的变化,并在 cart
里的商品数量发生变化时,触发 totalPrice
和 totalQuantity
的重新计算。 由于cart
是一个对象数组,所以要使用 deep: true
才能侦听到数组内部对象属性的变化。
第三节:计算属性和侦听器的选择
既然计算属性和侦听器都能实现数据的实时联动和校验,那什么时候该用哪个呢?
一般来说,可以遵循以下原则:
- 如果一个数据的变化会导致另一个数据的变化,并且这个变化是同步的,那么可以使用计算属性。 比如,总价是单价和数量的乘积,那么总价就可以使用计算属性来计算。
- 如果一个数据的变化会导致一些副作用,比如发送网络请求、修改 DOM 元素等,那么可以使用侦听器。 比如,当用户输入邮箱地址时,需要验证邮箱格式是否正确,并显示错误提示,那么就可以使用侦听器来实现。
- 如果需要侦听对象内部属性的变化,那么必须使用侦听器,并且需要将
deep
设置为true
。
简单来说,计算属性擅长“计算”,侦听器擅长“观察”。
第四节:进阶技巧:lodash.debounce
和 lodash.throttle
在某些情况下,数据的变化可能会非常频繁,导致计算属性或侦听器被频繁调用,影响性能。这时,可以使用 lodash.debounce
和 lodash.throttle
来优化性能。
-
lodash.debounce
: 用于防抖。它可以将多次连续的调用合并成一次调用。比如,当用户在输入框中输入内容时,可以使用lodash.debounce
来延迟发送网络请求,避免频繁发送请求。 -
lodash.throttle
: 用于节流。它可以限制函数的调用频率。比如,当用户滚动页面时,可以使用lodash.throttle
来限制某些函数的调用频率,避免页面卡顿。
4.1 使用 lodash.debounce
<template>
<div>
<label>搜索:</label>
<input type="text" v-model="searchText" />
</div>
</template>
<script>
import { debounce } from "lodash";
export default {
data() {
return {
searchText: "",
};
},
watch: {
searchText(newValue) {
this.debouncedSearch(newValue);
},
},
mounted() {
this.debouncedSearch = debounce(this.search, 500); // 500ms 防抖
},
methods: {
search(text) {
console.log("发送搜索请求:", text);
// 模拟发送搜索请求
},
},
};
</script>
在这个例子里,debouncedSearch
函数使用了 lodash.debounce
来进行防抖。只有在用户停止输入 500 毫秒后,才会发送搜索请求。
4.2 使用 lodash.throttle
<template>
<div>
<p>滚动距离:{{ scrollY }}</p>
</div>
</template>
<script>
import { throttle } from "lodash";
export default {
data() {
return {
scrollY: 0,
};
},
mounted() {
window.addEventListener("scroll", this.throttledUpdateScrollY);
this.throttledUpdateScrollY = throttle(this.updateScrollY, 200); // 200ms 节流
},
beforeDestroy() {
window.removeEventListener("scroll", this.throttledUpdateScrollY);
},
methods: {
updateScrollY() {
this.scrollY = window.scrollY;
},
},
};
</script>
在这个例子里,throttledUpdateScrollY
函数使用了 lodash.throttle
来进行节流。每 200 毫秒才会更新一次 scrollY
的值。
第五节:总结:让数据联动飞起来
今天,咱们一起学习了 Vue 中计算属性和侦听器的用法,以及它们在表单和购物车中的应用。希望大家能够灵活运用这两种工具,让你的数据联动飞起来,让你的用户体验更上一层楼!
记住,计算属性擅长“计算”,侦听器擅长“观察”。选择合适的工具,才能事半功倍。
表格总结:
特性 | 计算属性 (Computed Properties) | 侦听器 (Watchers) |
---|---|---|
用途 | 计算并缓存派生数据 | 响应数据变化执行副作用 |
触发 | 依赖的数据变化 | 被侦听的数据变化 |
执行 | 同步 | 异步 (可以) |
返回值 | 必须返回一个值 | 无需返回值 |
场景 | 总价计算、过滤列表 | 数据校验、发送请求、修改 DOM |
性能优化 | 缓存结果,依赖不变不重新计算 | 可以使用 debounce 和 throttle |
是否可以修改 | 可以通过 setter 修改 | 不能直接修改被侦听的数据 |
各位,下次再见! 希望这些知识能帮助你构建更强大的 Vue 应用。