理解vue3响应式机制(学习笔记)

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 中包含了很多我们常用的工具函数,我们可以把网络状态、异步请求的数据、动画和事件等功能,都看成是响应式的数据去管理。

猜你喜欢

转载自blog.csdn.net/qq_42107364/article/details/121888765