认识侦听器watch
侦听器案例
01_侦听器的基本使用.html
<!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>
</head>
<body>
<div id="app"></div>
<template id="my-app">
您的问题: <input type="text" v-model="question">
<!-- <button @click="queryAnswer">查找答案</button> -->
</template>
<script src="../js/vue.js"></script>
<script>
const App = {
template: '#my-app',
data() {
return {
// 侦听question的变化时, 去进行一些逻辑的处理(JavaScript, 网络请求)
question: "Hello World",
anwser: ""
}
},
watch: {
// question侦听的data中的属性的名称
// newValue变化后的新值
// oldValue变化前的旧值
question: function(newValue, oldValue) {
console.log("新值: ", newValue, "旧值", oldValue);
this.queryAnswer();
}
},
methods: {
queryAnswer() {
console.log(`你的问题${this.question}的答案是哈哈哈哈哈`);
this.anwser = "";
}
}
}
Vue.createApp(App).mount('#app');
</script>
</body>
</html>
侦听器watch的配置选项
02_侦听器的配置选项.html
<!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>
</head>
<body>
<div id="app"></div>
<template id="my-app">
<h2>{{info.name}}</h2>
<button @click="changeInfo">改变info</button>
<button @click="changeInfoName">改变info.name</button>
<button @click="changeInfoNbaName">改变info.nba.name</button>
</template>
<script src="../js/vue.js"></script>
<script>
const App = {
template: '#my-app',
data() {
return {
info: { name: "why", age: 18, nba: {name: 'kobe'} }
}
},
watch: {
// 默认情况下我们的侦听器只会针对监听的数据本身的改变(内部发生的改变是不能侦听)
// info(newInfo, oldInfo) {
// console.log("newValue:", newInfo, "oldValue:", oldInfo);
// },
// 深度侦听/立即执行(一定会执行一次)
info: {
handler: function(newInfo, oldInfo) {
// console.log("newValue:", newInfo, "oldValue:", oldInfo);
// console.log(newInfo == oldInfo)
console.log("newValue:", newInfo.nba.name, "oldValue:", oldInfo.nba.name);
},
deep: true, // 深度侦听
immediate: true // 立即执行
}
},
methods: {
changeInfo() {
this.info = {name: "kobe"};
},
changeInfoName() {
this.info.name = "kobe";
},
changeInfoNbaName() {
this.info.nba.name = "james";
}
}
}
Vue.createApp(App).mount('#app');
</script>
</body>
</html>
侦听器watch的其他方式(一)
侦听器watch的其他方式(二)
03_侦听器的其他方式.html
<!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>
</head>
<body>
<div id="app"></div>
<template id="my-app">
<h2>{{info.name}}</h2>
<button @click="changeInfo">改变info</button>
<button @click="changeInfoName">改变info.name</button>
<button @click="changeInfoNbaName">改变info.nba.name</button>
<button @click="changeFriendName">改变friends[0].name</button>
</template>
<script src="../js/vue.js"></script>
<script>
const App = {
template: '#my-app',
data() {
return {
info: { name: "why", age: 18, nba: {name: 'kobe'} },
friends: [{name: "why"}, {name: "kobe"}]
}
},
watch: {
"info.name": function(newName, oldName) {
console.log(newName, oldName);
},
"friends[0].name": function(newName, oldName) {
console.log(newName, oldName);
}
},
methods: {
changeInfo() {
this.info = {name: "kobe"};
},
changeInfoName() {
this.info.name = "kobe";
},
changeInfoNbaName() {
this.info.nba.name = "james";
},
changeFriendName() {
this.friends[0].name = "curry";
}
},
created() {
// 1.有返回值;2.info要用引号包裹。
const unwatch = this.$watch("info", function(newInfo, oldInfo) {
console.log(newInfo, oldInfo);
}, {
deep: true,
immediate: true
})
// unwatch() // 调用返回值,会取消侦听
}
}
Vue.createApp(App).mount('#app');
</script>
</body>
</html>
综合案例
index.html
<!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>
index.js
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");
style.css
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;
}