Vue的ref、shallowRef、reactive到底要怎么用!!?😵

你是一个vue3的初学者肯定会被这些响应式的API搞晕了,但实际上你不需要把它们想得太过复杂,通常你不会用到所有的API。

这里直接提供一种最简单的区分办法,你可以按照这个规则来学习,保证你的程序不会有问题:

  1. 只用ref声明响应式变量
  2. 只有当第三方库的操作对象需要具备响应性时,才使用shallowRef
  3. 只有当使用组合式函数需要命名空间时,才使用reactive

通过这个规则,你先快速入门,先能会用,后面再去慢慢看文档理解他们的作用

1. 只用ref声明响应式变量

什么是响应式变量?指的是那些你给它赋值了能引起UI更新的变量

虽然那个reactive声明的变量也具备响应性,但是,想要将vue3简单用,你应该选择最简单的方式,因此,无论是对象、数组、数字、字符串、布尔值、HTML引用,你都用ref来声明:

  • 不推荐 的做法👎👎👎:refreactive混着用

    javascript 复制代码
    const tableData = reactive([]);
    const obj = reactive({})
    const a = ref([])
    const b = ref(1)
  • 推荐 的做法👍👍👍:只用ref声明响应式变量

    javascript 复制代码
    const arr = ref([])
    const obj = ref({})
    const num = ref(0)
    const str = ref('')
    const bool = ref(false)
    const nil = ref(null)
    const udf = ref()

2. 只有当第三方库的操作对象需要具备响应性时,才使用shallowRef

什么是第三方库的操作对象?指的是那些不是开发时声明出来的,而是第三方库API创建并暴露出来给你操作的对象。比如Echart初始化之后会产生一个对象,允许你调用其setOption方法来更新图表,这个对象就属于第三方库的操作对象。

通常情况下,对于第三方库的操作对象,是不需要添加响应性的:

javascript 复制代码
let mychart = null
onMounted(()=>{
    mychart = echarts.init(document.getElement('chart'))
})

但是在有些场景下,你需要让这个对象具备响应性:比如你需要以props的形式将这个操作对象传递给子组件,并且这个操作对象还可能发生变化,你希望子组件也能跟着变化。

那么这时候你应该只使用shallowRef这个API,为这些你不知道底细的第三方库产生的对象提供响应性支持:

javascript 复制代码
let mychart = shallowRef(null)
onMounted(()=>{
    createNewChart()
})

function onClick(){
    createNewChart()
}

function createNewChart(){
  // 销毁旧对象
  if(mychart.value){
      mychart.value.dispose()
  }
  
  // 启用新对象
  mychart.value = echarts.init(document.getElement('chart'))
}
  • 为什么不能使用ref给这些对象提供响应性?因为refshallowRef的区别在于,ref会遍历整个对象,给对象的每个属性都创建响应性,无论是多深的对象,你给任何一个属性赋值,都会刷新界面。而shallowRef相反,只有给其.value赋值时,才会触发界面刷新。
  • 这就导致一些问题,当第三方库的操作对象也在监听内部数据自我更新时,就会产生一种:"你更新了,我监听到了,我更新;我更新了,你监听到了,你又更新;你又更新了,我监听到了,我又又更新..."的死循环中,然后导致页面崩溃。
  • 所以对于你不知道底细的对象(通常情况下也就只有第三方库会产生),直接使用shallowRef创建响应性。

3. 只有当使用组合式函数需要命名空间时,才使用reactive

什么是 组合式函数 ,指的是那些将响应式变量封装起来的函数。

官方文档传送门:组合式函数 | Vue.js (vuejs.org)

组合式API最大的优势在于函数级别的复用,这里的函数级别指的就是组合式函数

javascript 复制代码
const {chart} = useChart(div,{});

// useChart可以到处用
function useChart(el,option){
  let chart = shallowRef(echart.init(el))
  chart.setOption(option);
  return {
    chart
  }
}

当同一个组合式函数被多次使用时,你需要做别名:

javascript 复制代码
const {chart:chart1} = useChart(div1,{});
const {chart:chart2} = useChart(div2,{});
const {chart:chart3} = useChart(div3,{});

你也可以给组合式函数套一个命名空间

javascript 复制代码
const chart1 = useChart(div1,{});
const chart2 = useChart(div2,{});
const chart3 = useChart(div3,{});

但是这样写,你在模板传递参数时,问题就来了,在<template>里传递参数你必须加上.value

html 复制代码
<template>
    <!--生效👍-->
    <child-component :chart="chart1.chart.value"></child-component>
    
    <!--无效👎-->
    <child-component :chart="chart1.chart"></child-component>
    
     <!--无效👎-->
    <child-component v-bind="chart1"></child-component>
</template>

那你不想加.value怎么办?按照vue官网的做法,给函数套一个reactive就可以了:

js 复制代码
<template>
    <!--生效👍-->
    <child-component :chart="chart1.chart"></child-component>
    
     <!--生效👍-->
    <child-component v-bind="chart1"></child-component>
    <child-component v-bind="chart2"></child-component>
    <child-component v-bind="chart3"></child-component>
</template>
<script setup>
const chart1 = reactive(useChart(div1,{}));
const chart2 = reactive(useChart(div2,{}));
const chart3 = reactive(useChart(div3,{}));
</script>

所以,当只有你想给响应式函数添加一个命名空间的时候,才使用reactive

其他情况,参照前2个规则进行处理。

总结

  1. 为了避免认知混乱,基本上都使用ref进行声明响应式变量。
  2. 为了避免页面卡死,对于那些你不知道层级结构的数据,使用shallowRef为它创建响应性。
  3. 除非你是要给组合式函数加一个命名空间然后给组件使用v-bind,否则其他情况下都不建议考虑使用reactive
相关推荐
leoZ2317 分钟前
记忆系统与 Agent 定制完全指南(三):记忆的检索与使用
前端·chrome
遇乐的果园19 分钟前
前端学习笔记-vue状态管理优化
前端·笔记·学习
GIS阵地1 小时前
QgsSingleBandPseudoColorRenderer 完整详解(QGIS 3.40.13 C++)
开发语言·前端·c++·qt·qgis
刘较瘦_2 小时前
AI 开发中的 Git Submodule 父子仓库模式:前后端分仓管理与协作实践
前端·github
牧艺2 小时前
cos-design WeatherBackground:用 Canvas 做一个「会变天」的背景引擎
前端·canvas·视觉设计
OpenTiny社区2 小时前
深度解析 LSP 如何为 AI 装上“眼睛”
前端·ai编程
布列瑟农的星空2 小时前
流程类SVG画布的通用开发范式
前端
fsssb2 小时前
Chromium 源码学习笔记(七):那些跨进程的调用,底下都是同一个东西——Mojo
前端
gis开发之家3 小时前
《Vue3 从入门到大神35篇》Vue3 源码详解(五):effect 依赖收集原理——track 与 trigger 是如何工作的?
javascript·typescript·前端框架·vue3·vue3源码
MichaelJohn3 小时前
从零星白屏到“启发式缓存”,记录一次刚接手屎山的惊险排查
前端