vue3【实用教程】声明响应式状态(含ref,reactive,toRef(),toRefs() 等)

简介: vue3【实用教程】声明响应式状态(含ref,reactive,toRef(),toRefs() 等)

Vue 3 中的数据基于 JavaScript Proxy (代理) 实现响应式

( vue2 中的数据通过 Object.defineProperty() 方法和对数组变异方法的重写,实现响应式)


选项式 API

用 data 选项声明响应式状态,值为返回一个对象的函数。

  • 在创建组件实例的时候会调用此函数
  • 函数返回的对象会用响应式系统进行包装
  data() {
    return {
      count: 1
    }
  },

注意事项

  • data 的顶层属性避免用 $_开头(Vue 在组件实例上暴露的内置 API 使用 $ 作为前缀,也为内部属性保留 _ 前缀。)
  • 未在data中定义的组件实例新属性,无法触发响应式更新。
  • 始终通过 this 来访问响应式状态
  data() {
    return {
      someObject: {}
    }
  },
  mounted() {
    const newObject = {}
    this.someObject = newObject

    console.log(newObject === this.someObject) // false
  }

这里原始的 newObject 不会变为响应式。

组合式 API

方式一 ref 【推荐】

所有类型的数据,都推荐使用 ref 声明响应式。

import { ref } from 'vue'

// 定义一个初始值为数字 0 的响应式变量 countRef  
const countRef = ref(0)

// 得到一个带有 .value 属性的 ref 对象
console.log(countRef ) // { value: 0 }

// 通过 .value 访问 ref 响应式变量的值
console.log(countRef.value) // 0

