敲黑板!vue3重点!一文了解Composition API新特性:ref、toRef、toRefs

简介: 该文章深入探讨了Vue3中Composition API的关键特性,包括`ref`、`toRef`、`toRefs`的使用方法与场景,以及它们如何帮助开发者更好地管理组件状态和促进逻辑复用。

Composition API新特性

上一篇文章中,我们初步了解了vue3的新特性,今天,我们将延续 Composition API的话题,来了解 Composition API带来的新特性: reftoReftoRefs

下面开始进入本文的讲解✨

一、🙎如何理解ref、toRef和toRefs

1、ref、toRef和toRefs是什么

(1)ref

1)ref是什么
  • ref 可以生成 值类型(即基本数据类型) 的响应式数据;
  • ref 可以用于模板reative
  • ref 通过 .value 来修改值(一定要记得加上 .value );
  • ref 不仅可以用于响应式,还可以用于模板的 DOM 元素。
2)举个例子🌰

假设我们定义了两个值类型的数据,并通过一个定时器来看它响应式前后的效果。接下来我们用代码来演示一下:

<template>
    <p>ref demo {
  {ageRef}} {
  {state.name}}</p>
</template>

<script>
import {
     ref, reactive } from 'vue'

export default {
    
    name: 'Ref',
    setup(){
    
        const ageRef = ref(18)
        const nameRef = ref('monday')

        const state = reactive({
    
            name: nameRef
        })

        setTimeout(() => {
    
            console.log('ageRef', ageRef.value,'nameRef', nameRef.value)
            ageRef.value = 20
            nameRef.value = 'mondaylab'
            console.log('ageRef', ageRef.value,'nameRef', nameRef.value)
        },1500)

        return{
    
            ageRef,
            state
        }
    }
}
</script>

别眨眼,来看下此时浏览器的显示效果:

ref

大家可以看到,控制台先后打印的顺序是响应式前的数据响应式后的数据。因此,通过 ref ,可以实现值类型的数据响应式


值得注意的是, ref 不仅可以实现响应式,还可以用于模板的DOM元素我们用一段代码来演示一下:

<template>
    <p ref="elemRef">今天是周一</p>
</template>

<script>
import {
     ref, onMounted } from 'vue'

export default {
    
    name: 'RefTemplate',
    setup(){
    
        const elemRef = ref(null)

        onMounted(() => {
    
            console.log('ref template', elemRef.value.innerHTML, elemRef.value)
        })

        return{
    
            elemRef
        }
    }
}
</script>

此时浏览器的显示效果如下所示:

ref

我们通过在模板中绑定一个 ref ,之后在生命周期中调用,最后浏览器显示出该 DOM 元素。所以说, ref 也可以用来渲染模板中的DOM元素

(2)toRef是什么

1)toRef是什么
  • toRef 可以响应对象 Object ,其针对的是某一个响应式对象( reactive 封装)的属性prop

  • toRef 和对象 Object 两者保持引用关系,即一个改完另外一个也跟着改。

  • toRef 如果用于普通对象(非响应式对象),产出的结果不具备响应式。如下代码所示:

//普通对象
const state = {
   
    age: 20,
    name: 'monday'
}
//响应式对象
const state = reactive({
   
    age: 20,
    name: 'monday'
})
2)举个例子🌰

对于一个普通对象来说,如果这个普通对象要实现响应式,就用 reactive 。用了 reactive 之后,它就在响应式对象里面。那么在 一个响应式对象里面,如果其中有一个属性要拿出来单独做响应式的话,就用 toRef 。来举个例子看一看:

<template>
    <p>toRef demo - {
  {ageRef}} - {
  {state.name}} {
  {state.age}}</p>
</template>

<script>
import {
     ref, toRef, reactive, computed } from 'vue'

export default {
    
    name: 'ToRef',
    setup() {
    
        const state = reactive({
    
            age: 18,
            name: 'monday'
        })

        // // toRef 如果用于普通对象(非响应式对象),产出的结果不具备响应式
        // const state = {
    
        //     age: 18,
        //     name: 'monday'
        // }

        //实现某一个属性的数据响应式
        const ageRef = toRef(state, 'age')

        setTimeout(() => {
    
            state.age = 20
        }, 1500)

        setTimeout(() => {
    
            ageRef.value = 25 // .value 修改值
        }, 3000)

        return {
    
            state,
            ageRef
        }
    }
}
</script>

