【源码系列#01】vue3响应式原理(Proxy)
专栏分享:vue2源码专栏,vue3源码专栏,vue router源码专栏,玩具项目专栏,硬核推荐
欢迎各位ITer关注点赞收藏
在学习 Vue3 是如何进行对象的响应式代理之前,我想我们应该先去了解下 ES6 新增的API Proxy
与 Reflect
,可参考【Vue3响应式入门#02】Proxy and Reflect 。之后我们再手写下 reactive 和 effect 的源码
Reactive
定义: 接收一个普通对象然后返回该普通对象的响应式代理。等同于 2.x 的 Vue.observable()
const obj = reactive({ count: 0 })
响应式转换是“深层的”:会影响对象内部所有嵌套的属性。基于 ES6 的 Proxy 实现,返回的代理对象不等于原始对象。建议仅使用代理对象而避免依赖原始对象。
reactive.ts
Vue3中响应数据核心是 reactive , reactive 中的实现是由 proxy 加 effect 组合,先来看一下 reactive 方法的定义
import { isObject } from '@vue/shared'
import { mutableHandlers, ReactiveFlags } from './baseHandler'
// key只能是对象;弱引用,更有效的垃圾回收、释放内存 - https://www.zhangxinxu.com/wordpress/2021/08/js-weakmap-es6/
const reactiveMap = new WeakMap()
/**
* @desc 将数据转化成响应式的数据
*/
export function reactive(target) {
// issue1
if (!isObject(target)) {
return
}
// issue2
if (target[ReactiveFlags.IS_REACTIVE]) {
return target
}
// issue3
let existingProxy = reactiveMap.get(target)
if (existingProxy) {
return existingProxy
}
const proxy = new Proxy(target, mutableHandlers)
reactiveMap.set(target, proxy)
return proxy
}
@issue1 只能做对象的代理,不是对象,return
@issue2 代理对象被再次代理 可以直接返回代理对象
我们可以利用 Proxy 的 get方法,来判断他有没有代理过
如果访问这个对象的
__v_isReactive
属性,有值就说明代理过了,当然,我们可以约定__v_isReactive
为任何字段@issue3 同一个对象代理多次,返回同一个代理
用 WeakMap去缓存对象和代理对象的映射关系
代理完成时,将此对象和代理对象添加到 WeakMap缓存中;在代理之前,去 WeakMap中读取此对象是否有代理对象的映射,若存在,则返回缓存中的代理对象
WeakMap:key只能是对象;弱引用,更有效的垃圾回收、释放内存,详情请参考JS WeakMap应该什么时候使用 « 张鑫旭-鑫空间-鑫生活
baseHandler.ts
mutableHandlers 是 Proxy 的第二个参数 handler对象提取封装而来
- track () 依赖收集
- trigger () 触发依赖
这两个函数为 effect 里的方法,effect 为 reactive 的核心,后面我们会详细介绍。先来看一下mutableHandlers 对象的定义
import { track, trigger } from './effect'
export const enum ReactiveFlags {
IS_REACTIVE = '__v_isReactive',
}
export const mutableHandlers = {
// 这里可以监控到用户取值了
get(target, key, receiver) {
if (key === ReactiveFlags.IS_REACTIVE) {
return true
}
track(target, 'get', key)
let res = Reflect.get(target, key, receiver)
// @issue1
// 深度代理实现, 性能好 取值就可以进行代理
if (isObject(res)) {
return reactive(res)
}
return res
},
// 这里可以监控到用户设置值了
set(target, key, value, receiver) {
let oldValue = target[key] // 缓存老值
let result = Reflect.set(target, key, value, receiver)
if (oldValue !== value) {
// 值变化了,触发依赖
trigger(target, 'set', key)
}
return result
},
}
@issue1 嵌套对象深度代理。只有在取值时,才会进行深度代理,性能好
举个例子,看如下代码。当我们对嵌套对象
product.rate
进行取值时,就会触发 get劫持,然后深度代理嵌套对象product.rate
const product = reactive({
price: 5,
quantity: 2,
rate: {
value: 0.9
}
})
shared.ts
共享模块
// 判断是否是JS对象
export const isObject = function(value){
return typeof value === 'object' && value !== null
}
参考资料
JS WeakMap应该什么时候使用 « 张鑫旭-鑫空间-鑫生活
【源码系列#01】vue3响应式原理(Proxy)的更多相关文章
- vue 源码自问自答-响应式原理
vue 源码自问自答-响应式原理 最近看了 Vue 源码和源码分析类的文章,感觉明白了很多,但是仔细想想却说不出个所以然. 所以打算把自己掌握的知识,试着组织成自己的语言表达出来 不打算平铺直叙的写清 ...
- Vue 源码解析:深入响应式原理(上)
原文链接:http://www.imooc.com/article/14466 Vue.js 最显著的功能就是响应式系统,它是一个典型的 MVVM 框架,模型(Model)只是普通的 JavaScri ...
- 由浅入深,带你用JavaScript实现响应式原理(Vue2、Vue3响应式原理)
由浅入深,带你用JavaScript实现响应式原理 前言 为什么前端框架Vue能够做到响应式?当依赖数据发生变化时,会对页面进行自动更新,其原理还是在于对响应式数据的获取和设置进行了监听,一旦监听到数 ...
- vue3响应式原理以及ref和reactive区别还有vue2/3生命周期的对比,第二天
前言: 前天我们学了 ref 和 reactive ,提到了响应式数据和 Proxy ,那我们今天就来了解一下,vue3 的响应式 在了解之前,先复习一下之前 vue2 的响应式原理 vue2 的响应 ...
- 第三十六篇:vue3响应式(关于Proxy代理对象,Reflect反射对象)
好家伙,这个有点难. 1.代理对象Proxy Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找.赋值.枚举.函数调用等). 拦截对象中任意属性的变化,包括:查get, ...
- 大白话Vue源码系列(01):万事开头难
阅读目录 Vue 的源码目录结构 预备知识 先捡软的捏 Angular 是 Google 亲儿子,React 是 Facebook 小正太,那咱为啥偏偏选择了 Vue 下手,一句话,Vue 是咱见过的 ...
- 【Vue2.x源码系列07】监听器watch原理
上一章 Vue2计算属性原理,我们介绍了计算属性是如何实现的?计算属性缓存原理?以及洋葱模型是如何应用的? 本章目标 监听器是如何实现的? 监听器选项 - immediate.deep 内部实现 初始 ...
- java官网门户源码 SSM框架 自适应-响应式 freemarker 静态模版引擎
来源:http://www.fhadmin.org/webnewsdetail3.html 前台:支持(5+1[时尚单页风格])六套模版,可以在后台切换 官网:www.fhadmin.org 系统介绍 ...
- Mybaits 源码解析 (五)----- 面试源码系列:Mapper接口底层原理(为什么Mapper不用写实现类就能访问到数据库?)
刚开始使用Mybaits的同学有没有这样的疑惑,为什么我们没有编写Mapper的实现类,却能调用Mapper的方法呢?本篇文章我带大家一起来解决这个疑问 上一篇文章我们获取到了DefaultSqlSe ...
- vue2响应式原理与vue3响应式原理对比
VUE2.0 核心 对象:通过Object.defineProtytype()对对象的已有属性值的读取和修改进行劫持 数组:通过重写数组更新数组一系列更新元素的方法来实现元素的修改的劫持 Object ...
随机推荐
- [django]路由变量与正则表达式
示例: urlpatterns = [ path('detail.<int:id>.html', detailView, name='detail'), ] 路由变量的类型 示例路由配置了 ...
- 清理MySQL的binlog日志
前言 MySQL的binlog是以二进制形式打印的日志,没设置自动删除的话,时间长了就会占用大量存储空间.删除MySQL的binlog日志有两种方法:自动删除和手动删除. 自动删除 永久生效:修改My ...
- 利用pytorch自定义CNN网络(一):torchvision工具箱
本文是利用pytorch自定义CNN网络系列的第一篇,主要介绍 torchvision工具箱及其使用,关于本系列的全文见这里. 笔者的运行设备与软件:CPU (AMD Ryzen 5 4600U) + ...
- 2023CISCN华中赛区re
2023CISCN华中赛区re 当时出的题 misc3-babyandroid 找so文件,加密过程也不复杂 每三个一组进行加密 这里就是先每个减去65 然后 大概是 y1=(31x1)%26+65 ...
- 错过这5大AI绘画提示词平台,你会拍大腿!别问,直接收藏!
如今,AI绘画已经不再是简单的技术展示,而是逐渐转向了商业化的运营. 有的人利用AI生成的图片,再结合ChatGPT产生的文字,然后在平台上发布,这样就可以赚取平台的广告费. 其他一些变现操作参考之前 ...
- 从 DMAIC 方法论说起,记一个长链接 bug 的排查全过程
引 本文是我在前端团队的第四次分享,在过去我曾做过一次关于 bug 排查的全流程的排查分析,文档:客户线上反馈:从信息搜集到疑难 bug 排查全流程经验分享,但这个文章更侧重我过去几年的排查经验,而非 ...
- 每日一库:pprof简介
pprof简介 pprof是Go语言的一个性能分析库,它可以帮助开发者找出程序中的性能瓶颈.pprof提供了CPU分析.内存分析.阻塞分析等多种性能分析功能. 以下是pprof的主要特性: CPU分析 ...
- 白盒AES和SM4实现的差分故障分析
DFA攻击背景介绍 传统的密码安全性分析环境被称为黑盒攻击环境,攻击者只能访问密码系统的输入与输出,但随着密码系统部署环境的多样化,该分析模型已经不能够反映实际应用中攻击者的能力.2002年,Chow ...
- Graphviz入门
Graphviz可以用于状态机图的绘制 要绘制一张状态图,我们需要两个图形元素 结点,边 结点和边都有自己的属性 结点可以是圆.矩形.填充 边有粗细
- nmcli 命令设置网络
nmcli 命令设置网络 设置静态 IP 地址 sudo nmcli connection modify "连接名称" ipv4.addresses IP地址/子网掩码 设置网关 ...