【源码系列#01】Vue3响应式原理(Reactive)

专栏分享:vue2源码专栏vue3源码专栏vue router源码专栏玩具项目专栏,硬核💪推荐🙌 欢迎各位ITer关注点赞收藏🌸🌸🌸

在学习 Vue3 是如何进行对象的响应式代理之前,我想我们应该先去了解下 ES6 新增的API ProxyReflect,可参考【Vue3响应式入门#02】Proxy and Reflect 。之后我们再手写下 reactive 和 effect 的源码

Reactive

定义: 接收一个普通对象然后返回该普通对象的响应式代理。等同于 2.x 的 Vue.observable()

javascript 复制代码
const obj = reactive({ count: 0 })

响应式转换是"深层的":会影响对象内部所有嵌套的属性。基于 ES6 的 Proxy 实现,返回的代理对象不等于原始对象。建议仅使用代理对象而避免依赖原始对象。

reactive.ts

Vue3中响应数据核心是 reactive , reactive 中的实现是由 proxy 加 effect 组合,先来看一下 reactive 方法的定义

javascript 复制代码
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 对象的定义

javascript 复制代码
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

javascript 复制代码
const product = reactive({
  price: 5,
  quantity: 2,
  rate: {
    value: 0.9
  }
})

shared.ts

共享模块

javascript 复制代码
// 判断是否是JS对象
export const isObject = function(value){
  return typeof value === 'object' && value !== null
}

参考资料

JS WeakMap应该什么时候使用 << 张鑫旭-鑫空间-鑫生活

相关推荐
小北方城市网5 分钟前
JavaScript 实战 —— 实现一个简易的 TodoList(适合前端入门 / 进阶)
开发语言·前端·javascript
是上好佳佳佳呀5 分钟前
【前端(二)】CSS 知识梳理:从编写位置到选择器优先级
前端·css
倾颜31 分钟前
我是怎么把单 Tool Calling 升级成多 Tool Runtime 的
前端·后端·langchain
清汤饺子37 分钟前
Superpowers:给 AI 编程 Agent 装上"工程化超能力"
前端·javascript·后端
踩着两条虫39 分钟前
AI驱动的Vue3应用开发平台 深入探究(十三):物料系统之区块与页面模板
前端·vue.js·人工智能·架构·系统架构
weixin199701080161 小时前
《得物商品详情页前端性能优化实战》
前端·性能优化
帮我吧智能服务平台1 小时前
装备制造企业售后服务数字化:从成本中心到利润中心背景
java·前端·制造
qq_368019661 小时前
用 react 的react-syntax-highlighter 实现语法高亮、行号与多行错误行高亮
前端·react.js·前端框架
lbh1 小时前
从LLM到Agent的核心概念
前端·openai·ai编程
-Da-1 小时前
【操作系统学习日记】并发编程中的竞态条件与同步机制:互斥锁与信号量
java·服务器·javascript·数据库·系统架构