// 通过 .value 修改 ref 响应式变量的值
countRef.value++
  • 参数为响应式变量的初始值(当参数是一个对象时,ref() 会在内部调用reactive()
  • 返回一个带有 .value 属性的 ref 对象
  • 建议变量名以Ref为后缀,以便识别其是一个 ref 对象
  • 模板中使用时,ref 对象会自动解包,无需使用.value
<button @click="countRef++">
  {{ countRef }}
</button>
  • ref 对象在作为响应式对象的属性被访问或修改时会自动解包。
const countRef = ref(0)

// reactive 的用法详见下文
const state = reactive({
  count:countRef 
})

console.log(state.count) // 0 ,无需写成 state.count.value

state.count = 1 // 无需写成 state.count.value
console.log(countRef .value) // 1
  • ref 的另一个功能是获取模板引用(即 vue2 中的 this.$refs)
<script setup>
import { ref, onMounted } from 'vue'

// 获取到 ref 属性为 input 的模板元素的引用(ref 变量名必须和模板里的 ref 属性值相同)
const input = ref(null)

// 生命周期:DOM 挂载完成
onMounted(() => {
  // input 输入框获得焦点(注意此处需使用.value)
  input.value.focus()
})
</script>

<template>
  <!-- ref 属性为 input 的模板元素 -->
  <input ref="input" />
</template>

为什么需要 ref

因为 vue3 的响应式是通过 proxy 实现的,但 proxy 只能给对象添加响应式,无法给值类型的数据添加响应式,所以需要通过 ref() 函数先将值类型的数据包装成一个带 value 属性的 ref 对象,才能实现值类型数据的响应式。

为什么需要 .value

因为 ref() 函数返回的是一个 ref 对象,响应式变量的值存储在 ref 对象的value 属性中,所以在 js 中读取和修改响应式变量的值的时候,都需要 .value ,但在以下情况中,为了方便使用,vue 对 ref 对象进行了自动解包,所以无需 .value

  1. 模板中使用 ref 对象
  2. ref 对象作为响应式对象的属性被访问或修改

TS 中给 ref 声明类型

ref 会根据初始化时的值推导其类型

// 推导出的类型:Ref<number>
const year = ref(2020)

但比较复杂的数据类型,需用 Ref

import { ref } from 'vue'
import type { Ref } from 'vue'

interface userInfo {
  id: number
  name: String
}

const userList: Ref<userInfo[]> = ref([])

方式二 reactive

用于创建对象类型数据的响应式。

import { reactive } from 'vue'

const state = reactive({ count: 0 })
 
<button @click="state.count++">
  {{ state.count }}
</button>
  • reactive() 无需对源数据添加类似 ref 的包装,可使对象本身具有响应性
  • reactive() 返回的是一个原始对象的 Proxy,它和原始对象是不相等的
const raw = {}
const proxy = reactive(raw)

// 代理对象和原始对象不是全等的
console.log(proxy === raw) // false

reactive() 的局限性

  • 只能用于对象类型数据 (对象、数组和如 Map、Set 这样的集合类型),不能用于 string、number 或 boolean 这样的值类型数据
  • 替换整个对象会丢失响应式(Vue 的响应式跟踪是通过属性访问实现的),所以只能通过修改属性来修改 reactive() 创建的响应式变量。
  • 解构会丢失响应式
const state = reactive({ count: 0 })

// 当解构时,count 已经与 state.count 断开连接
let { count } = state
// 不会影响原始的 state
count++

其他相关 API

vue 提供了专门的API 来实现在不丢失响应式的情况下,解构响应式对象(reactive 创建),它们不创造响应式 (如 ref , reactive),而是延续响应式 (toRef , toRefs) !

toRef()

基于响应式对象上的属性,创建 ref (与源属性保持同步)

  • 第一个参数为 响应式对象(reactive 创建)
  • 第二个参数为 属性名
const state = reactive({
  foo: 1
})

// 基于响应式对象 state 上的属性 foo ,创建 ref
const fooRef = toRef(state, 'foo')

// 更改该 ref 会更新源属性
fooRef.value++
console.log(state.foo) // 2

// 更改源属性也会更新该 ref
state.foo++
console.log(fooRef.value) // 3

https://cn.vuejs.org/api/reactivity-utilities.html#toref


toRefs()

将响应式对象转换为每个属性都指向源对象相应属性的 ref 的普通对象

  • 推荐用法:合成函数返回响应式对象时,使用 toRefs,以便解构赋值时不丢失响应式。(详见下文的范例)
const state = reactive({
  foo: 1,
  bar: 2
})

// 得到一个每个属性都是 ref 的普通对象
const stateAsRefs = toRefs(state)
/*
stateAsRefs 的类型:{
  foo: Ref<number>,
  bar: Ref<number>
}
*/

// 源属性改变,Refs 对象里的 ref 属性会同步改变
state.foo++
console.log(stateAsRefs.foo.value) // 2

// Refs 对象里的 ref 属性改变,源属性会同步改变
stateAsRefs.foo.value++
console.log(state.foo) // 3

应用范例:解构对象属性,简化模板引用

<!-- 组合式API -->
<script setup>
import { reactive, toRefs } from "vue";

function getMyInfo() {
  const me = reactive({
    name: "朝阳",
    age: 35,
  });

  // 使用 toRefs 避免丢失响应式
  return toRefs(me);
}

// 通过解构赋值,便于模板中使用
let { name, age } = getMyInfo();

const changeName = () => {
  name.value = "张三";
};
</script>

<template>
  <p>姓名:{{ name }}</p>
  <p>年龄:{{ age }}</p>
  <button @click="changeName">改名</button>
</template>

isRef()

判断是否为 ref

let age = ref(35)
if (isRef(age)) {
}

isReactive()

判断一个对象是否是由 reactive() 或 shallowReactive() 创建的代理。

isProxy()

判断一个对象是否是由 reactive()、readonly()、shallowReactive() 或 shallowReadonly() 创建的代理。

unref()

对 ref 对象解包,是 isRef(val) ? val.value : val 的语法糖  

目录
相关文章
|
1月前
|
缓存 JavaScript PHP
斩获开发者口碑!SnowAdmin:基于 Vue3 的高颜值后台管理系统,3 步极速上手!
SnowAdmin 是一款基于 Vue3/TypeScript/Arco Design 的开源后台管理框架,以“清新优雅、开箱即用”为核心设计理念。提供角色权限精细化管理、多主题与暗黑模式切换、动态路由与页面缓存等功能,支持代码规范自动化校验及丰富组件库。通过模块化设计与前沿技术栈(Vite5/Pinia),显著提升开发效率,适合团队协作与长期维护。项目地址:[GitHub](https://github.com/WANG-Fan0912/SnowAdmin)。
256 5
|
1月前
|
JavaScript API 容器
Vue 3 中的 nextTick 使用详解与实战案例
Vue 3 中的 nextTick 使用详解与实战案例 在 Vue 3 的日常开发中,我们经常需要在数据变化后等待 DOM 更新完成再执行某些操作。此时,nextTick 就成了一个不可或缺的工具。本文将介绍 nextTick 的基本用法,并通过三个实战案例,展示它在表单验证、弹窗动画、自动聚焦等场景中的实际应用。
107 17
|
2月前
|
JavaScript 前端开发 算法
Vue 3 和 Vue 2 的区别及优点
Vue 3 和 Vue 2 的区别及优点
|
2月前
|
存储 JavaScript 前端开发
基于 ant-design-vue 和 Vue 3 封装的功能强大的表格组件
VTable 是一个基于 ant-design-vue 和 Vue 3 的多功能表格组件,支持列自定义、排序、本地化存储、行选择等功能。它继承了 Ant-Design-Vue Table 的所有特性并加以扩展,提供开箱即用的高性能体验。示例包括基础表格、可选择表格和自定义列渲染等。
139 6
|
29天前
|
JavaScript 前端开发 API
Vue 2 与 Vue 3 的区别:深度对比与迁移指南
Vue.js 是一个用于构建用户界面的渐进式 JavaScript 框架,在过去的几年里,Vue 2 一直是前端开发中的重要工具。而 Vue 3 作为其升级版本,带来了许多显著的改进和新特性。在本文中,我们将深入比较 Vue 2 和 Vue 3 的主要区别,帮助开发者更好地理解这两个版本之间的变化,并提供迁移建议。 1. Vue 3 的新特性概述 Vue 3 引入了许多新特性,使得开发体验更加流畅、灵活。以下是 Vue 3 的一些关键改进: 1.1 Composition API Composition API 是 Vue 3 的核心新特性之一。它改变了 Vue 组件的代码结构,使得逻辑组
106 0
|
3月前
|
JavaScript 前端开发 UED
vue2和vue3的响应式原理有何不同?
大家好,我是V哥。本文详细对比了Vue 2与Vue 3的响应式原理:Vue 2基于`Object.defineProperty()`,适合小型项目但存在性能瓶颈;Vue 3采用`Proxy`,大幅优化初始化、更新性能及内存占用,更高效稳定。此外,我建议前端开发者关注鸿蒙趋势,2025年将是国产化替代关键期,推荐《鸿蒙 HarmonyOS 开发之路》卷1助你入行。老项目用Vue 2?不妨升级到Vue 3,提升用户体验!关注V哥爱编程,全栈开发轻松上手。
171 2
|
3月前
|
JavaScript 前端开发 算法
高效工作流:用Mermaid绘制你的专属流程图;如何在Vue3中导入mermaid绘制流程图
mermaid是一款非常优秀的基于 JavaScript 的图表绘制工具,可渲染 Markdown 启发的文本定义以动态创建和修改图表。非常适合新手学习或者做一些弱交互且自定义要求不高的图表 除了流程图以外,mermaid还支持序列图、类图、状态图、实体关系图等图表可供探索。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
3月前
|
JavaScript 前端开发 API
管理数据必备;侦听器watch用法详解,vue2与vue3中watch的变化与差异
一篇文章同时搞定Vue2和Vue3的侦听器,是不是很棒?不要忘了Vue3中多了一个可选项watchEffect噢。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
2月前
|
JavaScript
vue实现任务周期cron表达式选择组件
vue实现任务周期cron表达式选择组件
247 4
|
11天前
|
JavaScript 前端开发 开发者
Vue 自定义进度条组件封装及使用方法详解
这是一篇关于自定义进度条组件的使用指南和开发文档。文章详细介绍了如何在Vue项目中引入、注册并使用该组件,包括基础与高级示例。组件支持分段配置(如颜色、文本)、动画效果及超出进度提示等功能。同时提供了完整的代码实现,支持全局注册,并提出了优化建议,如主题支持、响应式设计等,帮助开发者更灵活地集成和定制进度条组件。资源链接已提供,适合前端开发者参考学习。
99 17