Vue中watch

watch可以监听data中的数据变化:

watch:{
	value:function(newVal,oldVal){
		...do something
	}
}
methods:{
	valueChange(newVal,oldVal){
		...do something
	}
},
watch:{
	value:'valueChange'
}

如上两种方法都可以实现我们的监听需求,同样也是我们普遍的用法,但当我们所监听的vaule值变为一个object对象时,显然,上面的使用已经满足不了我们的需求了。

那我们可以直接修改需求吗,显然不能;如果能,我们也不能这么做;所以我们只能来修改我们的使用方法来满足需求。

deep

那假设我们有一个对象为**{id:‘1’,name:‘cb’}**,那我们要监听对象中id,和name的变化,并做一些处理。

data(){
	return {
		Object:{
			id:'1',
			name:'cb'
		}
	}
},
watch:{
	Object:{
		handler(newVal,oldVal){
			...do something
		},
		deep:true
	}
}

那这样就可以满足我们的需求了,这就是deep属性,deep属性会对目标深度监听。

那如果我们只想监听对象的 id或者name,那我们上面的写法是不是就有些浪费了呢。我们也可以单独对对象的某个属性来监听。

watch:{
	'Object.id':{
		handler(newVal,oldVal){
			...do something
		},
	},
	'Object.name':{
		handler(newVal,oldVal){
			...do something
		}
	}
}
immediate

在使用wacth 的监听中,组件初始化时,并不会执行wacth 中的handler方法,那么immediate属性就要登场了。

watch:{
	value:{
		handler(newVal,oldVal){
			...do something
		},
		immediate:true
	}
}

immediate 设为true时,组件初始化就会执行一次handler,灵活运用来满足需求。

提示

数组的变化不需要通过深度监听,对象数组中对象的属性变化则需要deep深度监听。

vue-watch监听器

1. 概述

watch是 vue 中常用的监听器,它主要用于侦听数据的变化,在数据发生变化的时候执行一些操作。

Vue 官网很明确的建议我们这样使用 watch 侦听属性:当需要在数据变化时执行 异步 或 开销较大 的操作时,这个方式是最有用的。

2. 用法

watch: {
   监听的属性: {
      handler(数据改变后新的值, 数据改变前旧的值) {
         编写处理逻辑
      }
   }
}

3. 监听基本类型

<template>
  <div>
    <div>
      <input type="text" v-model="something">
    </div>
  </div>
</template>
<script>
export default {
    data() {
        return {
            something: ""
        }
    },
    watch: {
        "something": {
            handler(newVal, oldVal) {
                console.log("新的值:" + newVal);
                console.log("旧的值:" + oldVal);
                console.log("hellow  world");
            }
        }
    }
}
</script>

📌 输入框中输入1、2 , 效果图如下:

💖 还可以 简写 如下:

watch: {
    "something"(newVal, oldVal)  {
        console.log("新的值:" + newVal);
        console.log("旧的值:" + oldVal);
        console.log("hellow  world");
    }
}

4. 监听复杂类型(数组、对象)中的某个属性

<template>
  <div>
    <div>
      <input type="text" v-model="obj.field1">
    </div>
  </div>
</template>
<script>
export default {
    data() {
        return {
            something: "",
            obj: {
                field1: "",
                field2: ""
            }
        }
    },
    watch: {
        "obj.field1"(newVal, oldVal)  {
            console.log("新的值:" + newVal);
            console.log("旧的值:" + oldVal);
            console.log("hellow  world");
        }
    }
}
</script>

📌 输入框中输入1、2 , 效果图如下:

5. 监听复杂类型

当需要监听一个对象的变化时,普通的 watch 方法无法监听到对象内部属性的变化,可以设置深度监听 deep: true,当对象的属性较多时,每个属性的变化都会执行 handler。

<template>
  <div>
    <div>
      <input type="text" v-model="obj.field1">
    </div>
  </div>
