返回

Vue学习笔记|Vue基础05

计算属性、侦听属性

考虑一个场景:在两个输入框中分别输入姓和名,下方显示出全名

插值语法实现

1
2
3
4
5
6
<!-- 准备好一个容器-->
<div id="root">
    姓:<input type="text" v-model="firstName"> <br/><br/>
    名:<input type="text" v-model="lastName"> <br/><br/>
    全名:{{firstName}}-{{lastName}}
</div>
1
2
3
4
5
6
7
new Vue({
    el:'#root',
    data:{
        firstName:'张',
        lastName:'三'
    }
})

插值语法实现

通过v-model实现实现属性和输入的双向绑定,使用插值语法{{firstName}}-{{lastName}}实现全名的拼接显示

优化方向

  • Vue官方推荐:组件模板应该只包含简单的表达式,复杂的表达式则应该重构为计算属性方法
  • 复杂表达式会让模板变得不那么声明式。我们应该尽量描述应该显示什么,而非如何计算那个值。而且计算属性和方法使得代码可以复用

methods实现

1
2
3
4
5
6
7
8
<div id="root">
    姓:<input type="text" v-model="firstName"> <br/><br/>
    名:<input type="text" v-model="lastName"> <br/><br/>
    全名:{{fullName()}}
    <!--测试函数调用次数-->
    <br>全名:{{fullName()}}
    <br>全名:{{fullName()}}
</div>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
new Vue({
    el:'#root',
    data:{
        firstName:'张',
        lastName:'三'
    },
    methods: {
        fullName(){
            console.log('调用了fullName()函数~')
            return this.firstName + '-' + this.lastName
        }
    },
})

methods实现

通过v-model实现实现属性和输入的双向绑定,使用方法fullname()实现全名的拼接显示

从图中可以看到fullname()每次读取时都会执行一次。当数据发生改变时fullname()会被重新调用。

优化方向:

  • 可否让fullname()在第一次调用后形成缓存,后续仅在属性值发生修改时才再次调用

计算属性实现

  • 定义:通过已有属性计算得来的属性
  • 原理:底层借助了Objcet.defineproperty方法提供的gettersetter
  • get()函数什么时候执行?
    • 初次读取时会执行一次。
    • 当依赖的数据发生改变时会被再次调用。
  • 优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
  • 备注:
    • 计算属性最终会出现在vm上,直接读取使用即可。
    • 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
1
2
3
4
5
6
7
8
<div id="root">
    姓:<input type="text" v-model="firstName"> <br/><br/>
    名:<input type="text" v-model="lastName"> <br/><br/>
    全名:{{fullName}}
    <!--测试函数调用次数-->
    <br>全名:{{fullName}}
    <br>全名:{{fullName}}
</div>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
new Vue({
    el:'#root',
    data:{
        firstName:'张',
        lastName:'三',
    },
    computed:{
        fullName:{
            get(){
                console.log('get()被调用了')
                return this.firstName + '-' + this.lastName //此处的this是vm
            },
            set(value){
                console.log('set',value)
                const arr = value.split('-')
                this.firstName = arr[0]
                this.lastName = arr[1]
            }
        }
    }
})

计算属性简写

  • 若计算属性只读,则可以简写计算属性:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
new Vue({
    el:'#root',
    data:{
        firstName:'张',
        lastName:'三',
    },
    computed:{
        fullName(){
            console.log('get()被调用了')
            return this.firstName + '-' + this.lastName //此处的this是vm
            },
   }
})

计算属性实现

通过v-model实现实现属性和输入的双向绑定,使用计算属性fullname实现全名的拼接显示

从图中可以看到get()只在初次读取时会执行一次,后续会使用缓存实现复用。当依赖的数据发生改变时会被再次调用。

天气案例

考虑一个场景:有一句话表示当前天气情况,每当单击『切换天气』按钮,当前天气情况都会切换

1
2
3
4
5
6
7
<!-- 准备好一个容器-->
<div id="root">
    <h2>今天天气很{{info}}</h2>
	<!-- 绑定事件的时候:@xxx="yyy" yyy可以写一些简单的语句 -->
	<!-- <button @click="isHot = !isHot">切换天气	</button> -->
	<button @click="changeWeather">切换天气</button>
</div>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
const vm = new Vue({
    el:'#root',
    data:{
        isHot:true,
    },
    computed:{
        info(){
            return this.isHot ? '炎热' : '凉爽'
        }
    },
    methods: {
        changeWeather(){
            this.isHot = !this.isHot
        }
    },
})

