vue用到了哪些响应式
有三种方案:object.defineProperty、proxy、value setter
object.defineProperty
defineProperty API 作为 Vue 2 实现响应式的原理,它的语法中也有一些缺陷。比如在我们删除数组的时候,无法监听到,这也是为什么在 Vue 2 中,我们需要 $delete 一个专门的函数去删除数据。
proxy
Vue 3 的响应式机制是基于 Proxy 实现的。Proxy 的重要意义在于它解决了 Vue 2 响应式的缺陷。我们看下面的代码,在其中我们通过 new Proxy 代理了 obj 这个对象,然后通过 get、set 和 deleteProperty 函数代理了对象的读取、修改和删除操作,从而实现了响应式的功能。
let proxy = new Proxy(obj,{
get : function (target,prop) {
//监听访问动作
return target[prop]
},
set : function (target,prop,value) {
//监听赋值动作
target[prop] = value;
if(prop==='count'){
double = getDouble(value)
}
},
deleteProperty(target,prop){
//监听删除动作
delete target[prop]
if(prop==='count'){
double = NaN
}
}
})
console.log(obj.count,double)
proxy.count = 2
console.log(obj.count,double)
delete proxy.count
// 删除属性后,我们打印log时,输出的结果就会是 undefined NaN
console.log(obj.count,double)
从这里可以看出和 Vue 2 的 definePropery 类似,都可以监听赋值和访问动作,但Proxy 还完善了几个 definePropery 的缺陷,比如说可以监听到属性的删除。
Proxy 是针对对象来监听,而不是针对某个具体属性,所以不仅可以代理那些定义时不存在的属性,还可以代理更丰富的数据结构,比如 Map、Set 等,并且我们也能通过 deleteProperty 实现对删除操作的代理。有了 Proxy 后,响应式机制就比较完备了。
get set
在 Vue 3 中还有另一个响应式实现的逻辑,就是利用对象的 get 和 set 函数来进行监听,这种响应式的实现方式,只能拦截某一个属性的修改,这也是 Vue 3 中 ref 这个 API 的实现。在下面的代码中,我们拦截了 count 的 value 属性,并且拦截了 set 操作,也能实现类似的功能。
reactive函数 & ref
reactive可以把一个对象变成响应式,是基于proxy实现的,用于复杂数据结构
ref实现原理,利用了valuesetter(对象的get set函数监听)只拦截属性操作,用于简单数据结构
三种实现原理的对比表格如下,帮助你理解三种响应式的区别。
watch和watchEffect的区别
1.watch可以访问新值和旧值,watchEffect不能访问。
2.watchEffect有副作用,DOM挂载或者更新之前就会触发,需要我们自己去清除副作用。
3.watch是惰性执行,也就是只有监听的值发生变化的时候才会执行,但是watchEffect不同,每次代码加载watchEffect都会执行。
4.watch需要指明监听的对象,也需要指明监听的回调。watchEffect不用指明监视哪一个属性,监视的回调函数中用到哪个属性,就监视哪个属性。
5.watchEffect数据发生更改就会执行
利用响应式机制封装可复用函数
需求:把数据存储到localstorge中,与响应式数据同步
我们可以直接抽离一个 useStorage 函数,在响应式的基础之上,把任意数据响应式的变化同步到本地存储。我们先看下面的这段代码,ref 从本地存储中获取数据,封装成响应式并且返回,watchEffect 中做本地存储的同步,useStorage 这个函数可以抽离成一个文件,放在工具函数文件夹中。
import {
ref,watchEffect} from 'vue'
/**
* 把 localStorage 简单地变成了响应式对象,实现数据的更新和 localStorage 的同步。
* @param {*} key 字段名称
* @returns 返回一个数组
*/
function useStorage(key){
let data = ref(JSON.parse(localStorage.getItem(key)) || []);
watchEffect(()=>{
// console.log('來了',data.value)
localStorage.setItem(key,JSON.stringify(data.value))
})
return data
}
export{
useStorage
}
使用方式如下:
let todos = useStorage('todos');//对返回值进行操作
Vueuse 工具包
Vue 社区中其实已经有一个类似的工具集合,也就是 VueUse,它把开发中常见的属性都封装成为响应式函数。
我们自己封装的 useStorage,算是把 localStorage 简单地变成了响应式对象,实现数据的更新和 localStorage 的同步。同理,我们还可以封装更多的类似 useStorage 函数的其他 use 类型的函数,把实际开发中你用到的任何数据或者浏览器属性,都封装成响应式数据,这样就可以极大地提高我们的开发效率。
在项目目录下打开命令行里,我们输入如下命令,来进行 VueUse 插件的安装:
npm install @vueuse/core
然后,我们就先来使用一下 VueUse。在下面这段代码中,我们使用 useFullscreen 来返回全屏的状态和切换全屏的函数。这样,我们就不需要考虑浏览器全屏的 API,而是直接使用 VueUse 响应式数据和函数就可以很轻松地在项目中实现全屏功能。
<template>
<h1 @click="toggle">click</h1>
</template>
<script setup>
import {
useFullscreen } from '@vueuse/core'
const {
isFullscreen, enter, exit, toggle } = useFullscreen()
</script>
useFullscreen 的封装逻辑和 useStorage 类似,都是屏蔽了浏览器的操作,把所有我们需要用到的状态和数据都用响应式的方式统一管理,VueUse 中包含了很多我们常用的工具函数,我们可以把网络状态、异步请求的数据、动画和事件等功能,都看成是响应式的数据去管理。