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);
  }
}
相关推荐
腾讯云云开发5 分钟前
小程序数据库权限管理,一看就会!——CloudBase新手指南
前端·数据库·微信小程序
多则惑少则明42 分钟前
Vue开发系列——自定义组件开发
前端·javascript·vue.js
用户250694921611 小时前
next框架打包.next文件夹部署
前端
程序猿小蒜1 小时前
基于springboot的校园社团信息管理系统开发与设计
java·前端·spring boot·后端·spring
一叶难遮天1 小时前
开启RN之旅——前端基础
前端·javascript·promise·js基础·es6/ts·npm/nrm
申阳1 小时前
Day 4:02. 基于Nuxt开发博客项目-整合 Inspira UI
前端·后端·程序员
程序猿_极客1 小时前
【期末网页设计作业】HTML+CSS+JavaScript 猫咪主题网站开发(附源码与效果演示)
前端·css·html·课程设计·网页设计作业
IT古董1 小时前
【前端】从零开始搭建现代前端框架:React 19、Vite、Tailwind CSS、ShadCN UI 完整实战教程-第1章:项目概述与技术栈介绍
前端·react.js·前端框架
有点笨的蛋1 小时前
从零搭建小程序首页:新手也能看懂的结构解析与实战指南
前端·微信小程序
爱宇阳1 小时前
Vue3 前端项目 Docker 容器化部署教程
前端·docker·容器