​Vue3响应式原理

news2024/12/22 13:24:33

目录

手动收集依赖+通知更新

effect():更改数据后执行,更新依赖该数据的数据(依赖)

track()收集依赖的effect()放进dep(set去重)

更新时触发trigger函数通知dep里所有effect()执行

当依赖的为object类型:WeakMap存obj,Map存obj.props

proxy:自动收集依赖+通知更新

new Proxy(target, handler)

Reflect

Reflect.get(target, propertyKey[, receiver])函数调用方式从对象中读值

reactive(被依赖的数据){return proxy}

vue3新增全局变量activeEffect存储当前执行的effect

track不必再判断obj和obj.key,直接dep存储当前activeEffect

应用

实现ref响应式

用ref实现computed

与Vue2区别

使用

初始化、监听:整个对象级别上进行拦截,无需遍历每个属性

性能:监听整个操作(不用遍历),不仅是读写

功能:可监听属性的增删

收集依赖

类型推导:TypeScript


手动收集依赖+通知更新

effect():更改数据后执行,更新依赖该数据的数据(依赖)

let name = '林三心', age = 22, money = 20
let myself = '', ohtherMyself = ''
const effect1 = () => myself = `${name}今年${age}岁,存款${money}元`
const effect2 = () => ohtherMyself = `${age}岁的${name}居然有${money}元`

effect1() // 先执行一次
effect2() // 先执行一次
console.log(myself) // 林三心今年22岁,存款20元
console.log(ohtherMyself) // 22岁的林三心居然有20元
money = 300

effect1() // 再执行一次
effect2() // 再执行一次

console.log(myself) // 林三心今年22岁,存款300元
console.log(ohtherMyself) // 22岁的林三心居然有300元

track()收集依赖的effect()放进dep(set去重)

更新时触发trigger函数通知dep里所有effect()执行

let name = '林三心', age = 22, money = 20
let myself = '', ohtherMyself = ''
const effect1 = () => myself = `${name}今年${age}岁,存款${money}元`
const effect2 = () => ohtherMyself = `${age}岁的${name}居然有${money}元`

const dep = new Set()
function track () {
    dep.add(effect1)
    dep.add(effect2)
}
function trigger() {
    dep.forEach(effect => effect())
}
track() //收集依赖
effect1() // 先执行一次
effect2() // 先执行一次
console.log(myself) // 林三心今年22岁,存款20元
console.log(ohtherMyself) // 22岁的林三心居然有20元
money = 300

trigger() // 通知变量myself和otherMyself进行更新

console.log(myself) // 林三心今年22岁,存款300元
console.log(ohtherMyself) // 22岁的林三心居然有300元

当依赖的为object类型:WeakMap存obj,Map存obj.props

const targetMap = new WeakMap()
function track(target, key) {
    let depsMap = targetMap.get(target)
    if (!depsMap) {
        targetMap.set(target, depsMap = new Map())
    }

    let dep = depsMap.get(key)
    if (!dep) {
        depsMap.set(key, dep = new Set())
    }

...
}
function trigger(target, key) {
    let depsMap = targetMap.get(target)
    if (depsMap) {
        const dep = depsMap.get(key)
        if (dep) {
            dep.forEach(effect => effect())
        }
    }
}

proxy:自动收集依赖+通知更新

new Proxy(target, handler)

target

包装target (对象/数组/函数甚/proxy对象)

handler

被代理对象上的自定义行为(定义一组处理函数(例如get、set)的对象)

target:被代理者
prop:被代理者的属性
receiver:代理者 ,Proxy 或者继承 Proxy 的对象

const person = { name: '林三心', age: 22 }

const proxyPerson = new Proxy(person, {
    get(target, key, receiver) {
        return target[key]
    },
    set(target, key, value, receiver) {
        target[key] = value
    }
})

console.log(proxyPerson.name) // 林三心

proxyPerson.name = 'sunshine_lin'

console.log(proxyPerson.name) // sunshine_lin

Reflect

Proxy和Reflect的方法都是一一对应的,在Proxy里使用Reflect会提高语义化

  • Proxy的get对应Reflect.get
  • Proxy的set对应Reflect.set

属性访问方法

A.属性访问器(访问):obj.key,obj[key]

B.函数调用:mp.get(key)

Reflect.get(target, propertyKey[, receiver])函数调用方式从对象中读值

