vue3.3中ref和reactive原理源代码分析

源码是ts编写的,这里部分简化成js便于阅读

复制代码
function ref(value) {
  return createRef(value, false)
}

function createRef(rawValue, shallow) { //shallow是否是浅层定义数据,用于区别ref和shallowRef
  if (isRef(rawValue)) {//如果已经是ref直接返回源数据
    return rawValue
  }
  return new RefImpl(rawValue, shallow)
}

class RefImpl<T> {
  private _value: T
  private _rawValue: T

  public dep?: Dep = undefined
  public readonly __v_isRef = true

  constructor(
    value: T,//第一个参数value:传入的源数据
    public readonly __v_isShallow: boolean //第二个参数__v_isShallow:是否是浅层次响应的属性
  ) {
    this._rawValue = __v_isShallow ? value : toRaw(value)
    this._value = __v_isShallow ? value : toReactive(value)//初始化数据如果是已经包装过的__v_isShallow就是true,否则通过toReactive包装传入的参数
  }

  get value() {
    trackRefValue(this) //依赖收集
    return this._value
  }

  set value(newVal) {
    const useDirectValue = this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)//判断是否已经是vue包装过的对象
    newVal = useDirectValue ? newVal : toRaw(newVal)
    if (hasChanged(newVal, this._rawValue)) {
      this._rawValue = newVal
      this._value = useDirectValue ? newVal : toReactive(newVal)//如果已经包装过返回源数据,否则通过toReactive包装传入的参数
      triggerRefValue(this, newVal)//触发响应式更新
    }
  }
}

toReactive = (value) => isObject(value) ? reactive(value) : value //基本数据类型通过class类依赖收集触发更新,引用数据类型通过Proxy代理实现

isObject = (val) => val !== null && typeof val === 'object' //上面用到的函数:判断是否是一个对象

//reactive()函数调用createReactiveObject函数(内部通过new Proxy())创建响应式数据,如下:
function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<any>,
  proxyMap: WeakMap<Target, any>
) {
  if (!isObject(target)) { //如果不是对象直接返回源数据,所以必须传入对象才有效
    if (__DEV__) {
      console.warn(`value cannot be made reactive: ${String(target)}`)
    }
    return target
  }
  // target is already a Proxy, return it.
  // exception: calling readonly() on a reactive object
  if (
    target[ReactiveFlags.RAW] &&
    !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
  ) {
    return target
  }
  // target already has corresponding Proxy
  const existingProxy = proxyMap.get(target)
  if (existingProxy) {
    return existingProxy
  }
  // only specific value types can be observed.
  const targetType = getTargetType(target)
  if (targetType === TargetType.INVALID) {
    return target
  }
  const proxy = new Proxy(//创建Proxy代理
    target,
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
  )
  proxyMap.set(target, proxy)
  return proxy
}

总结:

ref() 函数通过调用new RefImpl(rawValue, shallow)这个class类来包装数据,内部有value属性(可读get通过trackRefValue收集依赖;可写set通过triggerRefValue更新依赖), 传入的值会调用toReactive函数进行封装.

toReactive = (value) => isObject(value) ? reactive(value) : value

isObject = (val) => val !== null && typeof val === 'object'

reactive()函数调用createReactiveObject函数(内部通过new Proxy())创建响应式数据

**ref:**定义基本数据类型通过class类中的value属性依赖收集触发更新;定义引用数据类型会调用reactive()实现数据代理

**reactive:**只用于定义引用数据类型,通过Proxy代理实现

源码地址https://github.com/vuejs/core/tree/v3.3.4/packages/reactivity/src

相关推荐
爱勇宝2 小时前
大多数人不是在使用 AI 赚钱,而是在帮 AI 公司赚钱
前端·后端·程序员
冬奇Lab3 小时前
每日一个开源项目(第143篇):page-agent - 纯 JS 的网页 GUI Agent,无需截图、无需插件、无需后端
前端·人工智能·agent
To_OC4 小时前
LC 994 腐烂的橘子:人人都说是 BFS 入门题,我却写了三遍才过
javascript·算法·leetcode
IT_陈寒7 小时前
React的这个渲染问题连官方文档都没说清楚
前端·人工智能·后端
追逐时光者9 小时前
别再满网找零散工具了,腾讯 QQ 浏览器这个“帮小忙”工具箱真能省时间
前端·后端
如果超人不会飞9 小时前
脉络清晰的业务演进:TinyVue Timeline 时间线组件全方位实战指南
vue.js
如果超人不会飞9 小时前
从扁平到立体:掌握 TinyVue Grid 树形表格的高级实战指南
vue.js
To_OC10 小时前
LC 200 岛屿数量:经典 DFS 入门题,我第一次写居然连方向都搞错了
javascript·算法·leetcode
Asmewill11 小时前
grep&curl命令学习笔记
前端
stringwu11 小时前
Flutter 开发必备:MVI 架构的高效实现指南
前端·flutter