老生常谈响应式之 reactive & ref(下)

2024-02-20  本文已影响0人  羊驼驼驼驼

🌬🌬🌬 前言:由于篇幅有点长,所以分成了两节来分别介绍,上节我们构建了简易版的 reactive,并通过分析实践了解到 reactive 只能用于复杂数据类型,下面我们来分析一下 ref,刚刚开工,各位打工人进入状态了吗🌝🌝🌝,废话不多说,开整🌪️🌪️🌪️

一、构建基础列表

大致目录结构
---| packages
---|---| reactivity // 响应性模块
---|---|---| src
---|---|---|---| index.ts 出口文件
---|---|---|---| ref.ts
---|---|---|---| reactive.ts
---|---|---|---| effect.ts
---|---|---|---| dep.ts
---|---|---|---| baseHandlers.ts
---|---| shared // 公共方法模块
---|---|---| src
---|---|---|---| index.ts 出口文件
---|---|---|---| shapeFlags.ts
---|---| vue // 打包、测试实例、项目整体入口模块
---|---|---| dist
---|---|---| examples
---|---|---| src
---|---|---|---| index.ts 出口文件

二、开整

ref 目标:构建 ref 函数,分析为什么用 .value 去访问数据,ref 是如何分别处理复杂数据类型和简单数据类型的?
1. 创建 packages/reactivity/src/ref.ts 模块
import { createDep, Dep } from './dep'
import { activeEffect, trackEffects, triggerEffects } from './effect'
import { toReactive } from './reactive'

export interface Ref<T = any> { value: T }

/**
* ref 函数
* @param value unknown
*/
export function ref(value?: unknown) {
  return createRef(value, false)
}

/**
* 创建 RefImpl 实例
* @param rawValue 原始数据
* @param shallow boolean 形数据,表示《浅层的响应性(即:只有 .value 是响应性的)》
*/
function createRef(rawValue: unknown, shallow: boolean) {
  if(isRef(rawValue)) {
    return rawValue
  }
  return new RefImpl(rawValue, shallow)
}

class RefImpl<T> {
  // 私有属性
  private _value: T
  private _rawValue: T
  
  // 共有属性
  public dep?: Dep = undefined
  
  // 标记是否为 ref 类型
  public readonly __v_isRef = true
  
  constructor(value: T, public readonly __v_isShallow: boolean) {
    // https://cn.vuejs.org/api/reactivity-advanced.html#shallowref
    // 如果:__v_isShallow:true,则:value 不会被转化为 reactive 数据。
    // 如果:当前的 value 是复杂数据类型的话,则:会失去响应性
    this._value = __v_isShallow ? true : toReactive(value)
    
    // 原始数据
    this._rawValue = value
  }
  
  /**
  * get:将对象属性绑定到查询该属性时调用的函数
  * 🌰:xxx.value 触发该函数
  */
  get value() {
    trackRefValue(this)
    return this._value
  }
  /**
  * set:更新属性值
  * newValue:新数据 
  * this._rawValue: 原始数据(老数据)
  * hasChanged:对比数据是否发生了变化
  */
  set value(newValue) {
    // 更新数据
    this._rawValue = newValue
    
    // 更新 .value 的值
    this._value = toReactive(newValue)
    
    // 触发依赖
    triggerRefValue(this)
  }
}

/**
* 为 ref 的 value 进行依赖收集工作
*/
export function trackRefValue(ref) {
  if(activeEffect) {
   // 收集所有依赖
   trackEffects(ref.dep || (ref.dep = createDep()))
  }
}

/**
* 为 ref 的 value 进行依赖触发工作
*/
export function triggerRefValue(ref) {
  if(ref.dep) {
    triggerEffects(ref.dep)
  }
}

/**
* 指定数据是否为 RefImpl 类型
*/
export function isRef(r: any) r is Ref {
  return !!(r && r.__v_isRef === true)
}
packages/reactivity/src/reactive.ts
/**
* 将指定数据变为 reactive 类型
*/
export const toReactive = < T extends unknown>(value: T): T => 
isObject(value) ? reactive(value as object) : value
packages/shared/src/index.ts
/**
* 判断是否为一个对象
*/
export const isObject = (val: unknown) => val !== null && typeof val === 'object'


/**
* 对比两个数据是否发生了改变
*/
export const hasChanged = (value: any, oldValue: any): boolean => !Object.is(value, oldValue)
2. packages/vue/examples/reactivity/ref.html 小试牛刀
<body>
  <div id="app"></div>
  <script>
    const { ref, effect } = Vue
    
    const obj = ref('张三')
    
    effect(() => {
      document.querySelector('#app').innerText = obj.value
    })
    
    setTimeout(() => {
      obj.value = '李四'
    }, 2000)
  </script>
</body>

✨✨✨ 至此,我们的ref函数构建完成,本质上是生成了一个 RefImpl 类型的实例对象,通过 getset 标记处理了 value 函数,并且 ref 是通过 toReactive 对简单数据类型和复杂数据类型做了区分处理。

都给你讲啦.png
上一篇 下一篇

猜你喜欢

热点阅读