首页 前端知识 vue2中的watch(侦听器)讲解,以及解决深度监听新值和旧值相同的两种方案(手写深拷贝和JSON.parse())。

vue2中的watch(侦听器)讲解,以及解决深度监听新值和旧值相同的两种方案(手写深拷贝和JSON.parse())。

2024-05-25 09:05:15 前端知识 前端哥 446 283 我要收藏

目录

一:什么是watch?

二:watch的基础使用 

 1.最基本的使用

 2.简写形式

三:watch中的immediate和deep属性

 1.immediate属性

 2.deep属性

 3.解决深度监听新旧值相同的问题        

       1)使用序列化和反序列化。

        2)手写深拷贝算法


一:什么是watch?

        相信大家在开发项目中,有时候会遇到一些需求,是当一个数据改变之后进行一些操作,这个时候有些人会设置一个定时器,周期性的去循环访问,当发现数据发生了改变后执行操作。但是这种操作方式会导致系统资源的浪费,以及更新的不及时等。因此vue通过watch(侦听器)提供了一个更通用的方法来响应数据的变化,当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

        核心原理就是对传入Vue options的watch对象里面的所有的属性,都添加一个自定义watcher,收集对应属性的依赖,然后当这个依赖项更新的时候,不仅通知它的渲染watcher(可能没有),也会通知当前的这个自定义watcher,从而叫到你定义的callback,完成函数内的逻辑。

二:watch的基础使用 

 1.最基本的使用

         下方代码是watch的一个基础使用,可以看到当第一次进入页面的时候,并没有触发,而点击按钮的时候,name的值发生了改变,被watch监听到,因此执行了下面的输出逻辑。

<template>
<div class="home">
<el-button @click="name='李四'">改变</el-button>
</div>
</template>
<script>
export default {
data() {
return {
name: '张三'
}
},
//使用watch监听器,当不点击按钮的时候不会触发
watch: {
name: { //这里是要监听的变量名
handler(newValue, oldValue) { //这里的第一个参数是改变后的值,第二个参数是原来的值
console.log('改后的值是:' + newValue)
console.log('原来的值是:' + oldValue)
}
}
}
}
</script>
复制
运行效果

 2.简写形式

        当只是如上所示,简单地进行监听的时候,为了方便,我们可以直接使用简写形式。

<template>
<div class="home">
<el-button @click="name='李四'">改变</el-button>
</div>
</template>
<script>
export default {
data() {
return {
name: '张三'
}
},
//使用watch监听器,当不点击按钮的时候不会触发
watch: {
name(newValue, oldValue) { //这里的第一个参数是改变后的值,第二个参数是原来的值
console.log('改后的值是:' + newValue)
console.log('原来的值是:' + oldValue)
}
}
}
</script>
复制

三:watch中的immediate和deep属性

 1.immediate属性

         在上述代码中,我们可以发现,当watch第一次绑定数据的时候并没有触发监听效果,这是watch中的一个特性,但同样的,我们可以使用immediate属性改变这种特性。

<template>
<div class="home">
<el-button @click="name='李四'">改变</el-button>
</div>
</template>
<script>
export default {
data() {
return {
name: '张三'
}
},
//使用watch监听器,当不点击按钮的时候不会触发
watch: {
name: {
handler(newValue, oldValue) {//这里的第一个参数是改变后的值,第二个参数是原来的值
console.log('改后的值是:' + newValue)
console.log('原来的值是:' + oldValue)
},
immediate: true
}
}
}
</script>
复制
运行效果

 2.deep属性

         在使用watch的过程中,如果定义了一个对象,这时候我们会发现使用一般的watch监听,是无法监听对象内部的改变的,这时候我们需要使用depp属性进行一个深度监听。

        比如在以下代码中,因为watch所监听的是一个person对象,当点击按钮改变之后,会发现没有任何执行效果。也就是监听失败。

<template>
<div class="home">
<el-button @click="person.name='李四'">改变</el-button>
</div>
</template>
<script>
export default {
data() {
return {
person: {
name:'张三'
}
}
},
//使用watch监听器,当不点击按钮的时候不会触发
watch: {
person: {
handler(newValue, oldValue) {//这里的第一个参数是改变后的值,第二个参数是原来的值
console.log('改后的值是:' + newValue.name)
console.log('原来的值是:' + oldValue.name)
}
}
}
}
</script>
复制

         所以我们在上述代码中加入depp:true,形成下面的代码,这时候我们可以在控制台看到是能够成功执行的。

