敲黑板!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 的內容就講完啦!但願對你們有幫助!

若有疑問或文章有誤歡迎評論區留言或私信我交流~

  • 關注公衆號 星期一研究室 ,第一時間關注學習乾貨,更多有趣的專欄待你解鎖~
  • 若是這篇文章對你有用,記得 點個贊加個關注 再走哦!
  • 咱們下期見!🥂🥂🥂
相關文章
相關標籤/搜索