此时我们来看下浏览器的显示效果:

toRef

我们通过 reactive 来创建一个响应式对象,之后呢,如果只单独要对响应式对象里面的某一个属性进行响应式,那么使用toRef 来解决。用 toRef(Object, prop) 的形式来传对象名具体的属性名,达到某个属性数据响应式的效果。

(3)toRefs是什么

1)toRefs是什么
  • toRef 不一样的是, toRefs 是针对整个对象的所有属性,目标在于将响应式对象( reactive 封装)转换为普通对象
  • 普通对象里的每一个属性 prop 都对应一个 ref
  • toRefs 和对象 Object 两者保持引用关系,即一个改完另外一个也跟着改。
2)举个例子🌰

假设我们要将一个响应式对象里面的所有元素取出来,那么我们可以这么处理。代码如下:

<template>
    <p>toRefs demo {
  {state.age}} {
  {state.name}}</p>
</template>

<script>
import {
     ref, toRef, toRefs, reactive } from 'vue'

export default {
    
    name: 'ToRefs',
    setup() {
    
        const state = reactive({
    
            age: 20,
            name: 'monday'
        })

        return {
    
            state
        }
    }
}
</script>

此时浏览器的显示结果如下:

toRefs

但是这样子好像有点略显麻烦,因为在模板编译的时候一直要 state. ,这样如果遇到要取很多个属性的时候就有点臃肿了。


既然太臃肿了,那我们换一种思路,把 state 进行解构。代码如下:

<template>
    <p>toRefs demo {
  {age}} {
  {name}}</p>
</template>

<script>
import {
     ref, toRef, toRefs, reactive } from 'vue'

export default {
    
    name: 'ToRefs',
    setup() {
    
        const state = reactive({
    
            age: 20,
            name: 'monday'
        })

        return {
    
            ...state
        }
    }
}
</script>

此时浏览器的显示结果如下:

toRefs

效果是一样的,看起来清晰了很多。但是呢……天上总不会有无缘无故的馅饼出现,得到一些好处的同时总要失去些原本拥有的东西。


对于解构后的对象来说,如果直接解构 reactive ,那么解构出来的对象会直接失去响应式。我们用一个定时器来检验下效果,具体代码如下:

<template>
    <p>toRefs demo {
  {age}} {
  {name}}</p>
</template>

<script>
import {
     ref, toRef, toRefs, reactive } from 'vue'

export default {
    
    name: 'ToRefs',
    setup() {
    
        const state = reactive({
    
            age: 20,
            name: 'monday'
        })

         setTimeout(() => {
    
             state.age = 25
         }, 1500)

        return {
    
            ...state
        }
    }
}
</script>

此时浏览器的显示结果如下:

toRefs

我们等了好几秒之后,发现 age 迟迟不变成25,所以当我们解构 reactive 的对象时,响应式将会直接失去。


所以,就来到了我们的 toRefstoRefs 在把响应式对象转变为普通对象后,不会丢失掉响应式的功能。具体我们用代码来演示一下:

<template>
    <p>toRefs demo {
  {age}} {
  {name}}</p>
</template>

<script>
import {
     ref, toRef, toRefs, reactive } from 'vue'

export default {
    
    name: 'ToRefs',
    setup() {
    
        const state = reactive({
    
            age: 18,
            name: 'monday'
        })

        const stateAsRefs = toRefs(state) // 将响应式对象,变成普通对象

        setTimeout(() => {
    
            console.log('age', state.age, 'name', state.name)
            state.age = 20,
            state.name = '周一'
            console.log('age', state.age, 'name', state.name)
        }, 1500)

        return stateAsRefs
    }
}
</script>

此时我们观察浏览器的显示效果:

toRefs

大家可以看到,用了 toRefs ,普通对象的值成功被取出来了,并且还不会丢失响应式的功能,该改变的值一个也不少。

(4)合成函数返回响应式对象

了解了上面三种类型的使用,我们再来看一种场景:合成函数如何返回响应式对象下面附上代码:

function useFeatureX(){
       
    const state = reactive({
           
    x: 1,        
    y: 2    
    })        
    //逻辑运行状态,……        
    //返回时转换为ref    
    return toRefs(state)
}
export default{
       setup(){
           
    //可以在不失去响应性的情况下破坏结构        
    const {
   x, y} = useFeatureX()                
    return{
               
        x,            
        y        
    }    
}}

