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);
  }
}
相关推荐
Ulyanov2 分钟前
三维战场可视化核心原理(一):从坐标系到运动控制的全景指南
开发语言·前端·python·pyvista·gui开发
天若有情6739 分钟前
从语法拆分到用户感知:我的前端认知重构之路
前端·javascript
_OP_CHEN12 分钟前
【前端开发之CSS】(五)CSS 盒模型深度解析:从基础到实战,掌控页面布局核心
前端·css·html·盒模型·页面开发·页面布局·页面美化
摘星编程16 分钟前
用React Native开发OpenHarmony应用:DrawerNavigation侧滑关闭
javascript·react native·react.js
阿蒙Amon17 分钟前
TypeScript学习-第2章:基础类型
javascript·学习·typescript
轩情吖17 分钟前
Qt多元素控件之QListWidget
开发语言·前端·c++·qt·控件·qlistwidget·桌面级
Yaru1119 分钟前
伪3D地图和3D饼图实现
前端·3d·echarts
测试_AI_一辰21 分钟前
Agent & RAG 测试工程 02:RAG 从最小闭环到可信
开发语言·前端·人工智能·github·ai编程
DreamOneDay26 分钟前
MapLibre GL JS加载ArcGis Terrain3D地形
javascript·3d·arcgis·maplibre·terrain3d
_xaboy31 分钟前
开源Vue组件-动态表单组件设计,告别重复CRUD,JSON一键生成表单
前端·vue.js·低代码·开源·json