侦听属性

  • 侦听属性watch:当被侦听的属性变化时, 回调函数handler自动调用, 进行相关操作
  • 侦听的属性必须存在,才能进行侦听
  • 侦听的两种写法:
    • new Vue时传入watch配置,适用于创建Vue实例时知道要侦听哪些属性
    • 通过vm.$watch侦听,适用于事先不知道侦听的对象,根据用户操作决定侦听属性
1
2
3
4
5
<!-- 准备好一个容器-->
<div id="root">
    <h2>今天天气很{{info}}</h2>
	<button @click="changeWeather">切换天气</button>
</div>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const vm = new Vue({
    el:'#root',
    data:{
        isHot:true,
    },
    computed:{
        info(){
            return this.isHot ? '炎热' : '凉爽'
        }
    },
    methods: {
        changeWeather(){
            this.isHot = !this.isHot
        }
    },
    <!-- 写法一 -->
    /* watch:{
		isHot:{
			immediate:true, //初始化时让handler调用一下
			//handler什么时候调用?当isHot发生改变时。
			handler(newValue,oldValue){
				console.log('isHot被修改了',newValue,oldValue)
				}
			}
		} */
})
<!-- 写法二 -->
vm.$watch('isHot',{
    immediate:true, //初始化时让handler调用一下
    //handler什么时候调用?当isHot发生改变时。
    handler(newValue,oldValue){
        console.log('isHot被修改了',newValue,oldValue)
    }
})
  • 深度侦听deep
    • Vue中的watch默认不侦听对象内部值的改变(一层)
    • 配置deep:true可以监测对象内部值改变(多层)
  • 备注
    • Vue自身可以侦听对象内部值的改变,但Vue提供的watch默认不可以!
    • 使用watch时根据数据的具体结构,决定是否采用深度侦听deep
1
2
3
4
5
6
7
8
<!-- 准备好一个容器-->
<div id="root">
    <h3>a的值是:{{numbers.a}}</h3>
	<button @click="numbers.a++">点我让a+1</button>
	<h3>b的值是:{{numbers.b}}</h3>
	<button @click="numbers.b++">点我让b+1</button>
	{{numbers.c.d.e}}
</div>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const vm = new Vue({
    el:'#root',
    data:{
        numbers:{
            a:1,
            b:1,
            c:{
                d:{
                    e:100
                }
            }
        }
    },
    watch:{
        //侦听多级结构中某个属性的变化
        /* 'numbers.a':{
					handler(){
						console.log('a被改变了')
					}
				} */
        //侦听多级结构中所有属性的变化
        numbers:{
            deep:true,
            handler(){
                console.log('numbers改变了')
            }
        }
    }
})

侦听属性简写形式

  • 适用于所侦听的属性中只用回调函数handler,而不用immediatedeep等时
1
2
3
4
5
<!-- 准备好一个容器-->
<div id="root">
    <h2>今天天气很{{info}}</h2>
	<button @click="changeWeather">切换天气</button>
</div>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
const vm = new Vue({
   el:'#root',
   data:{
      isHot:true,
   },
   computed:{
      info(){
         return this.isHot ? '炎热' : '凉爽'
      }
   },
   methods: {
      changeWeather(){
         this.isHot = !this.isHot
      }
   },
   watch:{
      //正常写法
      /* isHot:{
         // immediate:true, //初始化时让handler调用一下
         // deep:true,//深度侦听
         handler(newValue,oldValue){
            console.log('isHot被修改了',newValue,oldValue)
         }
      }, */
       //简写
       isHot(newValue,oldValue){
           console.log('isHot被修改了',newValue,oldValue,this)
       }
   } 
})
//正常写法
/* vm.$watch('isHot',{
	immediate:true, //初始化时让handler调用一下
	deep:true,//深度监视
	handler(newValue,oldValue){
	console.log('isHot被修改了',newValue,oldValue)
	}
}) */

//简写
/* vm.$watch('isHot',(newValue,oldValue)=>{
	console.log('isHot被修改了',newValue,oldValue,this)
}) */

computed和watch区别

  • computed能完成的功能,watch都可以完成
  • watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作(定时器)

两个重要的小原则:

  • 所有被Vue管理的函数,最好写成普通函数,这样this的指向才是vm组件实例对象
  • 所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm组件实例对象
最后更新于 Oct 01, 2022 22:33 UTC
Built with Hugo
Theme Stack designed by Jimmy