<template>
<div class="home">
<el-button @click="person.name='李四'">改变</el-button>
</div>
</template>
<script>
export default {
data() {
return {
person: {
name:'张三'
}
}
},
//使用watch监听器,当不点击按钮的时候不会触发
watch: {
person: {
handler(newValue, oldValue) {//这里的第一个参数是改变后的值,第二个参数是原来的值
console.log('改后的值是:' + newValue.name)
console.log('原来的值是:' + oldValue.name)
},
deep:true//深度监听
}
}
}
</script>
复制
运行效果

 3.解决深度监听新旧值相同的问题        

        但是在上面的运行效果中,我们可以发现改变前和改编后的值都是一样的,这显然不符合我们的初衷,失去了oldValue的作用,当然我们可以把监听的字符串person变成’person.name‘,但是这样又不属于监听对象了,而是回到了最初的监听字符串。

        我查了一下vue的官方文档,对watch的解释如下

在变异 (不是替换) 对象或数组时,旧值将与新值相同,因为它们的引用指向同一个对象/数组。Vue 不会保留变异之前值的副本。

        通过官方解释,我们有以下两种方式解决该问题:

       1)使用序列化和反序列化。

<template>
<div class="home">
<el-button @click="person.name='李四'">改变</el-button>
</div>
</template>
<script>
export default {
data() {
return {
person: {
name:'张三'
}
}
},
//使用watch监听器,当不点击按钮的时候不会触发
watch: {
newPerson: {
handler(newValue, oldValue) {//这里的第一个参数是改变后的值,第二个参数是原来的值
console.log('改后的值是:' + newValue.name)
console.log('原来的值是:' + oldValue.name)
},
deep:true,//深度监听
}
},
computed:{
// 使用计算属性进行深拷贝
newPerson(){
return JSON.parse(JSON.stringify(this.person))
}
}
}
</script>
复制
运行效果

        这种方式优点是直接简单,对于程序员而言代码行数更少,但是缺点在于这种深拷贝方式容易带来性能的问题,并且会丢失原有的原型链,在如unidentified等特殊值时会报错,将NaN序列化成null等问题。所以通常而言,用第二种方法会更好一些

        2)手写深拷贝算法

<template>
<div class="home">
<el-button @click="person.name='李四'">改变</el-button>
</div>
</template>
<script>
export default {
data() {
return {
person: {
name: '张三'
}
}
},
//使用watch监听器,当不点击按钮的时候不会触发
watch: {
newPerson: {
handler(newValue, oldValue) { //这里的第一个参数是改变后的值,第二个参数是原来的值
console.log('改后的值是:' + newValue.name)
console.log('原来的值是:' + oldValue.name)
},
deep: true, //深度监听
}
},
computed: {
// 使用计算属性进行深拷贝
newPerson() {
// 使用自己手写的深克隆方法,效率更高
return this.deepClone(this.person)
}
},
methods: {
//下面这个是手写深拷贝
deepClone(source) {
let target = null;
if (!this.isArray(source) && !this.isObject(source)) {
target = source;
}
if (this.isArray(source)) {
target = [];
for (let i = 0; i < source.length; i++) {
target[i] = this.deepClone(source[i]);
}
}
if (this.isObject(source)) {
target = {};
for (let key in source) {
target[key] = this.deepClone(source[key]);
}
}
return target;
},
isArray(value) {
return Object.prototype.toString.call(value) === '[object Array]';
},
isObject(value) {
return Object.prototype.toString.call(value) === '[object Object]';
}
}
}
</script>
复制

         这里运行效果和方案一的效果是一样的,所以不再附加运行截图。但是运行时间上可以看到,将点击事件的代码修改成方法,并且使用console.time()来计算时间,如下代码所示

<template>
<div class="home">
<!-- 这里的@click直接变成一个方法 -->
<el-button @click="handleClick">改变</el-button>
</div>
</template>
<!-- script修改部分如下 -->
<script>
export default {
...
watch: {
newPerson: {
handler(newValue, oldValue) { //这里的第一个参数是改变后的值,第二个参数是原来的值
console.log('改后的值是:' + newValue.name)
console.log('原来的值是:' + oldValue.name)
console.timeEnd('process')// 《---新增代码(关闭计时器)
},
deep: true, //深度监听
}
},
...
},
methods: {
handleClick(){// 《---新增代码
console.time('process')//打开计时器
this.person.name='李四'
},
....
}
}
</script>
复制

添加上述代码后,再次执行两种方式的代码,执行时间如下

JSON.parse()的执行时间

手写深拷贝的执行时间

         通过上面两张图的对比,我们可以清楚地看到,使用JSON.parse()的执行时间是比手写深拷贝要久的,因此在大项目中,手写的执行效率往往更具有优势。

转载请注明出处或者链接地址:https://www.qianduange.cn//article/9471.html
标签
评论
还可以输入200
共0条数据,当前/页
发布的文章

JQuery中的load()、$

2024-05-10 08:05:15

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!