VUE|侦听器
1 侦听器
1) 什么是侦听器
可以通过watch配置项, 监听已经存在的属性的改变
2) 语法
watch: {
// 监听data中的firstName属性
firstName() {
// 执行一系列的操作
},
},watch: {
// 监听data中的firstName属性
firstName() {
// 执行一系列的操作
},
},
示例
<!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>
<script src="../node_modules/vue/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
姓: <input type="text" v-model="lastName" /> <br />
名: <input type="text" v-model="firstName" /> <br />
全名(侦听器实现): {{fullName}}
</div>
<script>
const { createApp } = Vue
const vm = createApp({
data() {
return {
lastName: '',
firstName: '',
fullName: '',
}
},
watch: {
// 侦听lastName的变化, 当lastName变化时, 执行该函数
lastName() {
this.fullName = this.lastName + this.firstName
},
// 侦听firstName的变化, 当firstName变化时, 执行该函数
firstName() {
this.fullName = this.lastName + this.firstName
},
},
}).mount('#app')
</script>
</body>
</html>
3) 特点
在watch对应的回调函数中, 可以获取到新值
和 旧值
示例
const vm = new Vue({
el: '#app',
data: {
firstName: '',
lastName: '',
},
// 使用watch这个配置项
watch: {
// 在watch对应的回调函数中, 可以得到新值和旧值
// 对于简单数据类型, 可以获取新旧值
// 对于引用数据类型, 不能获取旧值
firstName(newValue, oldValue) {
// 一对多: 监听某一个属性的改变, 做一系列的操作
console.log('firstName改变了...')
console.log('新的值:', newValue)
console.log('旧的值:', oldValue)
},
},
})
侦听器的高级用法
- immediate: true => 立即执行watch的回调
- flush: 'post' => 让watch的回调在DOM更新之后执行
- 对watch值类型属性, 可以在watch的回调中获取到新旧值
2 计算属性 VS 侦听器
-
是否会在vm实例中挂载新属性?
-
- computed会
- watch不会
-
对应关系
-
- computed是
多对一
, 可以同时监听多个值改变, 最终计算得到一个新的属性 - watch是
一对多
, 主要监听一个属性的变化, 执行多种逻辑
- computed是
-
能否获取新旧值?
-
- computed不能
- watch能
3 综合作业
需求
实现如下购物车效果
- 数量不能<1
- 修改数量, 总价格会实时计算
- 当所有的商品全部移除时, 显示
购物车中没有商品
<template>
<div class="about">
<table cellspacing="0">
<caption>
<h1>购物车</h1>
<br />
</caption>
<tr>
<th>序号</th>
<th>书籍名称</th>
<th>出版日期</th>
<th>价格</th>
<th>购买数量</th>
<th>操作</th>
</tr>
<tr v-for="(book, index) in books" :key="book.id">
<td>{{ book.id }}</td>
<td>{{ book.name }}</td>
<td>{{ book.time }}</td>
<td>{{ book.cost }}</td>
<td>
<el-button size="small" @click="minusNum(index)"
><el-icon><Minus /></el-icon
></el-button>
<a>{{ book.num }}</a>
<el-button size="small" @click="addNum(index)"
><el-icon><Plus /></el-icon
></el-button>
</td>
<td>
<el-button size="small" @click="clickClear(index)">移除</el-button>
</td>
</tr>
</table>
<div class="summary">汇总金额:{{ total }}元</div>
</div>
</template>
<script setup>
import { ElMessage } from 'element-plus'
import { computed, ref } from 'vue'
const books = ref([
{
id: 1,
name: '《密室收藏家》',
time: '2020.02.09',
cost: '30RMB',
num: '2',
},
{
id: 2,
name: '《东方快车谋杀案》',
time: '2021.12.09',
cost: '40RMB',
num: '7',
},
{
id: 3,
name: '《尼罗河上的惨案》',
time: '2022.10.06',
cost: '50RMB',
num: '5',
},
{
id: 4,
name: '《罗杰疑案》',
time: '2021.10.06',
cost: '45RMB',
num: '8',
},
])
function clickClear(index) {
this.books.splice(index, 1)
}
function addNum(index) {
this.books[index].num++
}
function minusNum(index) {
if (this.books[index].num <= 0) {
this.books[index].num = 0
ElMessage({
message: '书本数量不能为负数!',
type: 'warning',
})
} else {
this.books[index].num--
}
}
const total = computed(() => {
let sum_money = 0
books.value.forEach((element) => {
let money = parseFloat(element.cost)
let number = parseFloat(element.num)
sum_money += money * number
})
return sum_money
})
</script>
<style>
table tbody {
display: block;
height: 390px;
width: 100%;
overflow-y: scroll;
}
table {
width: 100%;
}
tr {
border-top: 1px solid #c1c3d1;
border-bottom: 1px solid #c1c3d1;
color: #666b85;
font-size: 15px;
font-weight: normal;
text-shadow: 0 1px 1px rgba(256, 256, 256, 0.1);
width: 100%;
}
tr:hover td {
background: #4e5066;
color: #ffffff;
border-top: 1px solid #22262e;
}
tr:first-child {
border-top: none;
}
tr:last-child {
border-bottom: none;
}
tr:nth-child(odd) td {
background: #ebebeb;
}
tr:nth-child(odd):hover td {
background: #4e5066;
}
tr:last-child td:first-child {
border-bottom-left-radius: 3px;
}
tr:last-child td:last-child {
border-bottom-right-radius: 3px;
}
td {
width: 300px;
background: #ffffff;
padding: 5px;
text-align: center;
vertical-align: middle;
font-weight: 300;
font-size: 15px;
text-shadow: -1px -1px 1px rgba(0, 0, 0, 0.1);
border-right: 1px solid #c1c3d1;
white-space: nowrap;
}
td a {
margin-left: 5px;
margin-right: 5px;
font-weight: 300;
width: 20px;
display: inline-block;
}
td:last-child {
border-right: 0px;
}
.summary {
margin-top: 10px;
text-align: right;
color: #666b85;
font-size: 15px;
font-weight: bold;
text-shadow: 0 1px 1px rgba(256, 256, 256, 0.1);
}
</style>