浅谈vue3中ref的执行过程

文章详细解析了Vue.js中ref和reactive实现响应式的核心逻辑,特别是ref函数如何处理简单数据类型,以及在创建ref实例时如何判断是否为浅层引用(shallowRef)。通过源码分析,揭示了在读取和设置值时如何跟踪变化,以及如何利用toRaw、reactive等函数处理原始值和响应式对象。
摘要由CSDN通过智能技术生成

在使用ref和reactive的时候我产生了一个疑问,既然reactive的核心是proxy代理,那么ref对于简单数据类型的响应式的实现运用了什么样的逻辑,通过翻阅源码,找到了以下代码执行顺序----

首先ref作为函数入口,调用后经过createRef中的一系列处理最终返回给函数本身,这里第二个参数是添加一个是否为shallowRef的标记符,因为这两个API同样需要调用createRef

function ref (value) {
  return createRef(value, false)
}

function shallowRef (value) {
  return createRef(value, ture)
}

来到内部之后会先进行一个判断,判断传入的值是否为另一个ref对象,如果不是则没有__v_isRef,isRef返回值一定是false。这里也存在了一个强制类型转换的作用,因为当r的值也为false时,第9行这条表达式会成立,也就是当r为undefined、null、' ' 、0 、NaN时的返回值不为false而是它本身,所以强制转换之后,返回值一定是false。我想这么做的目的是为了避免第2行在进行判断时需要对条件进行隐式类型转换,也许先储存再读取的行为在这里会更好。

function createRef (rawValue, shallow) {
          if (isRef(rawValue)) {
            return rawValue
          }
      return new RefImpl(rawValue, shallow)
    }

function isRef (r) {
        return !!(r && r.__v_isRef === true)
    }

如果上述判断结果不出意外的为false,就会执行第5的代码走下面的RefImp类形成一个新实例化对象最终返回给最先调用的ref。

下面代码其中7行代码判断不是shallowRef之后会调用toRaw拷贝一个value的原始值保存在实例身上,同时也保证了value是一个reactive对象时会进行还原。10行的_value是通过第9行中这个函数来判断是否为作为一个对象并需要通过reactive函数进行proxy代理,如果不是直接返回。下面函数value接收的参数是最开始使用ref函数处理响应式时传入的数据

class RefImpl {
          constructor (value, __v_isShallow) {
        this.__v_isShallow = __v_isShallow
        this.dep = undefined
        this.__v_isRef = true
        //toRaw会根据判断进行递归,最后把对象原始值放在这里
        this._rawValue = __v_isShallow ? value : toRaw(value)
        //toReactive如果value是一个对象,修改_value会引起rawValue的改变
        //const toReactive = value => (isObject(value) ? reactive(value) : value)
        this._value = __v_isShallow ? value : toReactive(value)
          }
          get value () {
            //追踪Ref的值
            trackRefValue(this)
            return this._value
          }
          set value (newVal) {
            const useDirectValue = 
                this.__v_isShallow || isShallow(newVal) || isReadonly(newVal)
            newVal = useDirectValue ? newVal : toRaw(newVal)
            if (hasChanged(newVal, this._rawValue)) {
                  this._rawValue = newVal
                  this._value = useDirectValue ? newVal : toReactive(newVal)
                  triggerRefValue(this, newVal)
                }
              }
        }

function toRaw (observed) {
  const raw = observed && observed['__v_raw' /* ReactiveFlags.RAW */]
  return raw ? toRaw(raw) : observed
}

代码初始化时和每当触发getter读取value发现已被修改时,14行的trackRefValue都会被调用(展示在下方),然后执行toRaw函数递归重新获取reactive对象的原始值,并打包成配置对象传给trackEffects重新进行Dep的初始化。

function trackRefValue (ref) {
  if (shouldTrack && activeEffect) {
    ref = toRaw(ref)
    {
      trackEffects(ref.dep || (ref.dep = createDep()), {
        target: ref,
        type: 'get' /* TrackOpTypes.GET */,
        key: 'value'
      })
    }
  }
}

触发setter时会判断ref的使用模式,先判断是否调用的为shallowRef,如果是则顾名思义的只对浅层数据进行修改,比如newVal为一个对象则会浅拷贝把内存地址直接赋给_rawValue,而不是通过toRaw递归返回一个深拷贝的对象。然后isReadOnly判断是否只读,而这个属性值会在写computed计算属性时默认加上,也可以人为调用readonly这个API标记只读不可修改。然后通过调用hasChanged使用Object.is方法判断新值是否跟_rawValue的原始值相等,如果两个值不相等,false取反后为true,就给_rawValue和_value重新赋值,且_value会多一层判断,看新值是不是对象需要通过reactive来处理

function isReadonly (value) {
  return !!(value && value['__v_isReadonly' /* ReactiveFlags.IS_READONLY */])
}
const hasChanged = (value, oldValue) => !Object.is(value, oldValue)
const toReactive = value => (isObject(value) ? reactive(value) : value)

最后,setter在赋值完成之后会调用一个与getter中trackRefValue相似的函数并调用对应的Effects函数传递一个对象进行Dep修改相关的操作

//getter
function trackRefValue (ref) {
  if (shouldTrack && activeEffect) {
    ref = toRaw(ref)
    {
      trackEffects(ref.dep || (ref.dep = createDep()), {
        target: ref,
        type: 'get' /* TrackOpTypes.GET */,
        key: 'value'
      })
    }
  }
}
//setter
function triggerRefValue (ref, newVal) {
  ref = toRaw(ref)
  console.log('hh')
  if (ref.dep) {
    {
      triggerEffects(ref.dep, {
        target: ref,
        type: 'set' /* TriggerOpTypes.SET */,
        key: 'value',
        newValue: newVal
      })
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值