Vue2.0 | Vue3.0 | |
双向绑定 | 利用ES5的ApiObject.defineProperty()对数据进行劫持,并结合发布订阅模式的方式实现 | 利用Es6的Proxy 对数据进行代理的方式实现 |
根节点 | 根节点只能是一个 | 根节点可以是多个 |
数据监听 | computed watch | computed watch watcheffect |
指令优先级 | v-for优先于v-if生效 | v-if优先于v-for生效 |
Diff算法 | 双端比较算法 | 去头尾的最长子序列算法 |
生命周期 | beforeCreated组件创建之前 created组件创建之后 beforeMount组件挂载到页面之前执行 mounted组件挂载到页面之后执行 beforeUpdate组件更新之前 updated组件更新之后 | set up开始创建组件 onBeforeMounted组件挂载到页面之前执行 onMounted组件挂载到页面之后执行 onBeforeUpdate组件更新之前执行 onUpdated组件更新之后 |
Object.defineProperty && Proxy
Object.defineProperty缺点:
只能针对对象的一个属性,也只能监听某个属性的第一层,如果对象里面还有对象是监听不了的,如果要对整个对象实行响应式需要自己去递归整个对象
无法监听数组的变化,Vue2对数组实现劫持的防方法是重写一些数组的方法
对象新增加的属性也不能劫持
let obj ={
name:'小明',
age:18,
obj1:{
sex:'男'
}
}
function watchProperty(obj,key,val){
Object.defineProperty(obj,key,{
get(){
console.log('get',key)
return val
},
set(value){
console.log('set')
val=value
}
})
}
function watchObj(obj){
object.keys(obj).forEach(key=>{
const value = obj[key]
if(value instanceof Object && !Array.isArray(value)){
watchObj(value)
}
watchProperty(obj,key,value)
})
}
watchObj(obj)
Proxy:
在Vue3中运用的是Proxy,他在目标对象之前架设一层“拦截”,外界对该对象的访问们都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写
补充:proxy也只能拦截一层对象,如果对象里面有新的对象就要用递归来劫持
let obj ={
name:'小明',
age:18,
obj1:{
sex:'男'
},
arr:[1,2,3,4,5,6]
}
function toProxy(obj){
object.keys(obj).forEach(key=>{
const value = obj[key]
if(value instanceof Object ){
obj[key] = toProxy(value)
}
})
return new Proxy(obj,{
get:function(taget,key){
console.log(key,'get')
return taget[key]
},
set(taget,key,val){
console.log(key,'set')
taget[key] = val
},
})
}
watch和watchEffect的区别
在 Vue3 中的组合式 API 中,watch 的作用和 Vue2 中的 watch 作用是一样的,他们都是用来监听响应式状态发生变化的,当响应式状态发生变化时,都会触发一个回调函数。
watch 基本用法
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<p>{{ message }}</p>
<button @click="changeMsg">更改 message</button>
</template>
<script setup lang="ts">
import { ref, watch } from "vue";
const message = ref("姓名");
watch(message, (newValue, oldValue) => {
console.log("新的值:", newValue);
console.log("旧的值:", oldValue);
});
const changeMsg = () => {
message.value = "张三";
};
</script>
上段代码中我们点击按钮就会更改响应式变量 message 的值。我们又使用 watch 监听器监听了 message 变量,当它发生变化时,就会触发 watch 监听函数中的回调函数,并且回调函数默认接收两个参数:新值和旧值。
watch 监听类型
前面我们一直强调 watch 监听的是响应式数据,如果我们监听的数据不是响应式的,那么可能会抛出警告
1、ref 和计算属性
ref 定义的数据我们是可以监听到的,因为我们前面的代码以及证明了。除此之外,计算属性也是可以监听到的,比如下列代码:
2、getter 函数
这里的 getter 函数大家可以简单的理解为获取数据的一个函数,说白了该函数就是一个返回值的操作,有点类似与计算属性。
3、监听响应式对象
4、监听多个来源的数组
const x1 = ref(12);
const number = reactive({ count: 0 });
const countAdd = () => {
number.count++;
};
watch([x1, () => number.count], (newValue, oldValue) => {
console.log("新的值:", newValue);
console.log("旧的值:", oldValue);
});
3.watchEffect
我们前面使用 watch 监听数据状态时,不知道大家有没有发现这样一个问题:只有当我们监听的数据源发生了变化,监听函数的回调函数才会执行。但是需求总是多变的,有些场景下我们可能需要刚进页面,或者说第一次渲染页面的时候,watch 监听器里面的回调函数就执行一遍。
watchEffect 也是一个监听器,只不过它不会像 watch 那样接收一个明确的数据源,它只接收一个回调函数。而在这个回调函数当中,它会自动监听响应数据,当回调函数里面的响应数据发生变化,回调函数就会立即执行。
所以我们可以将方式一中的代码使用 watchEffect 优雅的实现。
const number = reactive({ count: 0 });
const countAdd = () => {
number.count++;
};
watchEffect(()=>{
console.log("新的值:", number.count);
})
4.watch 和 watchEffect 区别
我们已经大概知道了 watch 和 watchEffect 的用法,那么它们之间的区别相信大家也了解了一些,这里我们总结一下它们之间的区别。
watch 和 watchEffect 都能监听响应式数据的变化,不同的是它们监听数据变化的方式不同。
watch 会明确监听某一个响应数据,而 watchEffect 则是隐式的监听回调函数中响应数据。
watch 在响应数据初始化时是不会执行回调函数的,watchEffect 在响应数据初始化时就会立即执行回调函数
手动停止监听器
通常来说,我们的一个组件被销毁或者卸载后,监听器也会跟着被停止,并不需要我们手动去关闭监听器。但是总是有一些特殊情况,即使组件卸载了,但是监听器依然存在,这个时候其实式需要我们手动关闭它的,否则容易造成内存泄漏。
比如下面这中写法,我们就需要手动停止监听器:
<script setup>
import { watchEffect } from 'vue'
// 它会自动停止
watchEffect(() => {})
// ...这个则不会!
setTimeout(() => {
watchEffect(() => {})
}, 100)
</script>
const unwatch = watchEffect(() => {})
// ...当该侦听器不再需要时
unwatch()