vue3的ref原理

127 阅读1分钟

1、执行下面代码

packages\vue\examples\composition\test.html文件下

案例二:原始值类型 1

<script src="../../dist/vue.global.js"></script>
<div id="demo">
  <h1>{{count}}</h1>
</div>
<script>
  const { createApp, ref } = Vue
  var app = createApp({
    setup() {
      var count = ref(1)
      setTimeout(() => {
        count.value++
      }, 1000)
      return {
        count
      }
    }
  })
  app.mount('#demo')
</script>

案例二:为了检测ref只负责原始值类型值,至于对象则转接给reactive对象

<script src="../../dist/vue.global.js"></script>
<div id="demo">
  <h1>{{obj}}</h1>
</div>
<script>
  const { createApp, ref } = Vue
  var app = createApp({
    setup() {
      var obj = ref({ a: 1 })
      setInterval(() => {
        obj.value.a++
      }, 1000)
      return {
        obj
      }
    }
  })
  app.mount('#demo')
</script>

2、打断点

packages\reactivity\src\ref.ts文件下,搜索return createRef(value, false)

function ref(value) {
  return createRef(value, false)
}
function createRef(rawValue, shallow) {
  if (isRef(rawValue)) {
    return rawValue
  }
  return new RefImpl(rawValue, shallow)
}
function isRef(r){
  return r && r.__v_isRef === true
}
class RefImpl{
  public dep = undefined
  public readonly __v_isRef = true // new RefImpl() 会给实例对象加上属性 __v_isRef 为 true
  constructor(value, public readonly __v_isShallow) {
    this._rawValue = toRaw(value) // 
    this._value = toReactive(value) // 如果是对象,则使用Reactive做处理,这时走的是reactive(value)的逻辑了
  }
  get value() {
    trackRefValue(this)
    return this._value
  }
  set value(newVal) {
    newVal = toRaw(newVal)
    if (hasChanged(newVal, this._rawValue)) {
      this._rawValue = newVal
      this._value = toReactive(newVal)
      triggerRefValue(this, newVal)
    }
  }
}
const isObject = (val) =>  val !== null && typeof val === 'object'
const toReactive = ( T ) => isObject(value) ? reactive(value) : value
function toRaw(observed){
  const raw = observed && observed[ReactiveFlags.RAW]
  return raw ? toRaw(raw) : observed
}

3、调用栈分析

  • 在执行setup函数时候,会处理ref函数,将数据处理为类的实例,当访问到了实例的value属性,就会触发相应的函数trackRefValue和triggerRefValue
  • 其中trackRefValue是收集依赖,并不是所有的触发get函数都会真的去收集依赖,只有在render中读取的才会收集
  • 在setup函数内部访问到的就不会触发依赖
  • triggerRefValue是触发依赖,等到设置了新的值时,触发该函数,进一步触发副作用的schedule函数
  • 进而触发componentUpdateFn,在内部重新触发patch,就这样更新视图

代码流程图 image.png

image.png