</template>
<script>
import { stringifyQuery } from "vue-router";

export default {
    data() {
        return {
            something: "",
            obj: {
                field1: "",
                field2: ""
            }
        }
    },
    watch: {
        "obj": {
            handler(newVal, oldVal)  {
                console.log("新的值:" + newVal);
                console.log("旧的值:" + oldVal);
                console.log("hellow  world");
            },
            deep: true
        }
    }
}
</script>

📌 输入框中输入1、2 , 效果图如下:

💦 vue 中用 console.log 直接打印对象显示 [object Object],使用 console.log(JSON.stringify(obj)),能把对象以 json 格式输出。

watch: {
    "obj": {
        handler(newVal, oldVal)  {
            console.log("新的值:" + JSON.stringify(newVal));
            console.log("旧的值:" + JSON.stringify(oldVal));
            console.log("hellow  world");
        },
        deep: true
    }
}

⚠️但是,结果发现旧的值和新的值一样,为什么呢?

对于引用类型,赋值指向是地址,地址指向堆区存储的值,所以新旧值一样,换句话说就是指向堆的同一个空间,拷贝的是地址,值也是跟着变的,一旦改变拷贝对象中某个变量的值 原始对象也会被改变;

✨解决方案:

利用深拷贝,会创建出一个新的地址指向新的空间。属性发生改变时,跟原始对象值互不干扰(这里运用了computed计算属性的暂缓特性来赋值)

<template>
  <div>
    <div>
      <input type="text" v-model="obj.field1">
    </div>
  </div>
</template>
<script>
export default {
    data() {
        return {
            something: "",
            obj: {
                field1: "",
                field2: ""
            }
        }
    },
    computed: {
        newObj() {
            return JSON.stringify(this.obj);
        }
    },
    watch: {
        "newObj": {
            handler(newVal, oldVal)  {
                console.log("新的值:" + JSON.stringify(newVal));
                console.log("旧的值:" + JSON.stringify(oldVal));
                console.log("hellow  world");
            },
            deep: true
        }
    }
}
</script>

6. immediate的用法和作用

immediate设置为 true,页面进来就会立即执行监听。

watch: {
    "newObj": {
        handler(newVal, oldVal)  {
            console.log("新的值:" + JSON.stringify(newVal));
            console.log("旧的值:" + JSON.stringify(oldVal));
            console.log("hellow  world");
        },
        deep: true,
        immediate: true
    }
}

进入页面,立即执行监听。

7. VUE3 中的 Watch 用法

🎈 VUE2 代码:

watch: {
    nums () {},
    'demo.name' () {}
}

🎈 VUE3 代码:

watch(nums, () => {})
watch(() => demo.name, () => {})

8. 应用场景

8.1 表单输入实时验证

监听输入框数据变化,实时验证格式合法性(如邮箱、密码等),并反馈错误信息。

<template>
  <input v-model="email">
</template>

<script>
export default {
  data() {
    return { email: "" };
  },
  watch: {
    email(newVal) {
      const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
      this.isValid = pattern.test(newVal); // 实时验证邮箱格式
    }
  }
};
</script>

8.2 异步操作

当数据变化需要触发异步任务(如 API 请求)时

<script>
export default {
  data() {
    return { keyword: "", timer: null };
  },
  watch: {
    keyword(newVal) {
      fetchResults() { /* 异步请求逻辑 */ }
    }
  }
};
</script>

8.3 监听路由参数变化

组件内响应路由参数变化,重新加载数据:

watch: {
  '$route.params.id'(newId) {
    this.fetchUser(newId);
  }
}

8.4 深度监听对象/数组

当需要检测对象内部属性或数组元素变化时:

watch: {
  formData: {
    handler(newVal) {
      this.validateForm();
    },
    deep: true // 深度监听
  }
}

9. 与计算属性的区别

  • computed:派生新数据,适合模板中依赖其他数据的 计算结果(有缓存)。
  • watch:监听数据变化执行 副作用(如异步操作、DOM 操作)。

