Vue之监视属性

Posted 爱上口袋的天空

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue之监视属性相关的知识,希望对你有一定的参考价值。

目录

一、案例引入

 二、监视属性

三、深度监视

四、监视的简写形式

五、watch对比computed


一、案例引入

我们实现以下的一个小案例:

 

点击切换天气会分别显示凉爽、炎热(只有这两种状态),且每一次天气切换的时候都会被控制台给捕获到。

我们先不考虑控制台的那一部分,先把切换部分给做出来。

我们可以使用前文刚刚介绍的计算属性。第一部分实现代码如下:

	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<h2>今天天气很info</h2>
			<button @click="changeWeather">切换天气</button>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
		
		const vm = new Vue(
			el:'#root',
			data:
				isHot:true,
			,
			computed:
				info()
					return this.isHot ? '炎热' : '凉爽'
				
			,
			methods: 
				changeWeather()
					this.isHot = !this.isHot
				
			,
		)
	</script>

此处有两个注意点:
①如果我们把上述代码模板中的插值语法给去掉,也就是这样:

因为此时已有属性、计算属性在模板当中都没有使用,故会导致Vue的开发者工具出现一点小问题。
我们发现,点击了切换按钮之后,开发者面板中的属性值并没有发生变化:

 

事实是:数据已经更新了,但是开发者面板中没有更新,我们可以验证一下:

 ②一个小技巧
@click后面可以写一些简单的语句(就是你回调函数里面的语句),例如上面的代码我们还可以这样写:

同样也可以进行多项操作(理论上回调函数里面所有的语句都可以写在里面): 

 

效果:

 

不过这么写容易乱套,建议逻辑简单的时候可以使用这种技巧,如果复杂还是中规中矩的写回调函数。
同时这个方法还有一个注意点:不是所有的语句都可以执行的。例如:

 

报错:

 

因为alert是window身上的方法,而模板之中把对象给写死了就是vm,不会说找不到再去外头一层找(但会再去vm的原型对象上继续找,当然肯定是找不到的)。故这里alert是不能用的。
当然这种情况你非要解决也是有办法的(可以但没必要),就是让window可见就行:

 

我们直接将window放在data中,这样在模板中就可见了。 

然后控制台的捕获部分就要使用监视属性了。


 二、监视属性

监视属性顾名思义就是我们要监视的属性。那么我们如何去监视一个属性呢。Vue为我们提供了一个新的配置项watch。我们直接看相关实现代码,再进行解释:

	<body>
		<div id="root">
			<h2>今天天气很info</h2>
			<button @click="changeWeather">切换天气</button>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
		
		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)
					
				
			 
		)
	</script>

watch里面的被监视属性同样也要写成一个配置对象。其中:

  • handler是一个回调函数,当被监视的属性值发生变化的时候就会被调用。它会被传入两个参数:一个新值,一个旧值。
  • immediate是一个布尔属性,默认值是false。当设置为true的时候表示初始化的时候也要调用以下handler

不论是已有的属性还是计算属性都可以被监视

我们也可以不通过watch来进行监视,我们可以直接通过vm实现监视(前提是确保vm实例对象已经被创建完毕),代码如下:

	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<h2>今天天气很info</h2>
			<button @click="changeWeather">切换天气</button>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
		
		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)
			
		)
	</script>

注意:$watch()这个方法的第一个参数是要监视的对象。这个地方是有引号的。我们对象中的属性不写引号是一种简写形式,如果写全的话也是有引号的(因为是字符串类型)。

那么这两种我们如何选择呢?
如果一开始,也就是创建vm实例对象的时候,就已经很明确要监视谁,就可以直接选择watch配置,
如果创建实例的时候不是很明确要检测谁,后续根据用户的行为才能确定。那么就可以选择vm的相应api来监视

小总结:

 


三、深度监视

我们还是通过一个案例来理解:我们想检测number中的a的变化(b如果发生变化不管)

网页端:

也就是说监视多级结构中某个属性的变化,代码实现如下:


之所以加引号是因为对象里的key必须是字符串,里面直接写.是不可以的,所以我们只能还原原始的写法。

实现效果:

 

现在我们有新的需求,我们想要检测numbers中所有属性的变化(也就是说只要其中一种属性发生了变化就会被监测到),如果numbers中有很多很多的属性,继续沿用上面的代码的思路显然代码会非常冗长麻烦。这个时候我们就可以使用深度监视!