在第一段代码中,我们定义了一个函数,并且用 toRefsstate 对象进行返回,之后在组件里面直接调用响应式对象

通过这样方式,让代码逻辑变得更加清晰明了,复用性更强。

2、最佳使用方式

通过上面的演示可以得出以下几点结论:

  • reactive 做对象的响应式,用 ref值类型的响应式。
  • setup 中返回 toRefs(state) ,或者 toRef(state, 'xxx')
  • 为了防止误会产生, ref 的变量命名尽量都用 xxxRef ,这样在使用的时候会更清楚明了。
  • 合成函数返回响应式对象时,使用 toRefs

3、深入理解

讲完 reftoReftoRefs ,我们再来思考一个问题:为什么一定要用它们呢?可以不用吗?

(1)为什么需要用ref

  • 值类型(即基本数据类型)无处不在,如果不用 ref 而直接返回值类型,会丢失响应式
  • 比如在 setupcomputed合成函数等各种场景中,都有可能返回值类型
  • Vue 如果不定义 ref ,用户将自己制造 ref ,这样反而会更加混乱。

(2)为何ref需要.value属性

通过上面的分析我们知道, ref 需要通过 .value 来修改值。这看起来是一个很麻烦的操作,总是频繁的 .value 感觉特别琐碎。那为什么一定要 .value 呢?我们来揭开它的面纱。

  • ref 是一个对象,这个对象不丢失响应式,且这个对象用 value 来存储值。
  • 因此,通过 .value 属性的 getset 来实现响应式。
  • 只有当用于 模板reactive 时,不需要 .value 来实现响应式,而其他情况则都需要

(3)为什么需要toRef和toRefs

ref 不一样的是, toReftoRefs 这两个兄弟,它们不创造响应式,而是延续响应式。创造响应式一般由 ref 或者 reactive 来解决,而 toReftoRefs 则是把对象的数据进行分解和扩散,其这个对象针对的是响应式对象非普通对象总结起来有以下三点:

  • 初衷:不丢失响应式的情况下,把对象数据进行 分解或扩散
  • 前提: 针对的是响应式对象reactrive 封装的)而非普通对象
  • 注意: 不创造响应式,而是延续响应式。

二、🙆‍♀️Composition API实现逻辑复用

1、规则

先来了解几条规则:

  • Composition API抽离逻辑代码到一个函数
  • 函数的命名约定为 useXxxx 格式(React hooks也是);
  • setup 中引用 useXxx 函数。

2、举个例子🌰

引用一个非常经典的例子:获取鼠标的定位接下来我们用Composition API来进行封装演示:

定义一个 js 文件,名字为 useMousePosition具体代码如下:

import {
    reactive, ref, onMounted, onUnmounted } from 'vue'

function useMousePosition() {
   
    const x = ref(0)
    const y = ref(0)

    function update(e) {
   
        x.value = e.pageX
        y.value = e.pageY
    }

    onMounted(() => {
   
        console.log('useMousePosition mounted')
        window.addEventListener('mousemove', update)
    })

    onUnmounted(() => {
   
        console.log('useMousePosition unMounted')
        window.removeEventListener('mousemove', update)
    })

    return {
   
        x,
        y
    }
}

再定义一个 .vue 文件,命名为 index.vue具体代码如下:

<template>
    <p  v-if="flag">mouse position {
  {x}} {
  {y}}</p>
    <button @click="changeFlagHandler">change flag</button>
</template>

<script>
import {
     reactive } from 'vue'
import useMousePosition from './useMousePosition'

export default {
    
    name: 'MousePosition',
    return {
    
      flag: true
    },
    setup() {
    
        const {
     x, y } = useMousePosition()
        return {
    
            x,
            y
        }
    }changeFlagHandler() {
    
      this.flag = !this.flag
    },
}
</script>

此时浏览器的显示效果如下:

mousePosition

了解完 ref 后,我们来实现这个功能看起来会清晰很多。我们先通过 refxy 做响应式操作,之后通过 .value 来修改值,最终达到时刻获取鼠标定位的效果。同时,如果我们时刻保持着鼠标移动时不断改变值,这样子是非常耗费性能的。所以,我们可以通过一个按钮,来随时控制它的出现与隐藏。

大家可以发现,当隐藏的时候,随后会触发 onUnmounted 生命周期组件内容随之被销毁。也就是说,使用的时候调用,不使用的时候及时销毁,这样子可以很大程度上提升性能

