敲黑板!vue3重点!一文了解Composition API新特性:ref、toRef、toRefs
一文了解Composition API新特性:ref、toRef、toRefs
- 一、🙎如何理解ref、toRef和toRefs
- 1、ref、toRef和toRefs是什么
- (1)ref
- 1)ref是什么
- 2)舉個(gè)例子🌰
- (2)toRef是什么
- 1)toRef是什么
- 2)舉個(gè)例子🌰
- (3)toRefs是什么
- 1)toRefs是什么
- 2)舉個(gè)例子🌰
- (4)合成函數(shù)返回響應(yīng)式對(duì)象
- 2、最佳使用方式
- 3、深入理解
- (1)為什么需要用ref
- (2)為何ref需要.value屬性
- (3)為什么需要toRef和toRefs
- 二、🙆?♀?Composition API實(shí)現(xiàn)邏輯復(fù)用
- 1、規(guī)則
- 2、舉個(gè)例子🌰
- 三、🙅?♀?結(jié)束語
在 上一篇文章中,我們初步了解了vue3的新特性,今天,我們將延續(xù) Composition API的話題,來了解 Composition API帶來的新特性: ref 、 toRef 和 toRefs 。
下面開始進(jìn)入本文的講解?
一、🙎如何理解ref、toRef和toRefs
1、ref、toRef和toRefs是什么
(1)ref
1)ref是什么
- ref 可以生成 值類型(即基本數(shù)據(jù)類型) 的響應(yīng)式數(shù)據(jù);
- ref 可以用于模板和reative;
- ref 通過 .value 來修改值(一定要記得加上 .value );
- ref 不僅可以用于響應(yīng)式,還可以用于模板的 DOM 元素。
2)舉個(gè)例子🌰
假設(shè)我們定義了兩個(gè)值類型的數(shù)據(jù),并通過一個(gè)定時(shí)器來看它響應(yīng)式前后的效果。接下來我們用代碼來演示一下:
<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>別眨眼,來看下此時(shí)瀏覽器的顯示效果:
大家可以看到,控制臺(tái)先后打印的順序是響應(yīng)式前的數(shù)據(jù)和響應(yīng)式后的數(shù)據(jù)。因此,通過 ref ,可以實(shí)現(xiàn)值類型的數(shù)據(jù)響應(yīng)式。
值得注意的是, ref 不僅可以實(shí)現(xiàn)響應(yīng)式,還可以用于模板的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>此時(shí)瀏覽器的顯示效果如下所示:
我們通過在模板中綁定一個(gè) ref ,之后在生命周期中調(diào)用,最后瀏覽器顯示出該 DOM 元素。所以說, ref 也可以用來渲染模板中的DOM元素。
(2)toRef是什么
1)toRef是什么
-
toRef 可以響應(yīng)對(duì)象 Object ,其針對(duì)的是某一個(gè)響應(yīng)式對(duì)象( reactive 封裝)的屬性prop 。
-
toRef 和對(duì)象 Object 兩者保持引用關(guān)系,即一個(gè)改完另外一個(gè)也跟著改。
-
toRef 如果用于普通對(duì)象(非響應(yīng)式對(duì)象),產(chǎn)出的結(jié)果不具備響應(yīng)式。如下代碼所示:
2)舉個(gè)例子🌰
對(duì)于一個(gè)普通對(duì)象來說,如果這個(gè)普通對(duì)象要實(shí)現(xiàn)響應(yīng)式,就用 reactive 。用了 reactive 之后,它就在響應(yīng)式對(duì)象里面。那么在 一個(gè)響應(yīng)式對(duì)象里面,如果其中有一個(gè)屬性要拿出來單獨(dú)做響應(yīng)式的話,就用 toRef 。來舉個(gè)例子看一看:
<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 如果用于普通對(duì)象(非響應(yīng)式對(duì)象),產(chǎn)出的結(jié)果不具備響應(yīng)式// const state = {// age: 18,// name: 'monday'// }//實(shí)現(xiàn)某一個(gè)屬性的數(shù)據(jù)響應(yīng)式const ageRef = toRef(state, 'age')setTimeout(() => {state.age = 20}, 1500)setTimeout(() => {ageRef.value = 25 // .value 修改值}, 3000)return {state,ageRef}} } </script>此時(shí)我們來看下瀏覽器的顯示效果:
我們通過 reactive 來創(chuàng)建一個(gè)響應(yīng)式對(duì)象,之后呢,如果只單獨(dú)要對(duì)響應(yīng)式對(duì)象里面的某一個(gè)屬性進(jìn)行響應(yīng)式,那么使用toRef 來解決。用 toRef(Object, prop) 的形式來傳對(duì)象名和具體的屬性名,達(dá)到某個(gè)屬性數(shù)據(jù)響應(yīng)式的效果。
(3)toRefs是什么
1)toRefs是什么
- 與 toRef 不一樣的是, toRefs 是針對(duì)整個(gè)對(duì)象的所有屬性,目標(biāo)在于將響應(yīng)式對(duì)象( reactive 封裝)轉(zhuǎn)換為普通對(duì)象
- 普通對(duì)象里的每一個(gè)屬性 prop 都對(duì)應(yīng)一個(gè) ref
- toRefs 和對(duì)象 Object 兩者保持引用關(guān)系,即一個(gè)改完另外一個(gè)也跟著改。
2)舉個(gè)例子🌰
假設(shè)我們要將一個(gè)響應(yīng)式對(duì)象里面的所有元素取出來,那么我們可以這么處理。代碼如下:
<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>此時(shí)瀏覽器的顯示結(jié)果如下:
但是這樣子好像有點(diǎn)略顯麻煩,因?yàn)樵谀0寰幾g的時(shí)候一直要 state. ,這樣如果遇到要取很多個(gè)屬性的時(shí)候就有點(diǎn)臃腫了。
既然太臃腫了,那我們換一種思路,把 state 進(jìn)行解構(gòu)。代碼如下:
<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>此時(shí)瀏覽器的顯示結(jié)果如下:
效果是一樣的,看起來清晰了很多。但是呢……天上總不會(huì)有無緣無故的餡餅出現(xiàn),得到一些好處的同時(shí)總要失去些原本擁有的東西。
對(duì)于解構(gòu)后的對(duì)象來說,如果直接解構(gòu) reactive ,那么解構(gòu)出來的對(duì)象會(huì)直接失去響應(yīng)式。我們用一個(gè)定時(shí)器來檢驗(yàn)下效果,具體代碼如下:
<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>此時(shí)瀏覽器的顯示結(jié)果如下:
我們等了好幾秒之后,發(fā)現(xiàn) age 遲遲不變成25,所以當(dāng)我們解構(gòu) reactive 的對(duì)象時(shí),響應(yīng)式將會(huì)直接失去。
所以,就來到了我們的 toRefs 。 toRefs 在把響應(yīng)式對(duì)象轉(zhuǎn)變?yōu)槠胀▽?duì)象后,不會(huì)丟失掉響應(yīng)式的功能。具體我們用代碼來演示一下:
<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) // 將響應(yīng)式對(duì)象,變成普通對(duì)象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>此時(shí)我們觀察瀏覽器的顯示效果:
大家可以看到,用了 toRefs ,普通對(duì)象的值成功被取出來了,并且還不會(huì)丟失響應(yīng)式的功能,該改變的值一個(gè)也不少。
(4)合成函數(shù)返回響應(yīng)式對(duì)象
了解了上面三種類型的使用,我們?cè)賮砜匆环N場(chǎng)景:合成函數(shù)如何返回響應(yīng)式對(duì)象。下面附上代碼:
function useFeatureX(){ const state = reactive({ x: 1, y: 2 }) //邏輯運(yùn)行狀態(tài),…… //返回時(shí)轉(zhuǎn)換為ref return toRefs(state) } export default{ setup(){ //可以在不失去響應(yīng)性的情況下破壞結(jié)構(gòu) const {x, y} = useFeatureX() return{ x, y } }}在第一段代碼中,我們定義了一個(gè)函數(shù),并且用 toRefs 將 state 對(duì)象進(jìn)行返回,之后在組件里面直接調(diào)用響應(yīng)式對(duì)象。
通過這樣方式,讓代碼邏輯變得更加清晰明了,復(fù)用性更強(qiáng)。
2、最佳使用方式
通過上面的演示可以得出以下幾點(diǎn)結(jié)論:
- 用 reactive 做對(duì)象的響應(yīng)式,用 ref 做值類型的響應(yīng)式。
- setup 中返回 toRefs(state) ,或者 toRef(state, 'xxx') 。
- 為了防止誤會(huì)產(chǎn)生, ref 的變量命名盡量都用 xxxRef ,這樣在使用的時(shí)候會(huì)更清楚明了。
- 合成函數(shù)返回響應(yīng)式對(duì)象時(shí),使用 toRefs
3、深入理解
講完 ref 、 toRef 和 toRefs ,我們?cè)賮硭伎家粋€(gè)問題:為什么一定要用它們呢?可以不用嗎?
(1)為什么需要用ref
- 值類型(即基本數(shù)據(jù)類型)無處不在,如果不用 ref 而直接返回值類型,會(huì)丟失響應(yīng)式。
- 比如在 setup 、 computed 、合成函數(shù)等各種場(chǎng)景中,都有可能返回值類型。
- Vue 如果不定義 ref ,用戶將自己制造 ref ,這樣反而會(huì)更加混亂。
(2)為何ref需要.value屬性
通過上面的分析我們知道, ref 需要通過 .value 來修改值。這看起來是一個(gè)很麻煩的操作,總是頻繁的 .value 感覺特別瑣碎。那為什么一定要 .value 呢?我們來揭開它的面紗。
- ref 是一個(gè)對(duì)象,這個(gè)對(duì)象不丟失響應(yīng)式,且這個(gè)對(duì)象用 value 來存儲(chǔ)值。
- 因此,通過 .value 屬性的 get 和 set 來實(shí)現(xiàn)響應(yīng)式。
- 只有當(dāng)用于 模板 和 reactive 時(shí),不需要 .value 來實(shí)現(xiàn)響應(yīng)式,而其他情況則都需要。
(3)為什么需要toRef和toRefs
與 ref 不一樣的是, toRef 和 toRefs 這兩個(gè)兄弟,它們不創(chuàng)造響應(yīng)式,而是延續(xù)響應(yīng)式。創(chuàng)造響應(yīng)式一般由 ref 或者 reactive 來解決,而 toRef 和 toRefs 則是把對(duì)象的數(shù)據(jù)進(jìn)行分解和擴(kuò)散,其這個(gè)對(duì)象針對(duì)的是響應(yīng)式對(duì)象而非普通對(duì)象。總結(jié)起來有以下三點(diǎn):
- 初衷: 在不丟失響應(yīng)式的情況下,把對(duì)象數(shù)據(jù)進(jìn)行 分解或擴(kuò)散。
- 前提: 針對(duì)的是響應(yīng)式對(duì)象( reactrive 封裝的)而非普通對(duì)象。
- 注意: 不創(chuàng)造響應(yīng)式,而是延續(xù)響應(yīng)式。
二、🙆?♀?Composition API實(shí)現(xiàn)邏輯復(fù)用
1、規(guī)則
先來了解幾條規(guī)則:
- Composition API 指抽離邏輯代碼到一個(gè)函數(shù);
- 函數(shù)的命名約定為 useXxxx 格式(React hooks也是);
- 在 setup 中引用 useXxx 函數(shù)。
2、舉個(gè)例子🌰
引用一個(gè)非常經(jīng)典的例子:獲取鼠標(biāo)的定位。接下來我們用Composition API來進(jìn)行封裝演示:
定義一個(gè) 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} }再定義一個(gè) .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>此時(shí)瀏覽器的顯示效果如下:
了解完 ref 后,我們來實(shí)現(xiàn)這個(gè)功能看起來會(huì)清晰很多。我們先通過 ref 對(duì) x 和 y 做響應(yīng)式操作,之后通過 .value 來修改值,最終達(dá)到時(shí)刻獲取鼠標(biāo)定位的效果。同時(shí),如果我們時(shí)刻保持著鼠標(biāo)移動(dòng)時(shí)不斷改變值,這樣子是非常耗費(fèi)性能的。所以,我們可以通過一個(gè)按鈕,來隨時(shí)控制它的出現(xiàn)與隱藏。
大家可以發(fā)現(xiàn),當(dāng)隱藏的時(shí)候,隨后會(huì)觸發(fā) onUnmounted 生命周期,組件內(nèi)容隨之被銷毀。也就是說,使用的時(shí)候調(diào)用,不使用的時(shí)候及時(shí)銷毀,這樣子可以很大程度上提升性能。
三、🙅?♀?結(jié)束語
通過上文的學(xué)習(xí),我們可以知道, ref 、 toRef 和 toRefs 是 vue3 中 Composition API 的新特性,且 vue3 一般通過 ref 、 toRef 和 toRefs 來實(shí)現(xiàn)數(shù)據(jù)響應(yīng)式。有了這三個(gè)內(nèi)容,實(shí)現(xiàn)數(shù)據(jù)響應(yīng)式看起來方便許多,而不再像 vue2 中那種處理起來很困難。
到這里,關(guān)于 ref 、 toRef 和 toRefs 的內(nèi)容就講完啦!希望對(duì)大家有幫助!
如有疑問或文章有誤歡迎評(píng)論區(qū)瀏覽或私信我交流~
- 關(guān)注公眾號(hào) 星期一研究室 ,第一時(shí)間關(guān)注學(xué)習(xí)干貨,更多有趣的專欄待你解鎖~
- 如果這篇文章對(duì)你有用,記得 一鍵三連 再走哦!
- 我們下期見!🥂🥂🥂
總結(jié)
以上是生活随笔為你收集整理的敲黑板!vue3重点!一文了解Composition API新特性:ref、toRef、toRefs的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 东方空间“引力-1 号”计划 12 月首
- 下一篇: 卷不动也得继续学!紧跟vue3的步伐,再