receiver

如果target对象中指定了getterreceiver则为getter调用时的this

该方法会拦截目标对象的以下操作:

  • 访问属性:proxy[foo] 和 proxy.bar
  • 访问原型链上的属性:Object.create(proxy)[foo]

  • Reflect.get()
const person = { name: '林三心', age: 22 }

const proxyPerson = new Proxy(person, {
    get(target, key, receiver) {
        return Reflect.get(receiver, key) // 相当于 receiver[key]
    },
    set(target, key, value, receiver) {
        Reflect.set(receiver, key, value) // 相当于 receiver[key] = value
    }
})

console.log(proxyPerson.name)

proxyPerson.name = 'sunshine_lin' 
// 会直接报错,栈内存溢出 Maximum call stack size exceeded

reactive(被依赖的数据){return proxy}

function reactive(target) {
    const handler = {
        get(target, key, receiver) {
            track(receiver, key) // 访问时收集依赖
            return Reflect.get(target, key, receiver)
        },
        set(target, key, value, receiver) {
            Reflect.set(target, key, value, receiver)
            trigger(receiver, key) // 设值时自动通知更新
        }
    }

    return new Proxy(target, handler)
}

const person = reactive({ name: '林三心', age: 22 }) // 传入reactive
const animal = reactive({ type: 'dog', height: 50 }) // 传入reactive

...

vue3新增全局变量activeEffect存储当前执行的effect

track不必再判断obj和obj.key,直接dep存储当前activeEffect

let activeEffect = null
function effect(fn) {
    activeEffect = fn
    activeEffect()
    activeEffect = null // 执行后立马变成null
}
function track(target, key) {
    // 如果此时activeEffect为null则不执行下面
    // 这里判断是为了避免例如console.log(person.name)而触发track
    if (!activeEffect) return
    let depsMap = targetMap.get(target)
    if (!depsMap) {
        targetMap.set(target, depsMap = new Map())
    }

    let dep = depsMap.get(key)
    if (!dep) {
        depsMap.set(key, dep = new Set())
    }
    dep.add(activeEffect) // 把此时的activeEffect添加进去
}

// 每个effect函数改成这么执行
effect(effectNameStr1)
effect(effectNameStr2)
effect(effectAgeStr1)
effect(effectAgeStr2)
effect(effectTypeStr1)
effect(effectTypeStr2)
effect(effectHeightStr1)
effect(effectHeightStr2)

应用

实现ref响应式

function ref (initValue) {
    return reactive({
        value: initValue
    })
}

let num = ref(5)//num就会成为一个响应式的数据
console.log(num.value) // 5

用ref实现computed

function computed(fn) {
    const result = ref()
    effect(() => result.value = fn()) // 执行computed传入函数
    return result
}


let num1 = ref(5)
let num2 = ref(8)
let sum1 = computed(() => num1.value * num2.value)
let sum2 = computed(() => sum1.value * 10)

console.log(sum1.value) // 40
console.log(sum2.value) // 400

num1.value = 10

console.log(sum1.value) // 80
console.log(sum2.value) // 800

num2.value = 16

console.log(sum1.value) // 160
console.log(sum2.value) // 1600

与Vue2区别

使用

Vue2 中只要写在组件中 data 函数返回的对象里的属性 自动就有响应式

Vue3 则是通过 ref 定义普通类型响应式和 reactive 定义复杂类型响应式数据

<script setup>
  import { ref, reactive, toRefs } from "vue"
  const name = ref('沐华')
  const obj = reactive({ name: '沐华' })
  const data = { ...toRefs(obj) }
</script>

通过 toRefs 可以把响应式对象转为普通对象
因为使用 reactive 定义的响应式对象在进行解构(展开)或者销毁的时候,响应式就会失效了,因为 reactive 实例下有很多属性解构就丢失了,所以在需要解构且保持响应式的时候就可以用 toRefs

初始化、监听:整个对象级别上进行拦截,无需遍历每个属性

性能:监听整个操作(不用遍历),不仅是读写

Proxy可以监听对象的整个操作,而不仅仅是属性的读写操作,这使得数据变化的检测更加高效。Vue2中,需要递归地遍历对象的所有属性,而Proxy可以一次性监听整个对象的操作。

功能:可监听属性的增删

Proxy可以监听新增属性和删除属性的操作,而Object.defineProperty只能监听已有属性的读写操作

