首页 > 其他分享 >尤雨溪说:为什么Vue3 中应该使用 Ref 而不是 Reactive?

尤雨溪说:为什么Vue3 中应该使用 Ref 而不是 Reactive?

时间:2024-02-03 12:00:29浏览次数:41  
标签:count 尤雨溪 reactive 对象 Reactive state Vue3 ref 响应

每次有同学学习到 vue3 的时候,总会问:“ref 和 reactive 我们应该用哪个呢?” 我告诉他:“我们应该使用 ref,而不是 reactive”。那么此时同学就会有疑惑:“为什么呢?ref 还需要 .value 处理,reactive 看起来会更加简单呢?”

嗯....每当这个时候,我都需要进行一次长篇大论来解释这个问题。不过以后应该不需要了,因为这篇文章将会把这个事情解释的非常清楚......

为什么推荐使用ref而不是reactive

reactive在使用过程中存在一些局限性,如果不额外注意这些问题,可能会给开发带来一些不便。与此不同,ref更像是Vue2时代的option API中的data的替代品,可以存放任何数据类型,而reactive声明的数据类型则仅限于对象。

总体来说,非必要的情况下最好避免使用reactive。官方文档也强烈推荐使用ref()作为声明响应式状态的主要API。以下是详细原因:

  1. 局限性问题: reactive本身存在一些局限性,可能会在开发过程中引发一些问题。这需要额外的注意力和处理,否则可能对开发造成麻烦。
  2. 数据类型限制: reactive声明的数据类型仅限于对象,而ref则更加灵活,可以容纳任何数据类型。这使得ref更适合一般的响应式状态的声明。
  3. 官方推荐: 官方文档强烈建议使用ref()作为声明响应式状态的首选。这是因为ref更简单、更直观,同时避免了reactive可能引发的一些问题。

总的来说:除非有特定的需求需要使用reactive,否则在大多数情况下更推荐使用ref()

最懂Vue的人都这么说了:推荐ref!!!!!!

图片

reactive和 ref 对比

