vue3源码 - toRef和toRefs篇

943 阅读2分钟

前言

toRef有什么作用呢?官方文档toRef的解释: 可以用来为源响应式对象上的某个 property 新创建一个 ref。然后,ref 可以被传递,它会保持对其源 property 的响应式连接。

toRef有什么用呢?

和之前一样, 通过vue3源码测试一下

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="app"></div>
  <script src="../vue/dist/vue.global.js"></script>
  <script>
    let { ref, effect, toRef, reactive }  = Vue
    let state = reactive({
      name: 'vvv'
    })
    let name = toRef(state, 'name')
    console.log(name);
    effect(() => {
      app.innerHTML =  name.value
    })
    setTimeout(() => {
      name.value = 'vvv2'
    }, 2000)
  </script>
</body>
</html>

打开浏览器, 看看测试的结果

toRef.gif 可以看到通过toRef后返回的是一个ObjectRefImpl实例, 可以知道toRef的作用如名字一样,将值转化为ref

toRef的实现

toRef的定义

export const toRef = (object, key, defaultValue?) => {
  // 将一个对象的值转化为ref形式
  const val = object[key]
  return isRef(val) ? val : new ObjectRefImpl(object, key, defaultValue)
}

isRef在上一篇vue3源码 - ref篇有解释, 其作用是判断是不是已经ref过了

核心ObjectRefImpl类

// toRef核心
class ObjectRefImpl {
  public readonly __v_isRef = true
  constructor(private readonly _object, private readonly _key, private readonly _defaultValue?) {}
  get value() {
    const val = this._object[this._key] // 取值
    return val === undefined ? this._defaultValue : val // 是否去默认值
  }
  set value(newVal) {
    this._object[this._key] = newVal // 设置值
  }
}

或许有人会问,这里为什么会不用track(收集依赖) 和trigger(触发依赖)呢?
这是因为toRef主要针对对象是已经响应式过的, 具体可以看官方文档toRef的解释

测试toRef

新建一个toRef.html文件

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="app"></div>
  <script src="../node_modules/@vue/reactivity/dist/reactivity.global.js"></script>
  <script>
    let { ref, effect, toRef, reactive }  = VueReactivity
    let state = reactive({
      name: 'vvv'
    })
    let name = toRef(state, 'name')
    console.log(name);
    effect(() => {
      app.innerHTML =  name.value
    })
    setTimeout(() => {
      name.value = 'vvv2'
    }, 2000)
  </script>
</body>
</html>

打开浏览器, 看看我们实现的怎么样

toRef2.gif 为了具有辨识度, 我在ObjectRefImpl添加了前缀My

toRefs的实现

相信大家在项目中用的最多的是toRefs, 因为toRef只能转化一层,所以用的会相对比较少 那么下面就让我们一起来实现toRefs

export const toRefs = (object) => {
  // 循环遍历逐渐toRef
  let ret = isArray(object) ? new Array(Object.length) : {}
  for (let key in object) {
    ret[key] = toRef(object, key)
  }
  return ret
}

toRefs的实现还是相对来说比较简单的, 循环遍历逐渐toRef即可

测试toRefs

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="app"></div>
  <script src="../node_modules/@vue/reactivity/dist/reactivity.global.js"></script>
  <script>
    let { ref, effect, toRef, reactive, toRefs }  = VueReactivity
    let state = reactive({
      name: 'vvv',
      age: 18
    })
    let my = toRefs(state)
    console.log(my.name);
    console.log(my.age);

    effect(() => {
      app.innerHTML =  my.name.value + '=====' + my.age.value
    })
    setTimeout(() => {
      my.name.value = 'vvv2'
      my.age.value = '19'
    }, 2000)
  </script>
</body>
</html>

toRef3.gif 现在可能toRefs用的也相对比较少了, 毕竟现在出了setup语法糖, 不用在return出去了, 不过我相信, 学到总比没学到好, 相信自己会用到的

toReftoRefs已经实现了, vue3源码其他相关的实现静待下一篇哟

最后

如果觉得本文对你有帮助,记得点赞👍🏻 、 收藏⭐️ 加关注➕哟

vue3源码实现系列

vue3源码 - 响应式数据reactive篇

vue3源码 - effect依赖收集触发更新篇

vue3源码 - ref篇

vue3源码 - toRef和toRefs篇