​Vue3响应式原理

目录

手动收集依赖+通知更新

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 = 300effect1() // 再执行一次
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 = 300trigger() // 通知变量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 = fnactiveEffect()activeEffect = null // 执行后立马变成null
}
function track(target, key) {// 如果此时activeEffect为null则不执行下面// 这里判断是为了避免例如console.log(person.name)而触发trackif (!activeEffect) returnlet 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) // 400num1.value = 10console.log(sum1.value) // 80
console.log(sum2.value) // 800num2.value = 16console.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.mzph.cn/news/122304.shtml

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

相关文章

uniapp 中添加 vconsole

uniapp 中添加 vconsole 一、安装 vconsole npm i vconsole二、使用 vconsole 在项目的 main.js 文件中添加如下内容 // #ifdef H5 // 提交前需要注释 本地调试使用 import * as vconsole from "vconsole"; new vconsole() // 使用 vconsole // #endif三、成功

Ansible简介

环境 控制节点&#xff1a;Ubuntu 22.04Ansible 2.10.8管理节点&#xff1a;CentOS 8 组成 Ansible环境主要由三部分组成&#xff1a; 控制节点&#xff08;Control node&#xff09;&#xff1a;安装Ansible的节点&#xff0c;在此节点上运行Ansible命令管理节点&#xff…

Android 系统架构

目录 Android 系统架构 1. Android 应用层 2. Android应用框架层 2.1 Activity Manager &#xff08;活动管理器&#xff09; 2.2 Window Manager &#xff08;窗口管理器&#xff09; 2.3 Content Provider (内容提供器) 2.4 View System&#xff08;视图系统&a…

Leetcode刷题详解——搜索插入位置

1. 题目链接&#xff1a;35. 搜索插入位置 2. 题目描述&#xff1a; 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。…

JVM调优(10)JVM的运行时数据区

一、概述 对于 C C 来说&#xff0c;在内存管理领域&#xff0c;JVM既拥有最高的权利&#xff0c;但是同时他们又是从事最基础工作的劳动人员&#xff0c;因为他们担负着每一个对象从开始到结束的维护责任。而对于Java来说&#xff0c;再虚拟机自动内存管理的帮助下&#xff0…

x210项目重新回顾之十七升级到linux4.19.114 +buildroot2018再讨论

代码参考https://github.com/colourfate/x210_bsp/ 他的是linux_4.10(dtb为 s5pv210-x210..dtb)我打算用linux4.19.114(dtb为 s5pv210-smdkv210.dtb) &#xff0c;所以修改build.sh ------------------------------------------------------------------------------ 5 M…

STM32 CAN使用

STM32 CAN使用 简介各种通讯接口对比报文总线上的报文信息表示为几种固定的赖类型数据帧列表模式掩码模式配置CAN配置参数位时序 简介 控制器局域网CAN&#xff08;Controller Area Network)是由德国博世公司为汽车应用而开发的多主机局部网络&#xff0c;用于汽车的监测和控制…

智能水厂运行与调控3D模拟仿真在线展示提高整个系统的协同效应

水厂在生活中的重要性不可忽视。它们提供清洁、安全的水源&#xff0c;满足人们饮用、洗浴、烹饪等基本需求&#xff0c;保障公共卫生&#xff0c;预防疾病传播;同时&#xff0c;水厂也促进经济发展&#xff0c;为工业生产和农业灌溉提供保障&#xff0c;吸引和支持企业的投资和…

phar反序列化学习

PHP反序列化常见的是使用unserilize()进行反序列化&#xff0c;除此之外还有其它的反序列化方法&#xff0c;不需要用到unserilize()。就是用到phar反序列化。 Phar phar文件 Phar是将php文件打包而成的一种压缩文档&#xff0c;类似于Java中的jar包。它有一个特性就是phar文…

React Router初学者入门指南(2023版)

React Router&#xff0c;简单来说&#xff0c;是一个帮助处理React应用程序中导航和路由的库。它是用于管理React中路由的最流行的路由工具。如果你对路由的概念不熟悉&#xff0c;可以将其视为在网站的不同部分之间导航的过程。例如&#xff0c;当你进入网站的“联系我们”部…

Android系统的特性

目录 Android系统的特性 1. 显示布局 2. 数据存储 3. 网络 4. 信息 5. 浏览器 6. 编程语言支持 7. 媒体支持 8. 流媒体支持 9. 硬件支持 10. 多点触控 11.蓝牙 12. 多任务处理 13. 语音功能 14.无线共享功能 15. 截图功能 16. 跨平台 17. 应用程序的安全机制…

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://…

No authorization token was found

今天遇到了一个问题&#xff0c;我把前后端逻辑都理了一遍&#xff0c;开始怀疑后端&#xff0c;后端肯定没错了&#xff0c;把前端理了一遍&#xff0c;ok前后端没错&#xff0c;我错。登录哪里需要的token&#xff1f;&#xff1f;&#xff1f;&#xff1f;把我搞懵逼了。 测…

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

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

Capacitor 打包 h5 到 Android 应用,uniapp https http net::ERR_CLEARTEXT_NOT_PERMITTED

Capacitor 打包 h5 到 Android 应用&#xff0c;uniapp https http net::ERR_CLEARTEXT_NOT_PERMITTED capacitor 官网&#xff1a; https://capacitorjs.com/docs/ 项目上需要做一个 app&#xff0c;而这个 app 是用 uniapp 做的&#xff0c;里面用到了一个依赖 dom 的库&…

Android官方ShapeableImageView描边/圆形/圆角图,xml布局实现

Android官方ShapeableImageView描边/圆形/圆角图&#xff0c;xml布局实现 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://schemas.android.…

IDEA新建maven项目,使用mybatis操作数据库完整过程

IDEA新建maven项目&#xff0c;使用mybatis操作数据库完整过程 一、IDEA新建maven项目二、配置mybatis三、创建表对应实体类四、创建mapper接口五、使用mybatis操作数据库 前提&#xff1a; 这个教程是在maven项目中使用mybatis进行数据库操作&#xff0c;不是在spring boot项目…

接口自动化测试工具,Postman使用详解

一、概念 1、Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件&#xff0c;Postman分为Postman native app和Postman Chrome app两个版本。目前Chrome app已经停止维护&#xff0c;官方也不推荐使用该版本。 2、官网下载地址&#xff1a;http://www.getpostman…

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

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

uniapp把文件中的内复制到另一个文件中

使用的是Html 5的plus.io.resolveLocalFileSystemURL方法&#xff0c;文档&#xff1a;HTML5 API Reference var soursePath file:///storage/emulated/0/a/;//用于读取var removePath file:///storage/emulated/0/w/;//用于移除w这个文件夹var targetPath file:///storage/…