专栏分享:vue2源码专栏vue3源码专栏vue router源码专栏玩具项目专栏,硬核推荐

欢迎各位ITer关注点赞收藏

在学习 Vue3 是如何进行对象的响应式代理之前,我想我们应该先去了解下 ES6 新增的API ProxyReflect,可参考【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)的更多相关文章

  1. vue 源码自问自答-响应式原理

    vue 源码自问自答-响应式原理 最近看了 Vue 源码和源码分析类的文章,感觉明白了很多,但是仔细想想却说不出个所以然. 所以打算把自己掌握的知识,试着组织成自己的语言表达出来 不打算平铺直叙的写清 ...

  2. Vue 源码解析:深入响应式原理(上)

    原文链接:http://www.imooc.com/article/14466 Vue.js 最显著的功能就是响应式系统,它是一个典型的 MVVM 框架,模型(Model)只是普通的 JavaScri ...

  3. 由浅入深,带你用JavaScript实现响应式原理(Vue2、Vue3响应式原理)

    由浅入深,带你用JavaScript实现响应式原理 前言 为什么前端框架Vue能够做到响应式?当依赖数据发生变化时,会对页面进行自动更新,其原理还是在于对响应式数据的获取和设置进行了监听,一旦监听到数 ...

  4. vue3响应式原理以及ref和reactive区别还有vue2/3生命周期的对比,第二天

    前言: 前天我们学了 ref 和 reactive ,提到了响应式数据和 Proxy ,那我们今天就来了解一下,vue3 的响应式 在了解之前,先复习一下之前 vue2 的响应式原理 vue2 的响应 ...

  5. 第三十六篇:vue3响应式(关于Proxy代理对象,Reflect反射对象)

    好家伙,这个有点难. 1.代理对象Proxy Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找.赋值.枚举.函数调用等). 拦截对象中任意属性的变化,包括:查get, ...

  6. 大白话Vue源码系列(01):万事开头难

    阅读目录 Vue 的源码目录结构 预备知识 先捡软的捏 Angular 是 Google 亲儿子,React 是 Facebook 小正太,那咱为啥偏偏选择了 Vue 下手,一句话,Vue 是咱见过的 ...

  7. 【Vue2.x源码系列07】监听器watch原理

    上一章 Vue2计算属性原理,我们介绍了计算属性是如何实现的?计算属性缓存原理?以及洋葱模型是如何应用的? 本章目标 监听器是如何实现的? 监听器选项 - immediate.deep 内部实现 初始 ...

  8. java官网门户源码 SSM框架 自适应-响应式 freemarker 静态模版引擎

    来源:http://www.fhadmin.org/webnewsdetail3.html 前台:支持(5+1[时尚单页风格])六套模版,可以在后台切换 官网:www.fhadmin.org 系统介绍 ...

  9. Mybaits 源码解析 (五)----- 面试源码系列:Mapper接口底层原理(为什么Mapper不用写实现类就能访问到数据库?)

    刚开始使用Mybaits的同学有没有这样的疑惑,为什么我们没有编写Mapper的实现类,却能调用Mapper的方法呢?本篇文章我带大家一起来解决这个疑问 上一篇文章我们获取到了DefaultSqlSe ...

  10. vue2响应式原理与vue3响应式原理对比

    VUE2.0 核心 对象:通过Object.defineProtytype()对对象的已有属性值的读取和修改进行劫持 数组:通过重写数组更新数组一系列更新元素的方法来实现元素的修改的劫持 Object ...

随机推荐

  1. enumerate()使用方法

    enumerate()(单词意思是枚举的意思)是python中的内置函数, enumerate(X,[start=0]) 函数中的参数X可以是一个迭代器(iterator)或者是一个序列, start ...

  2. cesium 绑定dom弹窗(跟随模型)

    https://blog.csdn.net/qq_36266612/article/details/109648367?utm_term=cesium%E6%80%8E%E4%B9%88%E6%B7% ...

  3. gulp来处理html、css、js资源啦

    gulp 一般用于处理自动化任务,默认情况无法处理模块化,也不会用于大型项目,但它可以使用各种插件来编译 html.css.js 等资源. 不清楚如何使用 gulp 开启任务的朋友可以参考 gulp使 ...

  4. 【技术积累】Linux中的命令行【理论篇】【五】

    arpd命令 命令介绍 arpd命令是Linux系统中的一个网络工具,用于管理和操作ARP(地址解析协议)缓存.ARP协议用于将IP地址映射到MAC地址,以便在局域网中进行通信. 命令说明 arpd命 ...

  5. [ansible]建立ssh互信

    创建密钥 # 创建基于rsa算法的密钥,也可以创建ed25519算法的密钥,性能比rsa高 # 一般直接回车即可 ssh-keygen -t rsa 少量建立互信 如果主机数不多的话,可以手动建立互信 ...

  6. VictoriaLogs:一款超低占用的 ElasticSearch 替代方案

    背景 前段时间我们想实现 Pulsar 消息的追踪流程,追踪实现的效果图如下: 实现其实比较简单,其中最重要的就是如何存储消息. 消息的读取我们是通过 Pulsar 自带的 BrokerInterce ...

  7. PicGo+Github图床配置

    为了将 PicGo 设置为使用 GitHub 作为图床,您需要先创建一个 GitHub 仓库用于存储图片,然后在 PicGo 中进行相应的配置.您已经创建了一个仓库,所以让我们来配置 PicGo. 安 ...

  8. 推荐一个react上拉加载更多插件:react-infinite-scroller

    在开发网页和移动应用时,经常需要处理大量数据的展示和加载.如果数据量非常大,一次性全部加载可能会导致页面卡顿或崩溃.为了解决这个问题,我们可以使用无限滚动(Infinite Scroll)的技术.Re ...

  9. 研发效能|DevOps 是运维还是开发?

    DevOps 到底是 Dev还是Ops?答:属于研发工程师序列,偏向研发域,而不是运维域. DevOps是研发工程师 DevOps 主要服务的对象就是所有产研团队的人员,与产研团队打交道比较多,相互配 ...

  10. 文心一言 VS 讯飞星火 VS chatgpt (96)-- 算法导论9.3 1题

    一.用go语言,在算法 SELECT 中,输人元素被分为每组 5 个元素.如果它们被分为每组 7个元素,该算法仍然会是线性时间吗?证明:如果分成每组 3 个元素,SELECT 的运行时间不是线性的. ...