reactiveref
❌ 只支持对象和数组(引用数据类型) ✅ 支持基本数据类型 + 引用数据类型
✅ 在 <script> 和 <template> 中无差别使用 ❌ 在 <script> 和 <template> 使用方式不同(在 <script> 中要使用 .value
❌ 重新分配一个新对象会丢失响应性 ✅ 重新分配一个新对象不会失去响应
能直接访问属性 需要使用 .value 访问属性
❌ 将对象传入函数时,失去响应 ✅ 传入函数时,不会失去响应
❌ 解构时会丢失响应性,需使用 toRefs ❌ 解构对象时会丢失响应性,需使用 toRefs

即:

  • ref 用于将基本类型的数据和引用数据类型(对象)转换为响应式数据,通过 .value 访问和修改。
  • reactive 用于将对象转换为响应式数据,可以直接访问和修改属性,适用于复杂的嵌套对象和数组。

01: reactive 有限的值类型

reactive 只能声明引用数据类型(对象)

let obj = reactive({
  name: '小明',
  age: 18
})

ref 既能声明基本数据类型,也能声明对象和数组

Vue 提供了 ref() 方法,允许我们创建可以使用任何值类型的响应式 ref

// 对象
const state = ref({})
// 数组
const state2 = ref([])

使用 ref,你可以灵活地声明基本数据类型、对象或数组,而不受像 reactive 那样只能处理引用数据类型的限制。这为开发提供了更大的灵活性,尤其是在处理不同类型的数据时。

02: reactive 使用不当会失去响应

使用 reactive 时,如果不当使用,可能导致响应性失效,带来一些困扰。这可能让开发者在愉快编码的同时,突然发现某些操作失去了响应性,不明所以。因此,建议在不了解 reactive 失去响应的情况下慎用,而更推荐使用 ref

1. 赋值给 reactive 一个整个对象或 reactive 对象

赋值一个普通对象

let state = reactive({ count: 0 })
// 这个赋值将导致 state 失去响应
state = { count: 1 }

赋值一个 reactive 对象

<template>
  {{ state }}
</template>    

<script setup>
const state = reactive({ count: 0 })
// 在 nextTick 异步方法中修改 state 的值
nextTick(() => {
  // 并不会触发修改 DOM ,说明失去响应了
  state = reactive({ count: 11 });
});
</script>

在 nextTick 中给 state 赋值一个 reactive 的响应式对象,但是 DOM 并没有更新。

解决方法:

  1. 不要直接整个对象替换,一个个属性赋值
let state = reactive({ count: 0 })
// state = { count: 1 }
state.count = 1
  1. 使用 Object.assign
let state = reactive({ count: 0 })
// state = { count: 1 },state 不会失去响应
state = Object.assign(state, { count: 1 })
  1. 使用 ref 定义对象
let state = ref({ count: 0 })
state.value = { count: 1 }

2. 将 reactive 对象的属性赋值给变量(断开连接/深拷贝)

这种操作类似于深拷贝,不再共享同一内存地址,而是只是字面量的赋值,对该变量的赋值不会影响原来对象的属性值。

let state = reactive({ count: 0 })
// 赋值给 n,n 和 state.count 不再共享响应性连接
let n = state.count
// 不影响原始的 state
n++
console.log(state.count) // 0

解决方案:

  • 避免将 reactive 对象的属性赋值给变量。

3. 直接 reactive 对象解构时

直接解构会失去响应。

let state = reactive({ count: 0 })
// 普通解构,count 和 state.count 失去了响应性连接
let { count } = state
count++ // state.count 值依旧是 0

解决方案:

使用 toRefs 解构,解构后的属性是 ref 的响应式变量。

const state = reactive({ count: 0 })
// 使用 toRefs 解构,后的属性为 ref 的响应式变量
let { count } = toRefs(state)
count.value++ // state.count 值改变为 1

建议:ref 一把梭

推荐使用 ref,总结原因如下:

  1. reactive 有限的值类型:只能声明引用数据类型(对象/数组)。

  2. reactive 在一些情况下会失去响应,这可能导致数据回显失去响应(数据改了,DOM 没更新)。

   <template>
     {{ state.a }}
     {{ state.b }}
     {{ state.c }}
   </template>
   
   <script>
   let state = reactive({ a: 1, b: 2, c: 3 })
   onMounted(() => {
     // 通过 AJAX 请求获取的数据,回显到 reactive,如果处理不好将导致变量失去响应
     // 回显失败,给响应式数据赋值一个普通对象
     state = { a: 11, b: 22, c: 333 }
     // 回显成功,一个个属性赋值
     state.a = 11
     state.b = 22
     state.c = 33
   })
   </script>

上面这个例子如果是使用 ref 进行声明,直接赋值即可,不需要将属性拆分一个个赋值。

使用 ref 替代 reactive

   <template>
     {{ state.a }}
     {{ state.b }}
     {{ state.c }}
   </template>
   
   <script>
   let state = ref({ a: 1, b: 2, c: 3 })
   onMounted(() => {
     // 回显成功
     state.value = { a: 11, b: 22, c: 333 }
   })
   </script>
    • 给响应式对象的字面量赋一整个普通对象或 reactive 对象将导致 reactive 声明的响应式数据失去响应。
  1. ref 适用范围更广,可声明基本数据类型和引用数据类型。

虽然使用 ref 声明的变量在读取和修改时都需要加 .value 小尾巴,但正因为有这个小尾巴,我们在 review 代码的时候就很清楚知道这是一个 ref 声明的响应式数据。

ref 的 .value 好麻烦!

ref 声明的响应式变量携带迷人的 .value 小尾巴,让我们一眼就能确定它是一个响应式变量。虽然使用 ref 声明的变量在读取和修改时都需要加 .value 小尾巴,但是正因为有这个小尾巴,我们在 review 代码的时候就很清楚知道这是一个 ref 声明的响应式数据。

可能有些人不喜欢这个迷人小尾巴,如果我能自动补全,阁下又如何应对?

Volar 插件能自动补全 .value(强烈推荐!!!!!)

推荐 ref 一把梭,但是 ref 又得到处 .value,那就交给插件来完成吧!

  • Volar 自动补全 .value(不是默认开启,需要手动开启)

reactive 重新赋值丢失响应是因为引用地址变了,被 proxy 代理的对象已经不是原来的那个,所以丢失响应了。其实 ref 也是一样的,当把 .value 那一层替换成另外一个有着 .value 的对象也会丢失响应。ref 定义的属性等价于 reactive({ value: xxx })

另外,说使用 Object.assign 为什么可以更新模板:

Object.assign 解释是这样的:如果目标对象与源对象具有相同的键(属性名),则目标对象中的属性将被源对象中的属性覆盖,后面的源对象的属性将类似地覆盖前面的源对象的同名属性。

那个解决方法里不用重新赋值,直接 Object.assign(state, { count: 1 }) 即可,所以只要 proxy 代理的引用地址没变,就会一直存在响应性

标签:count,尤雨溪,reactive,对象,Reactive,state,Vue3,ref,响应
From: https://www.cnblogs.com/hellofangfang/p/18004507

相关文章

  • vue3 修改浏览器小图标
    vue3框架搭建后,默认显示vue自己的icon public/favicon.ico替换成自己想要的icon public/index.html修改:<linkrel="icon"href="<%=BASE_URL%>favicon.ico"/><linkrel="shortcuticon"type="image/x-icon"href="&l......
  • uni-app+vue3会遇到哪些问题
    已经用uni-app+vue3+ts开发了一段时间,记录一下日常遇见的问题和解决办法uni-app中的单端代码uni-app是支持多端,如果你想让你的代码,只在部分平台使用,那么就需要用的它的单端处理语法//#ifdef和//#ifndef等。1.//#ifdefxxx只在xxx平台生效//#ifdefMP-WEIXINmenuB......
  • 从零搭建Vue3 + Typescript + Pinia + Vite + Tailwind CSS + Element Plus开发脚手架
    项目代码以上传至码云,项目地址:https://gitee.com/breezefaith/vue-ts-scaffold目录前言脚手架技术栈简介vue3TypeScriptPiniaTailwindCSSElementPlusvite详细步骤Node.js安装创建以typescript开发的vue3工程集成Pinia安装pinia修改main.ts创建一个store在组件中使用store集......
  • vue3,封装检测元素大小变化的自定义指令
    1//resizeObserver.ts2//监听元素大小变化的指令3constmap=newWeakMap()4constob=newResizeObserver((entries)=>{5for(constentryofentries){6//获取dom元素的回调7consthandler=map.get(entry.target)8//存在回调函......
  • vue3+elementplus+vuedraggable插件,实现左右拖拽移入,和上下拖拽排序
    先看目标效果(gif由迅捷gif工具制作,使用vscode可以打开gif,进行预览)效果分析1.左右区域,支持拖拽。左侧选项,拖入右边。可以新建大模块,也可以给模块新增一项。2.模块内部,支持拖拽排序,并按照排序,生成一个简单的层级。3.模块名字支持编辑。同时增加表单校验,名字不存在,则无法保存。......
  • 3秒钟教你如何配置vscode中的vue3代码快速生成模版
    1.首先点击你的vscode左下角的齿轮设置按钮,然后点击配置用户代码片段2.输入vue搜索vue.json这个文件,然后点击这个文件3.接下来只需在原有的注释之下输入粘贴如下代码即可4.代码如下"vue3":{"prefix":"vue3","body":["<template>",......
  • vue3-setup中如何通过ref调用子组件的函数
    vue3-setup中如何通过ref调用子组件的函数子组件通过defineExpose向外导出需要调用的函数在父子间中定义ref引用来调用子组件关键代码:<scriptsetup>import{ref,reactive,defineExpose}from'vue'constshow=ref(false);consttitle=ref('添加收款方式');con......
  • vue3使用v-viewer图片预览工具
    vue3使用v-viewer图片预览v-viewer中文文档安装全局注册main.js文件中使用 v-viewer中文文档安装pnpminstallv-viewerviewerjs 全局注册main.js文件中importViewerfrom'v-viewer'import'viewerjs/dist/viewer.css'constapp=createApp(App)//......
  • Vue3.0学习(一)
    1.Vue脚手架创建通过npm命令可以创建Vue3的脚手架工程:##创建工程npminitvite-app<project-name>##进入工程目录cd<project-name>##安装依赖npminstall##运行npmrundev2.关于setupVue中支持两种Api风格:选项式、组合式。官网中解释:使用选项式API,我们可以用......
  • vue3 预览照片的插件 v-viewer
    查看图片主要使用的旋转、翻转、缩放、上下切换、键盘操作等功能都有。1、首先是安装pnpminstallv-viewerviewerjs 2、安装完在引用(还要记得引用它的css样式)下面有两种调用方式<scriptlang="ts"setup>importViewerfrom'v-viewer';import'viewerjs/dist/vi......