目录
一、计算属性
1.基本使用
现在有变量姓氏和名字,要得到完整的名字。
<div id="app">
<h2>您的firstname:{{firstName}}</h2>
<h2>您的lastname:{{lastName}}</h2>
<h2>您的fullname是从计算属性中得到:{{fullName}}</h2>
<h2>您的fullname是从方法中得到:{{getFullName()}}</h2>
</div>
<script>
Vue.config.productionTip = false; //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el: '#app',
data(){
return{
firstName:'zhang',
lastName:'san'
}
},
methods: {
getFullName(){
return this.firstName + this.lastName
}
},
computed:{//计算属性 一定要有一个返回值 在页面中渲染时是不需要加小括号的
fullName(){
return this.firstName + this.lastName
}
}
})
</script>
使用Mastache语法拼接
<h2>{{firstName}}{{lastName}}</h2>
使用方法methods
<h2>{{getFullName()}}</h2>
使用计算属性computed
<h2>{{fullName}}</h2>
例子中计算属性computed看起来和方法似乎一样,只是方法调用需要使用(),而计算属性不用,方法取名字一般是动词见名知义,而计算属性是属性是名词,但这只是基本使用。
2.复杂使用
现在有一个数组数据books,里面包含许多book对象,数据结构如下:
books:[ {id:110,name:"JavaScript从入门到入土",price:119}, {id:111,name:"Java从入门到放弃",price:80}, {id:112,name:"编码艺术",price:99}, {id:113,name:"代码大全",price:150}, ]
要求计算出所有book的总价格totalPrice
。
<div id="app">
<h2>您的总价:{{totalPrice}}</h2>
</div>
<script>
const vm = new Vue({
el: '#app',
data() {
return {
books: [{
id: 110,
name: "JavaScript从入门到入土",
price: 119
},
{
id: 111,
name: "Java从入门到放弃",
price: 80
},
{
id: 112,
name: "编码艺术",
price: 99
},
{
id: 113,
name: "代码大全",
price: 150
},
]
}
},
computed: {
/* totalPrice() {
let total = 0
for (let i = 0; i < this.books.length; i++) {
total += this.books[i].price
}
return total
} */
/* totalPrice() {
let total = 0
for (let index in this.books) {
total += this.books[index].price
}
return total
} */
/* totalPrice() {
let total = 0;
for(let item of this.books){
total += item.price
}
return total
} */
/* totalPrice() {
let total = 0;
this.books.forEach(item=>{
total += item.price
})
return total
} */
/* map */
/* totalPrice() {
let total = 0;
this.books.map(item=>{
total += item.price
})
return total
} */
/* filter */
/* totalPrice() {
let total = 0;
this.books.filter(item=>{
total += item.price
})
return total
} */
/* reduce */
/* totalPrice() {
return this.books.reduce((total,item)=>{
return total + item.price
},0)
} */
/* totalPrice() {
return this.books.reduce((total,item)=>total + item.price,0)
} */
/* some */
totalPrice() {
let total = 0;
this.books.some(item=>{
total += item.price
})
return total
}
}
})
</script>
获取每一个book对象的price累加,当其中一个book的价格发生改变时候,总价会随之变化。
3.getter和setter
在计算属性中其实是由这样两个方法setter和getter。
但是计算属性一般没有set方法,只读属性,只有get方法,但是上述中newValue就是新的值,也可以使用set方法设置值,但是一般不用。
<div id="app">
<input type="text" v-model="firstName"/><br>
<input type="text" v-model="lastName"/><br>
<input type="text" v-model="fullName"/>
<!-- v-model实现数据的双向绑定 -->
<!-- 2.双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data。
备注:
1.双向绑定一般都应用在表单类元素上(如:input、select等)
2.v-model:value 可以简写为 v-model,因为v-model默认收集的就
是value值。 -->
</div>
<script>
const vm = new Vue({
el: '#app',
data() {
return {
firstName: 'zhang',
lastName: 'san'
}
},
computed: { //计算属性 一定要有一个返回值 在页面中渲染时是不需要加小括号的
/* fullName(){
return this.firstName + this.lastName
} */
fullName: {
get: function() {
return this.firstName +','+ this.lastName
},
set:function(val){
var list = val.split(',')
console.log(list);
this.firstName = list[0]
this.lastName = list[1]
}
}
}
})
</script>
通过这种方式,我们可以在改变计算属性值的同时也改变和计算属性相关联的属性值。
4.计算属性和methods的对比
<div id="app">
<h2>您的fullname是从计算属性中得到:{{fullName}}</h2>
<h2>您的fullname是从计算属性中得到:{{fullName}}</h2>
<h2>您的fullname是从计算属性中得到:{{fullName}}</h2>
<h2>您的fullname是从计算属性中得到:{{fullName}}</h2>
<h2>您的fullname是从方法中得到:{{getFullName()}}</h2>
<h2>您的fullname是从方法中得到:{{getFullName()}}</h2>
<h2>您的fullname是从方法中得到:{{getFullName()}}</h2>
<h2>您的fullname是从方法中得到:{{getFullName()}}</h2>
</div>
<script>
const vm = new Vue({
el:'#app',
data(){
return {
firstName:'zhang',
lastName:'san'
}
},
methods:{
getFullName(){
console.log('这里调用了方法getFullName');
return this.firstName + this.lastName
}
},
computed:{//计算属性 一定要有一个返回值 在页面中渲染时是不需要加小括号的
fullName(){
console.log('这里调用了计算属性fullName');
return this.firstName + this.lastName
}
}
})
/* 总结:计算属性的性能是优于方法的 因为计算属性是具有缓存特性的 */
</script>
由此可见计算属性有缓存,在
this.firstName + " " + this.lastName
的属性不变的情况下,methods调用了四次,而计算属性才调用了一次,性能上计算属性明显比methods好。而且在改动firstName的情况下,计算属性只调用一次,methods依然要调用4次。
二、侦听器
1.watch的用法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue计算属性/侦听器/方法比较</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>计算属性:computed</h1>
{{fullName}}
<h1>方法:methods</h1>
{{fullName2()}}
<h1>侦听器:watch</h1>
{{watchFullName}}
<h1>年龄</h1>
{{age}}
</div>
<script>
var other = 'This is other';
var app = new Vue({
el:"#app",
data:{
firstName:"zhang",
lastName:"san",
watchFullName:"zhangsan",
age:18,
},
watch: {
firstName:function(newFirstName, oldFirstName){
console.log("firstName触发了watch,newFirstName="+newFirstName+",oldFirstName="+oldFirstName)
this.watchFullName = this.firstName+this.lastName+","+other
},
lastName:function(newLastName, oldLastName){
console.log("lastName触发了watch,newLastName="+newLastName+",oldLastName="+oldLastName)
this.watchFullName = this.firstName+this.lastName+","+other
}
},
computed: {
fullName:function(){
console.log("调用了fullName,计算了一次属性")
return this.firstName+this.lastName+","+other;
}
},
methods: {
fullName2:function(){
console.log("调用了fullName,执行了一次方法")
fullName2 = this.firstName+this.lastName+","+other;
return fullName2;
}
}
});
</script>
</body>
</html>
初始化:
修改firstName/lastName/两者都修改
修改computed中没计算的age
修改Vue实例外的对象
修改Vue实例外对象后在修改Vue实例内的对象
测试结论:
- 使用computed计算了fullName属性,值为firstName+lastName。计算属性具有
缓存功能
,当firstName和lastName都不改变的时候,fullName不会重新计算,比如我们改变age的值,fullName的值是不需要重新计算的。- methods并没有缓存特性,比如我们改变age的值,fullName2()方法会被执行一遍。
- 当一个功能可以用上面三个方法来实现的时候,明显使用computed更合适,代码简单也有缓存特性。
- 计算属性范围在vue实例内,修改vue实例外部对象,不会重新计算渲染,但是如果先修改了vue实例外对象,在修改vue计算属性的对象,那么外部对象的值也会重新渲染。
计算属性:computed
计算属性范围在Vue实例的fullName内所管理的firstName和lastName,通常监听多个变量 1111
侦听器:watch
监听数据变化,一般只监听一个变量或数组
使用场景
watch(异步场景
),computed(数据联动
) watch可以在所监听的数据后面直接加字符串形式的方法名
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue计算属性/侦听器/方法比较</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
</head>
<body>
<div id="app">
<h1>计算属性:computed</h1>
{{fullName}}
<h1>方法:methods</h1>
{{fullName2()}}
<h1>侦听器:watch</h1>
{{watchFullName}}
<h1>年龄</h1>
{{age}}
</div>
<script>
var other = 'This is other';
var app = new Vue({
el:"#app",
data:{
firstName:"zhang",
lastName:"san",
watchFullName:"zhangsan",
age:18,
},
watch: {
firstName:function(newFirstName, oldFirstName){
console.log("firstName触发了watch,newFirstName="+newFirstName+",oldFirstName="+oldFirstName)
this.watchFullName = this.firstName+this.lastName+","+other
},
lastName:function(newLastName, oldLastName){
console.log("lastName触发了watch,newLastName="+newLastName+",oldLastName="+oldLastName)
this.watchFullName = this.firstName+this.lastName+","+other
},
watchFullName:"change"
},
computed: {
fullName:function(){
console.log("调用了fullName,计算了一次属性")
return this.firstName+this.lastName+","+other;
}
},
methods: {
fullName2:function(){
console.log("调用了fullName,执行了一次方法")
fullName2 = this.firstName+this.lastName+","+other;
return fullName2;
},
change(){
console.log("调用了change,触发了watch")
return this.watchFullName='111'
}
}
});
</script>
</body>
</html>
handler方法就相当于普通侦听器触发的事件,从结果可以看到,组件初始化的时候,侦听器并没有handler方法 ,所以fullName是没有值的
当修改以上代码,加上immediate: true,immediate:true代表watch里面声明了之后会立马执行handler里面的函数。执行相应的逻辑。组件初始化的时候,侦听器会立马(immediate)触发handler方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue计算属性/侦听器/方法比较</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div>
<p>FullName: {{person.fullname}}</p>
<p>FirstName: <input type="text" v-model="person.firstname"></p>
</div>
</div>
<script>
var other = 'This is other';
var app = new Vue({
el: '#app',
data(){
return {
person: {
firstname: 'Menghui',
lastname: 'Jin',
fullname: ''
}
}
},
watch: {
person: {
handler(n,o){
this.person.fullname = n.firstname + '' + this.person.lastname;
},
immediate: true, //刷新加载 立马触发一次handler
deep: true // 可以深度检测到 person 对象的属性值的变化
}
}
})
</script>
</body>
</html>
当在输入框中输入数据时, 可以发现fullName的值并没有随之改变 这是因为vue无法检测到对象内部属性值的变化,比如person.firstname的变化
所以此时 需要用到vue的深度监听(deep)
此时加上代码 deep: true
可以发现 每次输入框数据变化 fullname随之改变
上面的可以发现handler监听的新值和老值是一样的 这是有vue2.0的坑 犹豫同源导致的 可以用computed来修
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="app">
<p>FullName: {{person.fullname}}</p>
<p>FirstName: <input type="text" v-model="person.firstname"></p>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data() {
return {
person: {
firstname: 'Menghui',
lastname: 'Jin',
fullname: ''
}
}
},
methods: {
},
computed: {
person2(){
return JSON.parse(JSON.stringify(this.person));
}//解决深度监听新老值同源问题
},
watch:{
person2:{
handler(n,o){
console.log(this.person);
console.log(n.firstname);
console.log(o.firstname);
/* this.person.fullname = this.person.firstname + this.person.lastname */
},
/* immediate: true, */
deep: true // 可以深度检测到 person 对象的属性值的变化
}
}
})
</script>
</body>
</html>