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

Composition API新特性

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

  • 一、🙎如何理解ref、toRef和toRefs
    • 1、ref、toRef和toRefs是什么
      • (1)ref
        • 1)ref是什么
        • 2)举个例子🌰
      • (2)toRef是什么
        • 1)toRef是什么
        • 2)举个例子🌰
      • (3)toRefs是什么
        • 1)toRefs是什么
        • 2)举个例子🌰
      • (4)合成函数返回响应式对象
    • 2、最佳使用方式
    • 3、深入理解
      • (1)为什么需要用ref
      • (2)为何ref需要.value属性
      • (3)为什么需要toRef和toRefs
  • 二、🙆‍♀️Composition API实现逻辑复用
    • 1、规则
    • 2、举个例子🌰
  • 三、🙅‍♀️结束语

在 上一篇文章中,我们初步了解了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 = 20nameRef.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.pageXy.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 的内容就讲完啦!希望对大家有帮助!

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

  • 关注公众号 星期一研究室 ,第一时间关注学习干货,更多有趣的专栏待你解锁~
  • 如果这篇文章对你有用,记得 一键三连 再走哦!
  • 我们下期见!🥂🥂🥂

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/307969.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# 枚举转列表

C# 枚举转列表独立观察员 2020 年 9 月 1 日今天有朋友问我&#xff0c;ComboBox 怎么绑定一个 Enum&#xff0c;其实他的意思是枚举如何转换为列表。想想这确实是一个挺正常的需求&#xff0c;但我一时也只想到遍历&#xff0c;他觉得麻烦&#xff0c;于是我在网上帮忙查了一下…

leetcode242. 有效的字母异位词(两种方法map或数组)

一:题目 二:上码 1:方法一&#xff08;map解法&#xff09; class Solution { public:bool isAnagram(string s, string t) {/**思路:1.分析题意&#xff0c;这个是要判断t中的字符出现次数和s中字符出现的次数相同2.可以用map<char,int>来做*/map<char,int>m1,m…

活久见!月薪30k的小程序全栈开发到底有多难?

10年前&#xff0c;公司的标配是门户网站&#xff0c;造就了一批网站工作室。随着移动互联网大潮兴起&#xff0c;App又成了企业标配&#xff0c;IOS和Android开发赚的盆满钵满。然而App导致的手机内存告急&#xff0c;无止尽的信息推送&#xff0c;让微信小程序应运而生。然而…

卷不动也得继续学!紧跟vue3的步伐,再来get一波进阶新特性!

vue3进阶新特性一、&#x1f4d7;watch和watchEffect1、watch和watchEffect的区别2、举个例子&#xff08;1&#xff09;wtach监听&#xff08;2&#xff09;watchEffect监听二、&#x1f4d8;setup如何获取组件实例&#xff08;1&#xff09;为什么需要获取组件实例&#xff0…

leetcode349. 两个数组的交集(思路+详解)

