ref的简易版 - 源码系列10

接着写 ref!

目标 ref

首先看下,目标 ref 的功能:

js 复制代码
import {
        reactive,
        effect,
        watch,
        watchEffect,
        computed,
        ref
      } from '../../../node_modules/@vue/runtime-dom/dist/runtime-dom.esm-browser.js';
      // import { reactive, effect,watch,watchEffect, computed } from './reactivity.js';
      
      const name = ref('颜酱');
      const obj = ref({name:'颜酱 对象'})
      effect(()=>{
        console.log('effect name.value',name.value)
      })
      effect(()=>{
        console.log('effect name',name)
      })
      effect(()=>{
        console.log('effect obj.value',obj.value)
      })
      effect(()=>{
        console.log('effect obj',obj)
      })
      
      obj.value.name = '颜酱3 对象'
      name.value = '颜酱 普通'

ref一般是监测普通类型的值。

分析 ref

  • ref 是一个函数,1 个参数,对象或者普通类型的值
  • 返回值是RefImpl实例
  • 获取值,通过.value,修改值,也是同理
  • 值变化的时候,触发相应的effect
  • 如果ref对象的话,值会变成proxy

写 ref

ref的逻辑和computed极其相似,取值改值都是通过value属性。

获取值的时候,收集effect 修改值的时候,触发effect

ts 复制代码
import { ReactiveEffect,trackEffects,triggerEffects } from './effect';

export function ref(param){
  return new RefImpl(param)
}

export class RefImpl{
  private _value
  public dep: Set<ReactiveEffect> = new Set()
  constructor(private rawValue){
    this._value = rawValue
  }
  get value(){
    trackEffects(this.dep);
    return this._value
  }
  set value(newVal){
    if(newVal===this._value){
      return
    }
    this._value = newVal;
    triggerEffects(this.dep);
  }
}

对象进行特殊处理

对象的时候,需要进行proxy

ts 复制代码
import {isObject,reactive} from './reactive'
function toReactive(param){
  return isObject(param)?reactive(param):param
}
export class RefImpl{
  constructor(private rawValue){
    this._value = toReactive(rawValue)
  }
  set value(newVal){
    // ...
    this._value = toReactive(newVal);
    // ...
  }
}

刷新下就可以了!

设置__v_isRef

这里有种情况,额外处理下,index.html加两句

js 复制代码
const obj2 = reactive({person:obj,age:18})
console.log('obj2 reactive ref',obj2.person)

如果按照现在的逻辑,应该是ref类型,但引用原vue发现结果是这样

也就是reactive对象里,如果key值是对象,且被ref过的话,将直接返回Proxy类型,并不会返回ref类型。

怎么处理呢?

  1. ref类用属性public __v_isRef = true,标识自己
  2. reactive里get取值时,如果发现__v_isRef = true,则返回xx.value就可以
ts 复制代码
// ref.ts
export class RefImpl{
  public __v_isRef = true
  // ...
}

// computed.ts 这边顺手加上
export class ComputedRefImpl {
  public __v_isRef = true
}

// reactive.ts
export function reactive(target) {
  // ...
  if (target[__v_isReactive]) {
    return target
  }
  // 如果是ref对象,直接返回value
  if (target.__v_isRef) {
    return target.value
  }
}

然后再换成自己的,就可以返回Proxy值了!

ref.ts

ts 复制代码
import { ReactiveEffect,trackEffects,triggerEffects } from './effect';

import {isObject,reactive} from './reactive'
export function ref(param){
  return new RefImpl(param)
}

function toReactive(param){
  return isObject(param)?reactive(param):param
}

export class RefImpl{
  public __v_isRef = true
  private _value
  public dep: Set<ReactiveEffect> = new Set()
  constructor(private rawValue){
    this._value = toReactive(rawValue)
  }
  get value(){
    trackEffects(this.dep);
    return this._value
  }
  set value(newVal){
    if(newVal===this._value){
      return
    }
    this._value = toReactive(newVal);
    triggerEffects(this.dep);
  }
}
相关推荐
Csvn37 分钟前
Worker 与 OffscreenCanvas
前端
Csvn38 分钟前
图片字体
前端
m0_7381207239 分钟前
JWT 安全从入门到实战:六种典型攻击场景解析
前端·网络·python·安全·网络安全
miofly39 分钟前
GitHub 今日推荐|floorplan:AI 读代码自动画交互式架构地图
javascript·开源·github
拖孩1 小时前
AI 帮我写的小程序上线 44 天:36 个人进了收银台,8 个人付钱
前端·后端·微信小程序
java1234_小锋1 小时前
【技术专题】CSS3 网页技术 - CSS3 视觉增强
前端·css·css3
Dovis(誓平步青云)1 小时前
拖动关键帧角色就抽动,动画编辑器怎样补齐中间状态
开发语言·前端·javascript·机器学习·编辑器
lpfasd1231 小时前
pywebview与Electron
前端·javascript·electron
行走的陀螺仪1 小时前
Claude Code 从入门到实战(一):安装、登录与第一个Vue项目
前端·javascript·vue.js
梦曦i1 小时前
0.8.2 新增 backFallback 配置,无 tabbar 页返回无忧
前端·uni-app x