watch, computed properties and filters
- 1. watch (listener)
-
- ==1, 1 shallow monitoring==
- 1.2 Deep monitoring
- 1.3 Summary
- 2. filter (filter)
-
- 2.1 Local filter
- 2.2 Global filter
- ==3. computed(computed attribute)==
-
- 3.1 Basic use of computed
- 3.2 Summary
- 3. 3 Shopping cart case
1. watch(listener)
1, 1 shallow monitoring
- Function: Monitor changes in data data
- grammar:
new Vue({
watch:{
name(newValue,oldValue){
//oldValue: old value, the value before changing
//newValue: new value, value after change
//Business code
}
}
})
<!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="./vue.js"></script>
<style>
.box{<!-- -->
margin: 20px;
padding: 20px;
border: 2px solid orangered;
}
.box span{<!-- -->
display: inline-block;
margin: 10px;
padding: 10px;
background: teal;
color: #fff;
cursor: pointer;
}
</style>
</head>
<body>
<div id="app">
<input type="text" v-model="kw">
<div class="box" v-show="kw=='' ">
<span v-for="(item,index) in hots" :key="index" @click=" kw=item ">{<!-- -->{item}}</span>
</div>
</div>
<script>
let vue = new Vue({<!-- -->
el:"#app",
data:{<!-- -->
kw:"", //search keywords
hots: ["Freedom", "The most dazzling national style", "Temptation of the wolf", "The man with the horse pole"]
},
methods:{<!-- -->},
//Listen for changes in data
watch:{<!-- -->
kw(newValue){<!-- -->
//The data has changed and some expensive operations need to be done, ajax
console.log("Initiate ajax request, keyword: " + newValue);
}
}
})
</script>
</body>
</html>
- Case 2—-Baidu search case
Implementation principle:
1.Principle of json
Create a script tag: Initiate a request through the src of the script tag: Add the script tag to the page and receive the returned result through the callback function
2. Layout, initialization
3. Monitor changes in the content of the input box, and initiate a watch request when the change occurs.
4. Initiate a request using jsonp method
5. After obtaining the data, change the value of vue’s arr
6. During the deletion process, if there is no data, an error will be reported when sending a request, and additional judgment will be made.
7. Up and down arrows
- code
<!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="./vue.js"></script>
<style>
.active{<!-- -->
background:#ccc
}
</style>
</head>
<body>
<div id="app">
<p>Please enter search content: <input type="text" v-model="wd" @keydown.down="downFun" @keydown.up="upFun" @keydown.enter="enterFun"></p>
<ul>
<li v-for="(item,index) in arr" :key="item" :class="[index==n? 'active':'']">{<!-- -->{item }}</li>
</ul>
</div>
<script>
let vue = new Vue({<!-- -->
el:"#app",
//1.Initialize data
data:{<!-- -->
wd:"", //keyword
arr:[],//array of searched content
n:0, //Initialization subscript of style
},
methods:{<!-- -->
//6.Down arrow
downFun(){<!-- -->
this.n + + ;
if(this.n >= this.arr.length){<!-- -->
this.n = 0;
}
},
//7.Up arrow
upFun(){<!-- -->
this.n--;
if(this.n < 0){<!-- -->
this.n = this.arr.length -1;
}
},
//8. Enter
enterFun(){<!-- -->
window.open('https://www.baidu.com/s?wd=' + this.arr[this.n])
}
},
//2. Monitor data changes
watch:{<!-- -->
wd(){<!-- -->
//5. Determine if there is value before initiating the request.
if(this.wd == ""){<!-- -->
this.arr = [];
return;
}
//3. Initiate a request to request backend data jsonp
let os = document.createElement("script");
os.src = 'http://suggestion.baidu.com/su?cb=callback & amp;wd=' + this.wd;
document.body.appendChild(os);
}
}
})
//The callback must be a global function
function callback(res){<!-- -->
//4. Change the value of arr of the vue instance. If the data changes, the view will also change accordingly.
vue.arr = res.s;
}
</script>
</body>
</html>
1, 2 deep monitoring
- Function: Monitor changes in reference data in data (array, object)
- grammar
new Vue({
watch:{
//Shallow monitoring
Monitored property name (newValue, oldValue){
//oldValue: old value, the value before changing
//newValue: new value, value after change
//Business code
},
Monitored attribute name: {
handler(new,old){
//The function called when the data changes
},
deep:true/false //If the value of deep is true, depth monitoring
}
}
})
<!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="./vue.js"></script>
</head>
<body>
<div id="app">
<div>Reference data type (object, array): {<!-- -->{user}}</div>
<button @click="change1">Change user</button>
<div>Basic data types (strng, number, boolean): {<!-- -->{name}}</div>
<button @click="change2">Change name</button>
</div>
<script>
new Vue({<!-- -->
el:"#app",
data:{<!-- -->
name:"eat rice",
user:{<!-- -->
username:"xiaoming"
}
},
methods:{<!-- -->
//Change user
change1(){<!-- -->
this.user.username = "daming";
//You can use events instead of deep monitoring
},
//Change NAME
change2(){<!-- -->
this.name = "eating noodles"
}
},
watch:{<!-- -->
//Shallow monitoring
// name(){}
name:{<!-- -->
handler(){<!-- -->
console.log("name changed");
},
deep:false
},
//If you monitor objects or arrays, you need to use deep monitoring
//It is not recommended to use deep monitoring. If there are too many deep monitoring, it will cause the page to freeze. It is recommended to use events instead of deep monitoring.
user:{<!-- -->
handler(){<!-- -->
console.log("user changed");
},
deep:true
}
}
})
</script>
</body>
</html>
watch: monitor changes in data attributes
Shallow monitoring:
watch:{
Attribute name(new,old){
}
If the monitoring is a reference data type, shallow monitoring cannot monitor data changes.
Deep monitoring:
watch:{
Monitored attribute name: {
handler(new,old){
},
deep:true
}
}
It is not recommended to use deep monitoring. Too much deep monitoring will cause the page to freeze. It is recommended to use events instead.
Summary of 1 and 3
- No need to call, automatically called
- Monitoring data changes can be done asynchronously, and if the overhead is relatively large, watch can do it
- If the monitored data is an array or object, you need to use deep monitoring. It is not recommended to use deep monitoring. Too much deep monitoring will cause the page to freeze. It is recommended to use events instead of deep monitoring
2. filter
2, 1 local filter
In most cases, the data returned to us by the backend can be rendered directly, but sometimes, we need to perform secondary operations on the returned data.
example:
Mobile number: 13112345678 --> 131****5678
Time: 1696657349946 --> 2023/10/07
new Vue({
filters:{
Filter name (content to be filtered, parameters passed when calling the filter) {
return returns the converted data result
}
}
})
<p>
{<!-- -->{ Data to convert | Filter name }}
{<!-- -->{ Data to be converted | Filter name (filter parameter) }}
</p>
|pipe character
<!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="./vue.js"></script>
<style>
li{<!-- -->
display: inline-block;
padding: 20px;
margin: 20px;
border: 1px solid orangered;
}
</style>
</head>
<body>
<div id="app">
<!-- Filter usage Data to be filtered | Filter name -->
<ul>
<li v-for="item in goods" :key="item.id">
<p>Product name: {<!-- -->{item.name}}</p>
<!-- <p>Product price: {<!-- -->{item.price | filterTwo(2)}}</p> -->
<p>Product price: {<!-- -->{item.price | filterTwo}}</p>
<p>Item quantity: {<!-- -->{item.num}}</p>
<p>Total product price: {<!-- -->{item.price * item.num | filterTwo(2)}}</p>
<p>User phone number: {<!-- -->{item.tel | filterTel}}</p>
</li>
</ul>
</div>
<div id="app2">
<h1>Telephone number: {<!-- -->{tel | filterTel}}</h1>
</div>
<script>
//Global filter--to be defined before all vue instances, only one can be defined at a time/
//Syntax: Vue.filter('filter name', (data to be filtered, parameters passed)=>{})
Vue.filter("filterTel",(tel)=>{<!-- -->
return tel.slice(0,3) + "****" + tel.slice(7)
})
new Vue({<!-- -->
el:"#app",
data:{<!-- -->
goods:[
{<!-- -->id:1,name:"Litchi",num:5,price:49,tel:'13112345678'},
{<!-- -->id:2,name:"Grape",num:4,price:5.5,tel:'13112345678'},
{<!-- -->id:3,name:"Grapefruit",num:3,price:8.99,tel:'13112345678'},
{<!-- -->id:4,name:"Banana",num:2,price:3.45,tel:'13112345678'}
],
},
methods:{<!-- -->},
//1. Local filter: can only be used in the current vue instance
filters:{<!-- -->
/*
Filter name (content to be filtered, parameters passed when calling the filter) {
return returns the converted data result
}
*/
filterTwo(p,n=2){<!-- --> //p: data that needs to be filtered and does not need to be passed
console.log(p,n);
//Return the processed data
return p.toFixed(n);
},
filterTel(tel){<!-- -->
//'13112345678'
return tel.slice(0,3) + "****" + tel.slice(7)
}
}
})
new Vue({<!-- -->
el:"#app2",
data:{<!-- -->
tel:"15167893245"
}
})
</script>
</body>
</html>
- Note: Local filters can only be used in the current vue instance and cannot be used in other vue instances
2, 2 global filters
- Local filter: can only be used in the current vue instance, other vue instances cannot be used
- Global filter: available to all vue instances before the vue instance is defined
- grammar
Vue.filter("Filter name", (data to be filtered, parameters)=>{ return processed data })
//Global filter--to be defined before all vue instances, only one can be defined at a time/
- Note: Global filters must be defined before all vue instances
- Case
<!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="./vue.js"></script>
</head>
<body>
<div id="app">
<h3>News</h3>
<ul>
<li v-for="item in news" :key="item.id">
<p>City: {<!-- -->{item.city}}</p>
<p>News: {<!-- -->{item.news}}</p>
<p>Date: {<!-- -->{item.time | filterTime}}</p>
</li>
</ul>
</div>
<script>
//Define global filter
Vue.filter("filterTime",(time)=>{<!-- -->
//Convert a certain time format == Create a time object, obtain the corresponding year, month and day, and splice it
let oDate = new Date(time*1); //new Date("2023-10-7") new Date(2023,10,7),new Date(timestamp)
let y = oDate.getFullYear();
let m = (oDate.getMonth() + 1 + "").padStart(2,'0');
let d = (oDate.getDate() + "").padStart(2,'0');
let h = (oDate.getHours() + "").padStart(2,'0');
let mi = (oDate.getMinutes() + "").padStart(2,'0');
let s = (oDate.getSeconds() + "").padStart(2,'0');
return `${<!-- -->y}-${<!-- -->m}-${<!-- -->d} ${<!-- -->h}:$ {<!-- -->mi}:${<!-- -->s}`
})
new Vue({<!-- -->
el:"#app",
data:{<!-- -->
news:[
{<!-- -->id:1,city:"Hangzhou",news:"Hangzhou Asian Games",time:"1696657349946"},
{<!-- -->id:2,city:"Beijing",news:"Beijing hahaha",time:"1696659454316"},
{<!-- -->id:3,city:"Shanghai",news:"Shanghai Hahaha",time:"1697657349946"}
]
},
methods:{<!-- -->},
})
</script>
</body>
</html>
3. computed (computed attribute)
3.1 Basic use of computed
- Function: Calculated attributes
- grammar
new Vue({
computed:{
attribute name(){
//Calculation logic return result
}
}
})
<!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="./vue.js"></script>
</head>
<body>
<div id="app">
<table width="500" border="1">
<thead>
<tr>
<th>Number</th>
<th>Name</th>
<th>Achievements</th>
</tr>
</thead>
<tbody>
<tr v-for="item in students" :key="item.id">
<td>{<!-- -->{item.id}}</td>
<td>{<!-- -->{item.name}}</td>
<td><input type="text" v-model.number="item.score"></td>
</tr>
<tr>
<td>Grade average</td>
<td colspan="2">{<!-- -->{avagScore}}</td>
</tr>
</tbody>
</table>
<h4>Average score method: {<!-- -->{getAvag()}}</h4>
<h4>Average score method: {<!-- -->{getAvag()}}</h4>
<h4>Average score method: {<!-- -->{getAvag()}}</h4>
<h4>Average score method: {<!-- -->{getAvag()}}</h4>
<h4>Method - call once and calculate once</h4>
<h4>Average score calculation attribute: {<!-- -->{avagScore}}</h4>
<h4>Average score calculation attribute: {<!-- -->{avagScore}}</h4>
<h4>Average score calculation attribute: {<!-- -->{avagScore}}</h4>
<h4>Average score calculation attribute: {<!-- -->{avagScore}}</h4>
<h4>Computed attributes - there is a cache, and the calculation will only be called again when the data changes</h4>
</div>
<script>
new Vue({<!-- -->
el: "#app",
data: {<!-- -->
students: [
{<!-- --> id: 1, name: "Liu Bei", score: 99 },
{<!-- --> id: 2, name: "Zhuge Liang", score: 100 },
{<!-- --> id: 3, name: "Sun Ce", score: 50 },
{<!-- --> id: 4, name: "Sima Yi", score: 85 },
{<!-- --> id: 5, name: "Xiao Qiao", score: 70 }
],
},
methods: {<!-- -->
getAvag() {<!-- -->
console.log("Calculated by method");
//Calculate average
let sum = 0;
this.students.forEach(value => {<!-- -->
sum + = value.score;
});
//return average score
return (sum / this.students.length).toFixed(2)
}
},
//mounted(){} Once the page is rendered, you need to calculate the score.
//Computed property: If you need to calculate the value, you will use the calculated property.
//Depends on data: If the data changes, it will be recalculated
computed: {<!-- -->
//Attribute name(){ calculation return calculation result }
avagScore() {<!-- -->
console.log("Computed Property");
//Calculate average
let sum = 0;
this.students.forEach(value => {<!-- -->
sum + = value.score;
});
//return average score
return (sum / this.students.length).toFixed(2)
}
}
})
</script>
</body>
</html>
- Note: Calculated properties are cached, and the calculation will only be re-called when the data changes
3, 2 Summary
- To call, use {{name}} as a normal attribute without adding ()
- Dependent data, if the dependent data changes, the calculation will be called again
- Do synchronization operations
- What is the difference between computed properties and methods?
The same point: their results are determined by dependent data, and will be recalculated when the dependent data changes.
difference:
Computed properties have a cache effect. No matter how many times they are called, the first calculation result will be applied as long as the data remains unchanged.
The method is to call it several times and calculate how many times
- The difference between computed and watch
wacth is one-to-one, monitors changes in an attribute, and performs multiple events and asynchronous operations.
computed many-to-one, that is, you can rely on multiple data, but the result is one, synchronous operation
3, 3 shopping cart cases
- renderings
- code
<!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="./vue.js"></script>
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css">
<style>
img {<!-- -->
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<div id="app" class="container">
<!-- 1. Layout -->
<table class="table table-bordered">
<thead>
<tr>
<th><input type="checkbox" @change="changeAll" v-model="isAll"> Select all</th>
<th>Product information</th>
<th>Unit price of product</th>
<th>Quantity of items</th>
<th>Item Subtotal</th>
<th>Operation</th>
</tr>
</thead>
<tbody>
<tr v-for="(item,index) in goods" :key="item.id">
<td><input type="checkbox" v-model="item.isChecked" @change="changeOne"></td>
<td>
<strong>{<!-- -->{item.name}}</strong>
<img :src="item.img" alt="">
</td>
<td>{<!-- -->{item.price}}</td>
<td>
<button type="button" class="btn btn-defalut" @click="sub(index)">-</button>
{<!-- -->{item.num}}
<button type="button" class="btn btn-primary" @click="add(index)"> + </button>
</td>
<td>{<!-- -->{item.price*item.num}}</td>
<td><button type="button" class="btn btn-danger" @click="delItem(index)">Delete</button></td>
</tr>
<tr>
<td>Total:</td>
<td colspan="5">{<!-- -->{total}}</td>
</tr>
</tbody>
</table>
</div>
<script>
//10. Global filter, retain 2 decimal places
// Backend data
let mock = [
{<!-- -->
id: 1,
img: "https://img10.360buyimg.com/n7/jfs/t1/116325/12/33822/74207/642ec4caF5b53c8a7/8c548ca792f592fc.jpg",
name: "mobile phone",
price: 2999.9,
num: 3
},
{<!-- -->
id: 2,
img: "https://img12.360buyimg.com/n7/jfs/t1/72688/18/26431/128682/6433e218F83aa27a3/6e79bf181755bf44.jpg.avif",
name: "computer",
price: 13145.9,
num: 1
},
{<!-- -->
id: 3,
img: "https://img14.360buyimg.com/n7/jfs/t1/105370/22/32732/131023/646effdaFdff68a3f/ffeecdc7067016bd.jpg.avif",
name: "ipad pro",
price: 5999,
num: 1
}
]
new Vue({<!-- -->
el: "#app",
data: {<!-- -->
goods: [],
isAll:false
},
methods: {<!-- -->
//3. Click Delete to delete the corresponding item
delItem(index){<!-- -->
this.goods.splice(index,1);
},
//4. Click +, quantity + 1
add(index){<!-- -->
//Judge, cannot exceed 5
// this.goods[index].num < 5 ? + + this.goods[index].num : this.goods[index].num = 5
//Boundary value judgment Math.min(4,5)
this.goods[index].num = Math.min(5, + + this.goods[index].num);
},
//5.Click-, quantity-1
sub(index){<!-- -->
//Boundary value judgment Math.min(4,5)
this.goods[index].num = Math.max(1,--this.goods[index].num);
},
//7. Click the Select All button to let all products change status.
changeAll(){<!-- -->
// console.log("Select all changed", this.isAll);
//Change the selection status of all products, the same as selecting all
this.goods.forEach(value=>{<!-- -->
value.isChecked = this.isAll;
});
console.log(this.goods);
},
//8. Click the input of the product. If you select all, the select all button also needs to be selected.
changeOne(){<!-- -->
this.isAll = this.goods.every(value=>{<!-- -->
return value.isChecked == true
});
}
},
//2. Request data--start requesting data after the mounting is completed
mounted(){<!-- -->
//Initiate ajax
// this.goods = mock;
// //6. Give the input tag a checked attribute, but the backend does not give it. Add it yourself after requesting the data.
// this.goods.forEach(value=>{<!-- -->
// value.isChecked=false;
// });
// console.log(this.goods);
//Not defined in data, attributes added later are not responsive
//Process the data first, and then assign it to the attributes in data. In this way, it is defined in data, and there will be responsiveness.
mock.forEach(value=>{<!-- -->
value.isChecked=false;
});
this.goods = mock;
},
//9. Calculate the total price
computed:{<!-- -->
total(){<!-- -->
//Loop +
let sum = 0;
this.goods.forEach(value=>{<!-- -->
//The selected ones +
if(value.isChecked){<!-- -->
sum + = value.price * value.num;
}
});
return sum;
}
}
})
</script>
</body>
</html>