我们只需要添加一个配置项deep即可。它的默认值是false,以此来保证程序的运行效率。

监视多级结构中所有属性的变化,代码如下:

<script type="text/javascript">
		Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
		
		const vm = new Vue(
			el:'#root',
			data:
				isHot:true,
				numbers:
					a:1,
					b:1,
				
			,
			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)
					
				,
				//监视多级结构中某个属性的变化
				/* 'numbers.a':
					handler()
						console.log('a被改变了')
					
				 */
				//监视多级结构中所有属性的变化
				numbers:
					deep:true,
					handler()
						console.log('numbers改变了')
					
				
			
		)

	</script>

 小结:
深度监视:
(1).Vue中的watch默认不监测对象内部值的改变(一层)。
(2).配置deep:true可以监测对象内部值改变(多层)。
备注:
(1).Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!
(2).使用watch时根据数据的具体结构,决定是否采用深度监视。


四、监视的简写形式

使用前提:当你监视属性的配置项中只有handler的时候才可以使用。

与前文的计算属性的简写方法一致,直接写成一个函数。函数名对应监视的函数名,方法体就是handler的方法体。

代码如下:

	<body>
		<!-- 准备好一个容器-->
		<div id="root">
			<h2>今天天气很info</h2>
			<button @click="changeWeather">切换天气</button>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
		
		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)
				 */
			
		)
	</script>

同样的我们也可以使用vm的api进行监视,常规及简写版如下:

		//正常写法
		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)
		)

注意:此处不能写箭头函数(Vue管理的函数不能写成箭头函数)


五、watch对比computed

我们发现有时候一个功能可以用这两种方式分别实现。例如我们还是使用前面的一个小案例:有3个input框,姓、名、姓名。当姓和名发生变化的时候,姓名会跟着动态的发生变化。

watch实现:

<body>
    <div id="root">
        姓:<input type="text" v-model:value="firstName"> <br>
        名:<input type="text" v-model:value="lastName"> <br>
        姓名:<input type="text" :value="fullName">
    </div>
    
</body>
<script>
    const vm = new Vue(
        el:"#root", 
        data:
            firstName:'张',
            lastName:'三',
            fullName:'张-三'
        ,
        watch:
            firstName(newValue)
                this.fullName = newValue + '-' + this.lastName
            ,
            lastName(newValue)
                this.fullName = this.firstName + '-' + newValue
            ,
        

    )
</script>

 computed实现:

从这里可以看出使用计算属性的代码是比监听属性更加简洁的。但是这并不是绝对的,有时候我们要实现的功能甚至只有监听属性才能做。

下面我们讲将案例进行修改:我们要求修改之后的变化延迟一秒才生效。

代码实现如下:
使用监视属性实现:

<script>
    const vm = new Vue(
        el:"#root", 
        data:
            firstName:'张',
            lastName:'三',
            fullName:'张-三'
        ,
        watch:
            firstName(newValue)
                setTimeout(() => 
                    this.fullName = newValue + '-' + this.lastName
                , 1000);
            ,
            lastName(newValue)
                setTimeout(() => 
                    this.fullName = this.firstName + '-' + newValue
                , 1000);
            ,
        

    )
</script>

 使用计算属性是不能完成这个需求的。因为计算属性的变化全靠return,而我们是没有办法实现延迟return的。

如果你用setTimeout函数将return包裹,那么return里的东西返回给了延时函数,而不是计算属性的那个函数,相当于外层函数没有返回值,那么相应的计算属性也不会发生变化

 

所以我们可以知道:
计算属性里面是不能开启异步任务去维护数据的。但是使用监视属性是可以完成的。

当有一个需求,watch能实现computed也能实现,我们一般使用计算属性。但是当我们面临一些异步的计算的时候,我们就要使用watch.

同时还有一个小问题:

 

我们在这个箭头函数里面使用了this,那么它指向的是Vue的实例对象吗?

确实如此。我们可以在此基础上进行总结:

 

  1. 所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。
  2. 所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象。

以上是关于Vue之监视属性的主要内容,如果未能解决你的问题,请参考以下文章

Vue 核心

Vue 核心

Vue基础(中):数据代理事件处理计算和监视属性class与style绑定

Vue 计算属性和监视属性详解细节 class类绑定和style样式绑定

Vue.js watch监视属性

Vue之计算属性