三、🙅‍♀️结束语

通过上文的学习,我们可以知道, reftoReftoRefsvue3Composition API 的新特性,且 vue3 一般通过 reftoReftoRefs实现数据响应式。有了这三个内容,实现数据响应式看起来方便许多,而不再像 vue2 中那种处理起来很困难。

到这里,关于 reftoReftoRefs 的内容就讲完啦!希望对大家有帮助!

如有疑问或文章有误欢迎评论区浏览或私信我交流~

  • 关注公众号 星期一研究室 ,第一时间关注学习干货,更多有趣的专栏待你解锁~
  • 如果这篇文章对你有用,记得 一键三连 再走哦!
  • 我们下期见!🥂🥂🥂
相关文章
|
1月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
42 4
|
19天前
|
JavaScript 前端开发 API
Vue 3新特性详解:Composition API的威力
【10月更文挑战第25天】Vue 3 引入的 Composition API 是一组用于组织和复用组件逻辑的新 API。相比 Options API,它提供了更灵活的结构,便于逻辑复用和代码组织,特别适合复杂组件。本文将探讨 Composition API 的优势,并通过示例代码展示其基本用法,帮助开发者更好地理解和应用这一强大工具。
25 1
|
1月前
|
缓存 JavaScript API
Vue 3的全新Reactivity API:解锁响应式编程的力量
Vue 3引入了基于Proxy的全新响应式系统,提升了性能并带来了更强大的API。本文通过示例详细介绍了`reactive`、`ref`、`computed`、`watch`等核心API的使用方法,帮助开发者深入理解Vue 3的响应式编程。无论你是初学者还是资深开发者,都能从中受益,构建更高效的应用程序。
19 1
|
1月前
|
缓存 JavaScript 前端开发
深入理解 Vue 3 的 Composition API 与新特性
本文详细探讨了 Vue 3 中的 Composition API,包括 setup 函数的使用、响应式数据管理(ref、reactive、toRefs 和 toRef)、侦听器(watch 和 watchEffect)以及计算属性(computed)。我们还介绍了自定义 Hooks 的创建与使用,分析了 Vue 2 与 Vue 3 在响应式系统上的重要区别,并概述了组件生命周期钩子、Fragments、Teleport 和 Suspense 等新特性。通过这些内容,读者将能更深入地理解 Vue 3 的设计理念及其在构建现代前端应用中的优势。
31 0
深入理解 Vue 3 的 Composition API 与新特性
|
24天前
|
API
《vue3第四章》Composition API 的优势,包含Options API 存在的问题、Composition API 的优势
《vue3第四章》Composition API 的优势,包含Options API 存在的问题、Composition API 的优势
25 0
|
24天前
|
JavaScript 前端开发 API
《vue3第六章》其他,包含:全局API的转移、其他改变
《vue3第六章》其他,包含:全局API的转移、其他改变
20 0
|
6天前
|
JSON API 数据格式
淘宝 / 天猫官方商品 / 订单订单 API 接口丨商品上传接口对接步骤
要对接淘宝/天猫官方商品或订单API,需先注册淘宝开放平台账号,创建应用获取App Key和App Secret。之后,详细阅读API文档,了解接口功能及权限要求,编写认证、构建请求、发送请求和处理响应的代码。最后,在沙箱环境中测试与调试,确保API调用的正确性和稳定性。
|
18天前
|
供应链 数据挖掘 API
电商API接口介绍——sku接口概述
商品SKU(Stock Keeping Unit)接口是电商API接口中的一种,专门用于获取商品的SKU信息。SKU是库存量单位,用于区分同一商品的不同规格、颜色、尺寸等属性。通过商品SKU接口,开发者可以获取商品的SKU列表、SKU属性、库存数量等详细信息。
|
19天前
|
JSON API 数据格式
店铺所有商品列表接口json数据格式示例(API接口)
当然,以下是一个示例的JSON数据格式,用于表示一个店铺所有商品列表的API接口响应
|
29天前
|
编解码 监控 API
直播源怎么调用api接口
调用直播源的API接口涉及开通服务、添加域名、获取API密钥、调用API接口、生成推流和拉流地址、配置直播源、开始直播、监控管理及停止直播等步骤。不同云服务平台的具体操作略有差异,但整体流程简单易懂。