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

相关推荐
ayqy贾杰14 小时前
Agent First Engineering
前端·vue.js·面试
徐小夕16 小时前
JitWord 2.3: 墨定,行远
前端·vue.js·github
踩着两条虫17 小时前
从设计稿到代码:VTJ.PRO 的 AI 集成系统架构解析
前端·vue.js·人工智能
codingWhat19 小时前
如何实现一个「万能」的通用打印组件?
前端·javascript·vue.js
徐小夕21 小时前
pxcharts Ultra V2.3更新:多维表一键导出 PDF,渲染兼容性拉满!
vue.js·算法·github
前端Hardy1 天前
别再无脑用 `JSON.parse()` 了!这个安全漏洞你可能每天都在触发
前端·javascript·vue.js
前端Hardy1 天前
别再让 `console.log` 上线了!它正在悄悄拖垮你的生产系统
前端·javascript·vue.js
青青家的小灰灰1 天前
从入门到精通:Vue3 ref vs reactive 最佳实践与底层原理
前端·vue.js·面试
OpenTiny社区1 天前
我的新同事是个AI:支持skill后,它用TinyVue搭项目还挺溜!
前端·vue.js·ai编程
李剑一1 天前
大屏天气展示太普通?视觉升级!用 Canvas 做动态天气遮罩,雷阵雨效果直接封神
前端·vue.js·canvas