首發!vue3重點!一文了解Composition API新特性:ref、toRef、toRefs

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

上一篇文章中,我們初步了解了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 = 20
            nameRef.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.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  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 的內容就講完啦!希望對大家有幫助!

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

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容