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的依赖与收集实现可以参考本人之前的文章,也欢迎有问题的博友在评论区一起讨论!

相关推荐
2301_32241428042 小时前
活力孕康复APP 47737- 原创(免费领源码+部署教程+开发环境)
java·vue.js·spring boot·mysql·微信小程序·idea·微信开发者工具
专业程序开发源4 小时前
springbootLivehouse票务系统-计算机课程设计、毕业设计
vue.js·spring boot·后端·python·django·课程设计·pygame
leoZ2314 小时前
2026-09-10-静态扫描连错三次-用运行环境当裁判
前端·javascript·vue.js·人工智能·opencv·机器学习·数据挖掘
卓怡学长4 小时前
w233基于vue和springboot的宠物管理系统的设计与实现
java·vue.js·spring boot·spring·intellij-idea
梨涡泥窝6 小时前
JavaWeb——基于 Spring Boot + Vue 的图书管理系统的设计与实现
vue.js·spring boot·后端
huali8 小时前
vue-split-screen:让 Vue Router 的导航轨迹变成两个页面
前端·javascript·vue.js
雪芽蓝域zzs10 小时前
第三十六节:keep-alive 页面缓存(Vue3 + 后台管理系统)
前端·vue.js·缓存
边境悍匪10 小时前
蜗牛学苑 Java 智能体学习 Day41|ElementPlus 布局、Vue‑Router 登录、Axios 思维导图复盘
java·vue.js·学习
志尊宝10 小时前
Vue3 零基础每日笔记(017):事件处理进阶——$event、多事件与事件修饰符全家桶
javascript·vue.js·笔记
Bs_MoneyMagnet21 小时前
基于springboot+vue的医疗健康便民服务平台的设计与实现 源码+文档
java·vue.js·spring boot·后端·spring