vue响应式原理上篇

前言

vue三大核心:组件,虚拟dom,响应式,那这里我们就来深入理解vue的响应式原理,并手写简单的响应式系统,让我们对vue的理解上升一个level。

先回顾一下ref和reactive

  • ref : 用于创建一个响应式的引用对象。ref 通常用于包装基本类型的值(如 numberstring 等),使其成为响应式对象。ref 返回的对象包含一个 value 属性,通过响应式系统[proxy(vue3)或Object.defineProperty(vue2)]监听.vule属性的访问或修改,触发自动更新。

    javascript

    csharp 复制代码
    import { ref } from 'vue';
    
    const count = ref(0);
    console.log(count.value); // 0
    count.value++; // 响应式更新
  • reactive : 用于创建一个响应式的对象。reactive 可以接收一个普通对象,并返回该对象的响应式代理。与 ref 不同,reactive 直接操作对象本身,而不是通过 value 属性。

    javascript

    javascript 复制代码
    import { reactive } from 'vue';
    
    const state = reactive({
      count: 0,
    });
    console.log(state.count); // 0
    state.count++; // 响应式更新

1. Vue 3 响应式原理概述

Vue 3 的响应式系统主要依赖于 ProxyReflect 这两个 ES6 特性。Proxy 可以拦截对象的读取和修改操作,而 Reflect 则提供了一组操作对象的方法,与 Proxy 配合使用可以实现对对象的深度监听。说人话就是:利用ES6的proxy数据代理,通过reactive()函数,给普通对象包裹一层proxy,通过代理对象来操作这个普通对象。先了解一下reactive()函数

reactive()函数的完整参数结构

javascript 复制代码
function reactive(target, handler) {
	  // 实现代码
	}
  • target:必需参数,要转换为响应式的对象。

  • handler:可选参数,包含拦截方法的配置对象,用于自定义响应式行为。

  • 默认行为:若不传递handler,Vue使用内置的mutableHandlers,包含基础的get/set拦截逻辑

  • 自定义覆盖:传递handler可覆盖默认方法(这里就不多讲),我们重点讲一下默认行为mutableHandlers

  • vue3源码当中的mutableHandlers(来自baseHandlers.ts定义)

js 复制代码
export const mutableHandlers: ProxyHandler<object> = {
get, 
set, 
deleteProperty,
has, 
ownKeys
};

这里我们关注get和set两个拦截器

举个例子:

.vue

js 复制代码
const target = { num1:2,num2:2}
const counter = reactive(target)
console.log(counter.num1)//读取:触发get拦截器
counter.num1 = 100;// 修改:触发set拦截器

如上面代码,普通对象(也叫目标对象叫他target):{num1:1,num2:2}被代理之后返回一个新的proxy对象:响应式对象,这里是counter,它代理了对target的所有操作。reactive()通过Proxy实现响应式,通过mutableHandlers(里面的get和set)劫持对象属性的读写操作。这里还不能很好理解get和set的作用,先不急看下文。

javascript 复制代码
// vue 可以在后端运行
// vue 也是模块化写出来的 reactivity模块
const { 
     // 从 @vue/reactivity 模块中导入 effect 函数,
    // 用于创建响应式副作用,当依赖的响应式数据变化时会重新执行
    effect,
    reactive // vue 响应式
 } = require("@vue/reactivity");
 // 不用挂载到页面上, node 下运行
 let dummy
 // 响应式
 const counter = reactive({
    num1:1,
    num2:2,
 })
 // effect 是一个函数, 里面的代码会立即执行
 // 接受一个函数作为参数
 // 自动更新的是 函数, {{}} computed 生命周期......
 effect( () =>{
    // proxy get 收集依赖
    dummy = counter.num1 + counter.num2
    console.log(dummy,'依赖被执行1');
 })

 effect( () =>{
   // 收集依赖
    console.log(dummy,'依赖被执行2');
 })


 setInterval(() => {
    // proxy set 触发更新
    counter.num1++
 },1000)

 setInterval(() =>{
   // 触发更新
    counter.num2++
 },5000)

以上代码:

  • get 拦截器负责在访问属性时收集依赖,也就是把依赖该属性的 effect 函数记录下来。
  • set 拦截器负责在属性值改变时触发更新,也就是让所有依赖该属性的 effect 函数重新执行。
  • 简而言之:get 用于收集依赖,set 用于触发已收集的依赖(这里是effect 函数)重新执行。
  • effect:在Vue的响应式系统里,effect 函数是核心组成部分,它的主要功能是创建响应式副作用。当依赖的响应式数据发生变化时,副作用函数会重新执行。(假设我有一个需求希望在{ num1:1, num2:2, }的属性值发生变化时输出相应的值和特定语句,那本例当中就通过响应式数据和effect完成了这个需求)。

运行一下 基于上面我们对于响应式原理有了一定的理解,那下面这张图将进一步加深我们的理解。

上图(所谓一图胜千言)

哇哦哇哦,这就很到位了,图中普通对象通过reactive()函数包裹一层proxy,当属性被读取时候(counter.num1+counter.num2),触发get,调用get的track方法,把efffect注入到依赖地图。当属性被修改时触发set,调用set的trigger方法把之前收集到并注入的efffect挨个执行。当然efffect只是依赖的一种,在实际应用当中有各种依赖比如:

  • 数据绑定:在前端开发中,经常需要将数据绑定到 DOM 元素上。例如,在 Vue 中,当一个组件的数据发生变化时,与之绑定的 DOM 元素会自动更新。
  • 计算属性:计算属性是根据其他数据计算得出的属性。当依赖的数据发生变化时,计算属性会自动重新计算 到这一步

那到这一步呢?对于响应式原理的理解就有十之八九了(当然,对于基本理解而言),下一章咱们直接撸源码,到时候面试被问到,就是你我表演的时刻。觉得有用就点个关注吧,会有更多源码解析题哟!

相关推荐
萌萌哒草头将军35 分钟前
⚡⚡⚡Vite 被发现存在安全漏洞🕷,请及时升级到安全版本
前端·javascript·vue.js
会功夫的李白1 小时前
Electron + Vite + Vue 桌面应用模板
javascript·vue.js·electron·vite·模版
小兵张健1 小时前
运用 AI,看这一篇就够了(上)
前端·后端·cursor
不怕麻烦的鹿丸2 小时前
node.js判断在线图片链接是否是webp,并将其转格式后上传
前端·javascript·node.js
bst@微胖子2 小时前
阿里云平台Vue项目打包发布
vue.js·阿里云
vvilkim2 小时前
控制CSS中的继承:灵活管理样式传递
前端·css
南城巷陌2 小时前
Next.js中not-found.js触发方式详解
前端·next.js
武昌库里写JAVA3 小时前
Vue3生态工具:Volar语言服务与Unplugin自动化导入配置
vue.js·spring boot·毕业设计·layui·课程设计
拉不动的猪3 小时前
前端打包优化举例
前端·javascript·vue.js
Bigger3 小时前
Tauri(十五)——多窗口之间通信方案
前端·rust·app