收集依赖

Vue2 中是通过 ObserverDepWatcher 这三个类来实现依赖收集

Vue3 中是通过 track 收集依赖,通过 trigger 触发更新,本质上就是用 WeakMap,Map,Set 来实现

类型推导:TypeScript

由于Proxy是基于原生的Proxy对象实现的,所以可以更好地支持TypeScript等静态类型检查工具,提供更准确的类型推导和代码提示。

​Vue2响应式原理

林三心画了8张图,最通俗易懂的Vue3响应式核心原理解析 - 掘金

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1145808.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

代码训练营第53天:动态规划part12|leetcode309买卖股票的最佳时期含冷静期|leetcode714买卖股票的最佳时机含手续费

leetcode309&#xff1a;买卖股票的最佳时机含冷冻期 文章讲解&#xff1a;leletcode309 leetcode714&#xff1a;买卖股票的最佳时机含手续费 文章讲解&#xff1a;leetcode714 目录 1&#xff0c;leetcode309 买卖股票的最佳时机含冷冻期 2&#xff0c;leetcode714 买卖股票…

Qt中实现页面切换的两种方式

文章目录 方式一 &#xff1a;使用QStackedWidget讲解代码结构main.cpp完整代码运行结果&#xff1a; 方式二 &#xff1a;代码结构完整代码mainwindow.hnewmainwindow.hmain.cppmainwindow.cppnewmainwindow.cppmainwindow.uinewmainwindow.ui 效果 方式一 &#xff1a;使用QS…

目标检测YOLO实战应用案例100讲-改进YOLOv4的遥感图像目标检测

目录 前言 遥感图像目标检测国内外研究现状 传统遥感图像目标检测算法 深度学习遥感图像目标检测算法 2目标检测相关理论基础 2.1卷积神经网络 2.1.1基础理论 2.1.2卷积神经网络训练参数优化方式 2.2目标检测经典算法 2.2.1 两阶段目标检测算法 2.2.2 单阶段…

python opencv之图像分割、计算面积

以下代码是一个基于K-means聚类算法进行图像分割的实现。通过读取一个彩色图像&#xff0c;将其转化为二维数组形式。然后使用K-means算法对像素点进行聚类&#xff0c;聚类个数为7。根据聚类后的标签值对像素点进行着色&#xff0c;并创建掩膜图像。接着使用形态学开运算和闭运…

071:mapboxGL上传含shp的zip文件,在map上解析显示图形

第071个 点击查看专栏目录 本示例是介绍演示如何在vue+mapbox中上传含有shp文件的zip,在地图上显示图形。这里先通过上传解压解析,转换生成geojson文件,然后在地图上渲染图形。 直接复制下面的 vue+mapbox源代码,操作2分钟即可运行实现效果 文章目录 示例效果所用的zip文…

嵌入式开发

C语言 #include<stdio.h> #include<cstdio> volatile unsigned int TimeOut ; unsigned int cnt 0;//使用volatile变量情景&#xff1a; //1、中断 //2、多线程 //3、寄存器void isr_handle(int a) {cnt;if (cnt > 100){TimeOut 1;cnt 0;printf("cnt %…

JVM虚拟机详解

目录 01JVM由哪些部分组成/运行流程 什么是程序计数器 详细介绍堆 介绍方法区&#xff08;Method Area&#xff09; 直接内存 虚拟机栈(Java Virtual machine Stacks) 垃圾回收是否涉及栈内存 栈内存分配越大越好吗 方法内的局部变量是否线程安全 什么情况下会导致栈…

openpnp - src - 配置文件载入过程的初步分析

文章目录 openpnp - src - 配置文件载入过程的初步分析概述笔记自己编译用的git版本报错截图问题1 - 怎么在调试状态下, 定位到抛异常的第一现场?结合单步调试找到的现场, 来分析报错的原因openpnp配置文件读取的流程END openpnp - src - 配置文件载入过程的初步分析 概述 从…

MATLAB 安装教程(最新最全图文详解)

目录 一.简介 二.安装步骤 软件&#xff1a;MATLAB版本&#xff1a;2022b语言&#xff1a;简体中文大小&#xff1a;19.37G安装环境&#xff1a;Win11/Win10硬件要求&#xff1a;CPU2.6GHz 内存8G(或更高&#xff09;下载通道①百度网盘丨64位下载链接&#xff1a; https://pa…

