敲黑板!vue3重点!一文了解Composition API新特性:ref、toRef、toRefs
一文了解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帶來的新特性: ref 、 toRef 和 toRefs 。
下面開始進入本文的講解?
一、🙎如何理解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 不僅可以實現響應式,還可以用于模板的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 ,之后在生命周期中調用,最后瀏覽器顯示出該 DOM 元素。所以說, ref 也可以用來渲染模板中的DOM元素。
(2)toRef是什么
1)toRef是什么
-
toRef 可以響應對象 Object ,其針對的是某一個響應式對象( reactive 封裝)的屬性prop 。
-
toRef 和對象 Object 兩者保持引用關系,即一個改完另外一個也跟著改。
-
toRef 如果用于普通對象(非響應式對象),產出的結果不具備響應式。如下代碼所示:
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>此時我們來看下瀏覽器的顯示效果:
我們通過 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>此時瀏覽器的顯示結果如下:
但是這樣子好像有點略顯麻煩,因為在模板編譯的時候一直要 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>此時瀏覽器的顯示結果如下:
效果是一樣的,看起來清晰了很多。但是呢……天上總不會有無緣無故的餡餅出現,得到一些好處的同時總要失去些原本擁有的東西。
對于解構后的對象來說,如果直接解構 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>此時瀏覽器的顯示結果如下:
我們等了好幾秒之后,發現 age 遲遲不變成25,所以當我們解構 reactive 的對象時,響應式將會直接失去。
所以,就來到了我們的 toRefs 。 toRefs 在把響應式對象轉變為普通對象后,不會丟失掉響應式的功能。具體我們用代碼來演示一下:
<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 ,普通對象的值成功被取出來了,并且還不會丟失響應式的功能,該改變的值一個也不少。
(4)合成函數返回響應式對象
了解了上面三種類型的使用,我們再來看一種場景:合成函數如何返回響應式對象。下面附上代碼:
function useFeatureX(){ const state = reactive({ x: 1, y: 2 }) //邏輯運行狀態,…… //返回時轉換為ref return toRefs(state) } export default{ setup(){ //可以在不失去響應性的情況下破壞結構 const {x, y} = useFeatureX() return{ x, y } }}在第一段代碼中,我們定義了一個函數,并且用 toRefs 將 state 對象進行返回,之后在組件里面直接調用響應式對象。
通過這樣方式,讓代碼邏輯變得更加清晰明了,復用性更強。
2、最佳使用方式
通過上面的演示可以得出以下幾點結論:
- 用 reactive 做對象的響應式,用 ref 做值類型的響應式。
- setup 中返回 toRefs(state) ,或者 toRef(state, 'xxx') 。
- 為了防止誤會產生, ref 的變量命名盡量都用 xxxRef ,這樣在使用的時候會更清楚明了。
- 合成函數返回響應式對象時,使用 toRefs
3、深入理解
講完 ref 、 toRef 和 toRefs ,我們再來思考一個問題:為什么一定要用它們呢?可以不用嗎?
(1)為什么需要用ref
- 值類型(即基本數據類型)無處不在,如果不用 ref 而直接返回值類型,會丟失響應式。
- 比如在 setup 、 computed 、合成函數等各種場景中,都有可能返回值類型。
- Vue 如果不定義 ref ,用戶將自己制造 ref ,這樣反而會更加混亂。
(2)為何ref需要.value屬性
通過上面的分析我們知道, ref 需要通過 .value 來修改值。這看起來是一個很麻煩的操作,總是頻繁的 .value 感覺特別瑣碎。那為什么一定要 .value 呢?我們來揭開它的面紗。
- ref 是一個對象,這個對象不丟失響應式,且這個對象用 value 來存儲值。
- 因此,通過 .value 屬性的 get 和 set 來實現響應式。
- 只有當用于 模板 和 reactive 時,不需要 .value 來實現響應式,而其他情況則都需要。
(3)為什么需要toRef和toRefs
與 ref 不一樣的是, toRef 和 toRefs 這兩個兄弟,它們不創造響應式,而是延續響應式。創造響應式一般由 ref 或者 reactive 來解決,而 toRef 和 toRefs 則是把對象的數據進行分解和擴散,其這個對象針對的是響應式對象而非普通對象。總結起來有以下三點:
- 初衷: 在不丟失響應式的情況下,把對象數據進行 分解或擴散。
- 前提: 針對的是響應式對象( 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>此時瀏覽器的顯示效果如下:
了解完 ref 后,我們來實現這個功能看起來會清晰很多。我們先通過 ref 對 x 和 y 做響應式操作,之后通過 .value 來修改值,最終達到時刻獲取鼠標定位的效果。同時,如果我們時刻保持著鼠標移動時不斷改變值,這樣子是非常耗費性能的。所以,我們可以通過一個按鈕,來隨時控制它的出現與隱藏。
大家可以發現,當隱藏的時候,隨后會觸發 onUnmounted 生命周期,組件內容隨之被銷毀。也就是說,使用的時候調用,不使用的時候及時銷毀,這樣子可以很大程度上提升性能。
三、🙅?♀?結束語
通過上文的學習,我們可以知道, ref 、 toRef 和 toRefs 是 vue3 中 Composition API 的新特性,且 vue3 一般通過 ref 、 toRef 和 toRefs 來實現數據響應式。有了這三個內容,實現數據響應式看起來方便許多,而不再像 vue2 中那種處理起來很困難。
到這里,關于 ref 、 toRef 和 toRefs 的內容就講完啦!希望對大家有幫助!
如有疑問或文章有誤歡迎評論區瀏覽或私信我交流~
- 關注公眾號 星期一研究室 ,第一時間關注學習干貨,更多有趣的專欄待你解鎖~
- 如果這篇文章對你有用,記得 一鍵三連 再走哦!
- 我們下期見!🥂🥂🥂
總結
以上是生活随笔為你收集整理的敲黑板!vue3重点!一文了解Composition API新特性:ref、toRef、toRefs的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 东方空间“引力-1 号”计划 12 月首
- 下一篇: 卷不动也得继续学!紧跟vue3的步伐,再