Vue3响应式原理之ref篇

Vue 3 的响应式系统中,ref 和 reactive 是用于定义响应式变量最核心的两个API。今天本文将带大家剖析 ref 的内部机制,以及与 reactive 的对比和应用场景。

一、什么是 ref

ref 是 Vue 3 提供的一个函数,用于创建一个可响应式的引用对象,它的值通过 .value 属性访问和修改:

javascript 复制代码
import { ref } from 'vue'

const count = ref(0)
console.log(count.value) // 0

count.value++
console.log(count.value) // 1

内部实现简述

ref 返回的是一个包含 value getter/setter 的对象(如 RefImpl类实例)。当读取 .value 时,会触发getter下的track() 收集依赖;当赋值时,会触发 setter中的trigger() 触发更新。

javascript 复制代码
/*
* - RefImpl类实现get和set
* @params {any} rawValue - ref传入的值
* @params {boolean|readonly} _isShallow - 判断是否浅层代理决定是否需要reactive
* @returns - 无
*/

class RefImpl {
  constructor(rawValue, _isShallow) {
      this._value = _isShallow ? _rawValue: convert(_rawValue)
      this._shallow = _isShallow
  }
  get value() {
    track(this, 'value') // 收集依赖
    return this._value
  }
  set value(newValue) {
    if (hasChanged(newValue, this._value)) {//旧值与新值比对
       this._value = this._shallow ? newValue : convert(newValue);
      trigger(this, 'value') // 触发更新
    }
  }
}
function convert(value) {
    return (Object.prototype.toString.call(value) == '[object Object]') ? reactive(value) : value
}

说明:ref 的响应式依赖对 .value 的访问

二、ref vs reactive 核心区别

适用类型:
ref: 基本类型(number, string, boolean)或对象
reactive: 仅对象/数组(不能用于基本类型)

访问方式:
ref: 必须通过 .value
reactive: 直接属性访问(如 obj.count)
解包行为
ref: 在模板中自动 .value 解包
reactive: 无解包,直接使用
嵌套响应式
ref: 对象会自动转为 reactive(除非 shallow)
reactive: 所有嵌套对象进行响应式转换

javascript 复制代码
示例对比

// 使用 ref(推荐用于基本类型)
const count = ref(0)
const user = ref({ name: 'Alice' })

// 使用 reactive
const state = reactive({
  count: 0,
  user: { name: 'Alice' }
})
//在模板中
<template>
 {{ count }}        
 {{ user.name }}    
 {{ state.count }}
 {{ state.user.name }}
</template>

说明:在 JavaScript 中,ref 必须手动 .value,而 reactive 不需要。

三、何时使用 ref,何时使用 reactive?

推荐使用 ref 的场景:

如果是处理基本类型数据,则使用ref,这也是 reactive 无法做到的, reactive处理基本类型数据无响应式更新,例如:

javascript 复制代码
let val = reactive(1)
// 以下更改都会失去响应式
 setTimeout(() => {
  val = 2
  val = reactive(2)
 }, 1000)

推荐使用 reactive 的场景:

javascript 复制代码
// 管理复杂对象或表单状态
  const form = reactive({
     email: '',
     password: '',
     errors: []
   })
   
//避免大量 .value 写法
// 比 ref({ ... }) 更简洁
 	const state = reactive({ a: 1, b: 2, c: 3 })
   state.a++ // 直接操作
   
//性能略优于 ref 的嵌套对象
//reactive 只创建一层 Proxy,而 ref 包裹对象时内部仍会调用reactive

四、常见误区

1:在 effect 或 watchEffect 中创建 ref

javascript 复制代码
// 错误,每次 effect 执行都会新建 ref,失去响应式
effect(() => {
  const count = ref(0) 
  console.log(count.value)
})
// 正确做法:在 effect 外定义 ref
const count = ref(0)
effect(() => {
  console.log(count.value) // 追踪稳定的 ref
})

2:混淆 ref 和普通变量

javascript 复制代码
let count = ref(0)
count = 1 // 赋值给变量本身,丢失响应式!

//正确:始终操作 .value
count.value = 1 

五、总结

对于掌握 ref 与 reactive 的差异,可以在适当场景下写出高效、可维护 Vue 3 应用的关键。本文主要基于ref的核心源码实现,其他功能如reactive和effect的依赖与收集实现可以参考本人之前的文章,也欢迎有问题的博友在评论区一起讨论!

相关推荐
小疙瘩2 小时前
element-ui 中 el-upload 多文件一次性上传的实现
javascript·vue.js·ui
独泪了无痕2 小时前
useStorage:本地数据持久化利器
前端·vue.js
呆子小木心5 小时前
Vue2或Vue3项目引用百度地图
javascript·vue.js·typescript·前端框架·html5
UI设计兰亭妙微7 小时前
资金运营管理监控系统交互设计、UI设计及VUE开发
vue.js·ui·交互
王子肥波7 小时前
🚀 TabTab Admin:一款现代化的 Vue 3 后台管理系统模板
vue.js
白开水丶8 小时前
vue3源码学习(五)ref 、toRef、toRefs、proxyRefs 源码学习
前端·vue.js·学习
不想秃头的程序员9 小时前
Vue3 子传父全解析:从基础用法到实战避坑
前端·vue.js·面试
简单Janeee9 小时前
[Vue 3 从零到上线]-第三篇:网页的指挥官——指令系统 (v-if, v-for, v-bind, v-on)
前端·javascript·vue.js
前端程序猿i10 小时前
第 7 篇:性能优化 —— 大量消息下的流畅体验
前端·vue.js·性能优化