【转信创】银河麒麟:系统安全机制

银河麒麟执行脚本时一直显示权限不足&#xff0c;可能需要修改安全状态。 查看当前kysec的相关安全状态&#xff1a; getstatus 修改当前Kysec的相关安全状态&#xff1a; # 设置Kysec安全状态为软/强制模式&#xff1b; sudo setstatus softmode/normal # 关闭执行控制功能…

Flutter笔记:完全基于Flutter绘图技术绘制一个精美的Dash图标(中)

Flutter笔记 完全基于Flutter绘图技术绘制一个精美的Dart语言吉祥物Dash&#xff08;中&#xff09; 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://…

图神经网络及其在知识图谱的应用

一 应用领域 道路交通&#xff0c;动态预测 自动驾驶&#xff0c;无人机场景 化学&#xff0c;医疗等场景 物理模型相关 二 图基本模块定义 V Vertex点 E Edge 边&#xff08;向量&#xff09; U Global 图 &#xff08;例如&#xff1a;全局向量&#xff09; 无论事…

nodejs+vue全国公考岗位及报考人数分析

传统的搜索引擎尽管解决了信息搜索问题&#xff0c;但无法进行有效的数据分析和优质资源的获取。并且&#xff0c;人们的需求不同&#xff0c;数据的要求也不同。为了解决这一问题&#xff0c;定向抓取数据的爬虫诞生了。它的诞生把人们从重复性的劳动中解放出来&#xff0c;节…

macbook如何格式化硬盘 mac系统硬盘格式化需要选择哪种格式

很多mac用户想要格式化硬盘&#xff0c;却不知道正确的格式方法。小白用户看到如此多的硬盘格式却不知道选择哪个。今天小编就来教大家&#xff0c;macbook如何格式化硬盘&#xff0c;以及mac系统格式化硬盘&#xff0c;需要选择哪种格式。 一、macbook如何格式化硬盘 macboo…

利用AI Chat 将电子书自动截屏并保存成pdf文件

电子书如果要下载下来&#xff0c;无非就两种类型的方法&#xff0c;一种是从内部破解&#xff0c;通常是某些极客将软件破解成免费版&#xff0c;但是风险也大。另一种是从外部破解&#xff0c;就是截屏保存&#xff0c;然后将所有图片拼成pdf文件。 如果要将整本电子书截屏保…

Flutter笔记:完全基于Flutter绘图技术绘制一个精美的Dash图标(上)

Flutter笔记 完全基于Flutter绘图技术绘制一个精美的Dart语言吉祥物Dash&#xff08;上&#xff09; 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://…

测开 (Junit 单元测试框架)

目录 了解 Junit 引入相关依赖 1、Junit注解 Test BeforeEach、BeforeAll AfterEach && AfterAll 2、断言 1、Assertions - assertEquals 方法 2、Assertions - assertNotEquals 方法 3、Assertions - assertTrue && assertFalse方法 4、Assertions…

nodejs+vue人脸识别考勤管理系统的设计与实现-计算机毕业设计

根据分析&#xff0c;本系统主要有3个角色&#xff1a;管理员、用户、考勤系统。 &#xff08;1&#xff09;管理员&#xff1a;管理员信息的添加、删除、修改和查询&#xff0c;用户信息添加、删除、修改和查询。 &#xff08;2&#xff09;用户&#xff1a;用户的注册和登录&…

[红蓝攻防]MDOG(全新UI重制版)为Xss跨站而生,数据共享,表单劫持,URL重定向

说明 功能Cookie窃取表单劫持(钓鱼账密)重定向流量劫持多平台数据推送钉钉数据推送 运行窗口 ./dist目录下已生成exe文件,双击打开 Cookie窃取 点击运行服务,复制以上的payload,payload怎么变形那么你可已去混淆 payload在页面执行 受害者访问存在xss漏洞的页面时受到攻击,…

【软考】系统集成项目管理工程师(十)项目质量管理【3分】

一、质量概念 1、定义 国际&#xff1a;反应实体满足主体明确和隐含需求的能力的特性总和 国内&#xff1a;一组固有特性满足要求的程度 2、质量与等级 质量&#xff1a;作为实现的性能或成果&#xff0c;是一系列内在特性满足要求的程度ISO9000 等级&#xff1a;作为设计意…