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

一文了解Composition API新特性:ref、toRef、toRefs

这是我参与更文挑战的第23天,活动详情查看:更文挑战javascript

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

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

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

一、ref、toRef和toRefs是什么

(1)ref

1)ref是什么

  • ref 能够生成 值类型(即基本数据类型) 的响应式数据;
  • ref 能够用于模板reactive
  • ref 经过 .value 来修改值(必定要记得加上 .value );
  • ref 不只能够用于响应式,还能够用于模板的 DOM 元素。

2)举个例子🌰

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

<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>
复制代码

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

ref

你们能够看到,控制台前后打印的顺序是响应式前的数据响应式后的数据。所以,经过 ref ,能够实现值类型的数据响应式web


值得注意的是, 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>
复制代码

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

ref

咱们经过在模板中绑定一个 ref ,以后在生命周期中调用,最后浏览器显示出该 DOM 元素。因此说, ref 也能够用来渲染模板中的DOM元素app

(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 对象进行返回,以后在组件里面直接调用响应式对象

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

二、最佳使用方式

经过上面的演示能够得出如下几点结论:

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

三、深刻理解

讲完 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 封装的)而非普通对象
  • 注意: 不创造响应式,而是延续响应式。

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

一、规则

先来了解几条规则:

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

二、举个例子🌰

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

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

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>mouse position {{x}} {{y}}</p>
</template>

<script> import { reactive } from 'vue' import useMousePosition from './useMousePosition.js' export default { name: 'MousePosition', setup() { const { x, y } = useMousePosition() return { x, y } } } </script>
复制代码

最后,咱们再来定义一个 .vue 文件,命名为 App.vue具体代码以下:

<template>
    <MousePosition v-if="flag"/>
    <button @click="changeFlagHandler">change flag</button>
</template>

<script> import { reactive } from 'vue' import MousePosition from './components/MousePosition/index.vue' export default { name: 'MousePosition', return { flag: true }, methods: { changeFlagHandler() { this.flag = !this.flag } } } </script>
复制代码

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

mousePosition

了解完 ref 后,咱们来实现这个功能看起来会清晰不少。咱们先经过 refxy 作响应式操做,以后经过 .value 来修改值,最终达到时刻获取鼠标定位的效果。同时,若是咱们时刻保持着鼠标移动时不断改变值,这样子是很是耗费性能的,而且很容易致使内存泄漏。因此,咱们须要在组件进行 onUnmounted 的时候,及时去销毁它和解绑它。

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

3、🙅‍♀️结束语

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

到这里,关于 reftoReftoRefs 的内容就讲完啦!但愿对你们有帮助!

若有疑问或文章有误欢迎评论区留言或私信我交流~

  • 关注公众号 星期一研究室 ,第一时间关注学习干货,更多有趣的专栏待你解锁~
  • 若是这篇文章对你有用,记得 点个赞加个关注 再走哦!
  • 咱们下期见!🥂🥂🥂
相关文章
相关标签/搜索