Vue中的watch的使用 

watch的使用方式(简单数据类型)

watch: {
        这里写你在data中定义的变量名或别处方法名: {
                handler(数据改变后新的值, 数据改变之前旧的值) {
                                这里写你拿到变化值后的逻辑

                        }

                }

        }

举例

data() {
      return {
         value: ""
      }
    },
    watch: {
        //方法1
       "value"(newVal, oldVal) {
          console.log(`新值:${newVal}`);
          console.log(`旧值:${oldVal}`);
          console.log("hellow  world");
      },
        //方法2
        "value": {
            handler(newVal, oldVal) {
              console.log(`新的值: ${newVal}`);
              console.log(`旧的值: ${oldVal}`);
              console.log("hellow  world");
            }
          }
        }

监听:复杂数据类型的单一属性

'query.page': {
      handler(val, oldval) {
        console.log('1获取新的数据真正是', val, oldval)
      }
    },

watch的使用方式(复杂数据类型)

这个很常用,我们往往使用的就是复杂的数据类型,所以深度监听是非常方便的。

query: {
      handler(newVal, oldVal) {
        console.log('2获取复杂数据', newVal, oldVal)
      },
      deep: true
    }

因为watch是数据发生变化的时候才会调用,如果想已进入页面就使用watch(这个不常用)

value: {
      handler(val, oldval) {
        console.log('获取新的数据真正的', val, oldval)
      },
      immediate: true
    },

我们再思考一件事情我们除了监听data里边的数据,可不可以监听filter,computed呢?

首先我们总结一下vue组件中出现的数据有哪些?

vue中其他类型的数据

computed

computed的监听 

computed: {
    newValue: function() {
      if (this.value === 0) {
        return 'this is a computed'
      } else {
        return 'this is a new computed'
      }
    }
  },
 
watch:{
 
    newValue: {
      handler(newVal, oldVal) {
        console.log('监听computed', newVal, oldVal)
      }
    },
 
 
}

我们延申一下,再思考一下,computed是一个什么样的存在,相当于爆露出来还是一个新的数据,因为在beforeUpdate的回调中的我们也可以做这样的操作,比如把一个新的值给新的值,这样实现数据修饰。

我们看一下computed的介绍

computed用来监控自己定义的变量,该变量不在data里面声明,直接在computed里面定义.
然后就可以在页面上进行双向数据绑定展示出结果或者用作其他处理;并且带有缓存功能
computed比较适合对多个变量或者对象进行处理后返回一个结果值,也就是数多个变量中的某一个值发生了变化则我们监控的这个值也就会发生变化,
举例:总金额=价格*数量
也就是说当我们需要进行大量计算的时候我们适合用computed。c=a*b,a和b不发生变化,就不会重新去取,这就是computed的优势。也就是说这个可以提高项目的性能。

我们总结一下一个页面重新渲染的办法有哪些?

页面渲染的方式
1.表达式。(缺点是不太灵活)

2.methods。(缺点是每次调用都需要重新的解析)

3.filters

4.computed

5.生命周期内实现数据修饰

6.props (父组件传过来的值)

这里我们主要提到filter的computed的使用,可以提高项目的运行效率。那么filter与computed的区别是什么呢?

filter经常使用在格式化上。

computed经常使用在计算上。

filter 与 computed 的区别

触发时机不同
computed 属性背后的处理逻辑比较复杂,依赖 Vue 的数据更新通知机制,在属性所依赖的其他数据项发生变化时才会重新触发计算。优点是计算频率相对较低;缺点是依赖于组件,难以抽取成独立逻辑,也就是复用性低。

filter 则显的简单很多,只在显式调用时触发,一般应用在模板渲染上。优点是容易在组件外抽象;缺点是每次模板渲染时都需要重新执行计算。可以通过示例 感受调用时机的区别:

应用范围不同
computed 很广泛,可以应用在其他computed、methods、生命周期函数、模板;filter 一般只应用于模板渲染上,如果要在其他位置复用,需要使用 this._f 函数:

Vue.component('HelloWorld', {
     filters: {
         hello() {
             return 'hello';
         }
     },
     methods: {
         ping() {
             return `${this._f('hello')()} world`;
         }
      }
})

另外,在使用上filter支持链式调用,这为其增加了组合拼接的能力:

{{ name | normalize | capitalize }}

定义方式

最后需要指出,computed 属性只能在组件内部或通过mixins对象定义;而 filter 有两种定义方式,一是在组件内部通过 filters 属性定义;一是在组件外部通过 Vue.filter 函数定义:

Vue.component('HelloWorld', {
     filters: {
         hello() {
             return 'hello';
         }
     }
});
Vue.filter('hello', ()=> 'hello');

应用规则

综上,filter 无法缓存,调用频率高,因此特别适用于格式化输出场景,比如日期格式化。filter 具有组合调用能力,因此可以在项目架构层面定义一堆基础的、简单的filter,按需在组件内组合适用。

computed 属性具有缓存能力,在组件内普适性更强,因此适用于复杂的数据转换、统计等场景。

要用的属性不存在,要通过已有的属性(Vue实例上的data属性值)计算得来
原理:底层借助了Object.defineProperty方法提供的getter和setter
优势:与methods属性相比,computed内部有缓存机制(数据复用),效率更高,调试更方便
计算属性最终会出现在vm上,直接读取使用即可
如果计算属性要被修改,那必须写setter去响应修改,且setter要引起计算时依赖的数据发生变化
再延申一点,watch在deforeUpdated之前,先获取,再监听,然后再更新,再渲染

Vue学习笔记五:数据监听watch计算属性computed

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>数据监听watch计算属性computed</title>
</head>
<script src="../vue.js"></script>

<body>

    <!-- 
          watch监听单个,computed监听多个

               *业务思考:
                    /类似淘宝,当我输入某个人名时,我想触发某个效果(watch)
                    /利用vue做一个简单的计算器(computed)

               *类当watch监听的是复杂数据类型的时候需要做深度监听(写法如下)
                    /watch:{
                        msg:{
                            handler(val){
                                if(val.text=="love"){
                                    alert(val.text)
                                }
                            },
                            deep:true//开启深度监听
                        }
                    }

               *computed监视对象,写在函数内部,凡是函数内部有this.相关属性的,
                  改变都会触发当前函数
                    /每改变一个相关属性,触发一次当前函数
     -->

    <div id="app">
        watch监听数据
        <input type="text" name="" v-model="msg"><br>
        computed计算属性
        <input type="text" name="" v-model="n1">+
        <input type="text" name="" v-model="n2">*
        <input type="text" name="" v-model="n3">={{result}}
    </div>

    <script>
        new Vue({
            el: "#app",
            data() {
                return {
                    msg: '',
                    n1: '',
                    n2: '',
                    n3: '',
              
                }
            },
            computed: {
                result() {
                    return (Number(this.n1) + Number(this.n2)) * Number(this.n3)
                }
            },
            watch: {
                msg(newval,oldval){
                    console.log(newval,oldval);
                    if(newval=="love"){
                        alert(newval)
                    }
                }
                // msg: {
                //     handler(newval, oldval) {
                //         if (newval.text == "love") {
                //             alert(newval.text)
                //         }
                //     },
                //     deep: true,
                // }
            }
        })
    </script>
</body>

</html>

效果

Vue使用watch时要注意是否是深度监视

一般情况下,要监视数据的变化可以使用简写,例如

 watch: {
    todos (value) {
      localStorage.setItem('todos', JSON.stringify(value))
    }
  }

如果是要监视的数据是一个对象,并且是要监视对象中的属性的变化,应该使用深度监视,就应该写成它的完整版,并且加上deep:true

  watch: {
    todos: {
      handler (value) {
        localStorage.setItem('todos', JSON.stringify(value))
      },
      deep: true
    }
  }