一:题目 二:上码 class Solution { public:vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {/**思路:本题我们采用的哈希表数据结构是unordered_set,没有用数组因为我们不确定给出的数组中数值的范围如果 强上会出现空间的大量…

高效掌握新技能的「树型思维」

大家好&#xff0c;我是Z哥。不知道你有没有过这样的困惑&#xff0c;想学习某项新技能&#xff0c;但是很容失败。比如&#xff0c;出于职业发展的考虑&#xff0c;想学习一门新的编程语言&#xff0c;或者想了解一个新的技术框架&#xff1b;又或者看了某些综艺节目后想玩一玩…

vue2的响应式原理学“废”了吗?继续观摩vue3响应式原理Proxy

一文了解Vue3的响应式原理一、&#x1f7e9;回顾Object.defineProperty二、&#x1f7e8;Proxy基本使用三、&#x1f7e6;学习Proxy语法&#xff1a;Reflect四、&#x1f7e7;Vue3如何用Proxy实现响应式1、实现响应式2、Proxy总结&#xff08;1&#xff09;深度监听&#xff0c…

一名“企业定制化人才”的自诉:“我不愿意,但却无可奈何”

这是头哥侃码的第214篇原创几个月前&#xff0c;我曾在大智慧时期的一位同事跟我聊微信&#xff0c;说自己所在的公司这两年业务一直不温不火&#xff0c;而且从19年底就逐渐缩减技术人员。今年上半年又因为疫情的关系&#xff0c;公司干脆直接砍掉了剩余的三个技术部门&#x…

一文了解分而治之和动态规则算法在前端中的应用

一文了解分而治之和动态规则算法一、分而治之1、分而治之是什么&#xff1f;2、应用场景3、场景剖析&#xff1a;归并排序和快速排序二、动态规则1、动态规则是什么&#xff1f;2、应用场景3、场景剖析&#xff1a;斐波那契数列4、动态规则VS分而治之三、分而治之算法常见应用1…

leetcode1. 两数之和(两种方法)

一:题目 二:上码 1:方法一 class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {vector<int> v;for(int i 0; i < nums.size() - 1; i) {for(int j i1; j < nums.size(); j) {if(nums[i] nums[j] target) {v.push_…

排坑 | Exceptionless 5.x 无法正常发送邮件

【问题解决】| 作者 / Edison Zhou这是恰童鞋骚年的第282篇原创内容你有碰到过通过docker部署Exceptionless无法发送邮件的问题吗&#xff1f;此解决办法适用于Exceptionless 5.x版本&#xff08;如果你不想升级6.x的话&#xff09;。1问题起因去年这个时候&#xff0c;得知Exc…

一文了解贪心算法和回溯算法在前端中的应用

一文了解贪心算法和回溯算法在前端中的应用一、贪心算法1、贪心算法是什么&#xff1f;2、应用场景3、场景剖析&#xff1a;零钱兑换二、回溯算法1、回溯算法是什么&#xff1f;2、什么问题适合选用回溯算法解决&#xff1f;2、应用场景3、场景剖析&#xff1a;全排列三、贪心算…

leetcode454. 四数相加 II(思路+详解)

一:题目 二:上码 class Solution { public:int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {/**思路:1.我们用map容器的key值存进去前两个数的和并记录其个数,然后在后面两个数…

ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用

一文了解集合和字典在前端中的应用一、&#x1f4dd;集合1、集合是什么&#xff1f;2、前端与集合&#xff1a;使用ES6中的Set3、用Set模拟并集、交集和差集&#xff08;1&#xff09;模拟并集运算&#xff08;2&#xff09;模拟交集运算&#xff08;3&#xff09;模拟差集运算…

leetcode383. 赎金信(两种做法)

一&#xff1a;题目 二:上码 1:第一种方法 class Solution { public:bool canConstruct(string ransomNote, string magazine) {unordered_map<char,int>m,m1;for(int j 0; j < magazine.size(); j) {m[magazine[j]];}for(int i 0; i < ransomNote.size(); i) …

使用BeetleX在Linux下部署.NET多站点服务

在windows下常用IIS来部署.NET的多站点服务&#xff0c;但在Linux下就没这么方便了&#xff1b;虽然可以使用一些代理服务器如nginx&#xff0c;jexus等来反代或部署应用&#xff0c;但nginx对.NET应用的托管就相对没这么方便了&#xff0c;jexus的确是个不错的服务应用;在这里…

模块化妙用!用vue3实现一个鼠标追踪器和异步加载组件

用vue3实现一个鼠标追踪器和异步加载组件一、&#x1f5b1;️鼠标追踪器1、功能实现2、给静态页面绑定功能二、⚙️异步加载组件1、功能实现2、给静态页面绑定功能3、用泛型改造异步组件功能三、&#x1f4da;结束语周一最近学完 vue3 新特性&#xff0c;就想着用 vue3 来捣鼓…

leetcode15. 三数之和(三指针)

一:题目 二:思路 1.这里的去重是指的是我们在遍历元素的时候&#xff0c;遇到相同的挨着的相同的元素的时候要跳过 2.对元素进行排序&#xff0c;为了后面的比较 3.我们用的是三个指针&#xff0c;第一个指针i指向第一个元素&#xff0c;第二个指针left指向第二个元素,第三个指…

vue3的传送门teleport究竟有多神奇?suspense发起异步请求有多简约?

一文讲解vue3的Teleport和Suspense一、&#x1f44b;用teleport实现打开模态框操作1、teleport是什么2、实现模态框功能&#xff08;1&#xff09;设置锚点&#xff08;2&#xff09;定义子组件&#xff08;3&#xff09;定义父组件二、&#x1f91a;用Suspense1、Suspense是什…

【BCVP】实现基于 Redis 的消息队列

聆听自己的声音如果自己学不动了&#xff0c;或者感觉没有动力的时候&#xff0c;看看书&#xff0c;听听音乐&#xff0c;跑跑步&#xff0c;休息两天&#xff0c;重新出发&#xff0c;偷懒虽好&#xff0c;可不要贪杯。话说上回书我们说到了&#xff0c;Redis的使用修改《【B…