VUE3 中的 Watch 详解

一、监听基础类型

const nums = ref(9)

watch(nums, (newValue, oldValue) => {
    console.log('watch 已触发', newValue)
})

二、监听复杂类型

const demo = reactive({
	name: '前端小玖',
	nickName: '小玖',
	soulmate: {
		name: '',
		nickName: ''
	}
})

复杂类型的监听有很多种情况,具体的内容如下

1、监听整个对象

watch(demo, (newValue, oldValue) => {
    console.log('watch 已触发', newValue)
})

其第一个参数是直接传入要监听的对象。当监听整个对象时,只要这个对象有任何修改,那么就会触发 watch 方法。无论是其子属性变更(如 demo.name),还是孙属性变更(如 demo.soulmate.name)...,都是会触发 watch 方法的。

2. 监听对象中的某个属性

// 监听demo对象的name属性

watch(() => demo.name, (newValue, oldValue) => {
    console.log('watch 已触发', newValue)
})

如上代码,监听 demo 对象的 name 属性,那么只有当 demo 对象的 name 属性发生变更时,才会触发 watch 方法,其他属性变更不会触发 watch 方法。注意,此时的第一个参数是一个箭头函数

3. 只监听对象的子属性

watch(() => ({ ...demo }), (newValue, oldValue) => {
    console.log('watch 已触发', newValue)
})

这种情况,只有当 demo 的子属性发生变更时才会触发 watch 方法。孙属性,曾孙属性... 发生变更都不会触发 watch 方法。也就是说,当你修改 demo.soulmate.name 或者 demo.soulmate.nickName 时是不会触发 watch 方法的。

4. 监听对象的所有属性

watch(() => demo, (newValue, oldValue) => {
	console.log('watch 已触发', newValue)
}, { deep: true })

这个相当于监听整个对象(效果与上面的第一种相同)。但是实现方式与上面第一种是不一样的,这里我们可以看到,第一个参数是箭头函数,并且还多了第三个参数 { deep: true }。当加上了第三个参数 { deep: true },那么就不仅仅是监听对象的子属性了,它还会监听 孙属性,曾孙属性 ...

通常要实现监听对象的所有属性,我们都会采用上面第一种方法,原因无他,第一种编码简单,第一个参数直接传入 demo 即可。

三、组合监听

const nums = ref(9)
const demo = reactive({
	name: '前端小玖',
	nickName: '小玖',
	soulmate: {
		name: '',
		nickName: ''
	}
})

什么是组合监听呢?举个例子,比如我想同时监听 demo 对象的 name 属性,和基础类型 nums,只要他们其中任何一个发生变更,那么就触发 watch 方法。

watch([() => demo.name, nums], ([newName, newNums], [oldName, oldNums]) => {
	console.log('watch 已触发: name', newName)
	console.log('watch 已触发: nums', newNums)
})

注意,此时的第一个参数是一个数组,且第二参数箭头函数的参数也是数组的形式。

四、其他

与 VUE2 中的 watch 不同,VUE3 可以多次使用 watch 方法,通过多个watch 方法来监听多个对象。而 VUE2 则是把所有的要监控的对象放在 watch 里面。

VUE2 代码:

watch: {
    nums () {},
    'demo.name' () {}
}

VUE3 代码:

watch(nums, () => {})
watch(() => demo.name, () => {})

关于 watch 的第三个参数,除了布尔类型的 deep,还有一个布尔类型的 immediate。源码中的接口声明如下:

export declare interface WatchOptions<Immediate = boolean> extends WatchOptionsBase
{
    immediate?: Immediate;
    deep?: boolean;
}

immediate 的作用就是设置是否立即执行监控,当我们将其值设置为 true 时,那么被监控的对象在初始化时就会触发一次 watch 方法,相当于页面一刷新就会触发。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值