這是我參與更文挑戰的第23天,活動詳情查看:更文挑戰javascript
在上一篇文章中,咱們初步瞭解了vue3的新特性,今天,咱們將延續Composition API的話題,來了解Composition API帶來的新特性: ref
、 toRef
和 toRefs
。html
下面開始進入本文的講解✨vue
ref
能夠生成 值類型
(即基本數據類型) 的響應式數據;ref
能夠用於模板和reactive;ref
經過 .value
來修改值(必定要記得加上 .value
);ref
不只能夠用於響應式,還能夠用於模板的 DOM
元素。假設咱們定義了兩個值類型的數據,並經過一個定時器來看它響應式先後的效果。接下來咱們用代碼來演示一下: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
,能夠實現值類型的數據響應式。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
,以後在生命週期中調用,最後瀏覽器顯示出該 DOM
元素。因此說, ref
也能夠用來渲染模板中的DOM元素。app
toRef
能夠響應對象 Object
,其針對的是某一個響應式對象( reactive
封裝)的屬性prop
。函數
toRef
和對象 Object
二者保持引用關係,即一個改完另一個也跟着改。
toRef
若是用於普通對象(非響應式對象),產出的結果不具有響應式。以下代碼所示:
//普通對象
const state = {
age: 20,
name: 'monday'
}
//響應式對象
const state = reactive({
age: 20,
name: 'monday'
})
複製代碼
對於一個普通對象來講,若是這個普通對象要實現響應式,就用 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)
的形式來傳對象名和具體的屬性名,達到某個屬性數據響應式的效果。
toRef
不同的是, toRefs
是針對整個對象的全部屬性,目標在於將響應式對象( reactive
封裝)轉換爲普通對象prop
都對應一個 ref
toRefs
和對象 Object
二者保持引用關係,即一個改完另一個也跟着改。假設咱們要將一個響應式對象裏面的全部元素取出來,那麼咱們能夠這麼處理。代碼以下:
<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
,普通對象的值成功被取出來了,而且還不會丟失響應式的功能,該改變的值一個也很多。
瞭解了上面三種類型的使用,咱們再來看一種場景:合成函數如何返回響應式對象。下面附上代碼:
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
對象進行返回,以後在組件裏面直接調用響應式對象。
經過這樣方式,讓代碼邏輯變得更加清晰明瞭,複用性更強。
經過上面的演示能夠得出如下幾點結論:
reactive
作對象的響應式,用 ref
作值類型的響應式。setup
中返回 toRefs(state)
,或者 toRef(state, 'xxx')
。ref
的變量命名儘可能都用 xxxRef
,這樣在使用的時候會更清楚明瞭。toRefs
講完 ref
、 toRef
和 toRefs
,咱們再來思考一個問題:爲何必定要用它們呢?能夠不用嗎?
ref
而直接返回值類型,會丟失響應式。setup
、 computed
、合成函數等各類場景中,都有可能返回值類型。Vue
若是不定義 ref
,用戶將本身製造 ref
,這樣反而會更加混亂。經過上面的分析咱們知道, ref
須要經過 .value
來修改值。這看起來是一個很麻煩的操做,老是頻繁的 .value
感受特別瑣碎。那爲何必定要 .value
呢?咱們來揭開它的面紗。
ref
是一個對象,這個對象不丟失響應式,且這個對象用 value
來存儲值。.value
屬性的 get
和 set
來實現響應式。.value
來實現響應式,而其餘狀況則都須要。與 ref
不同的是, toRef
和 toRefs
這兩個兄弟,它們不創造響應式,而是延續響應式。創造響應式通常由 ref
或者 reactive
來解決,而 toRef
和 toRefs
則是把對象的數據進行分解和擴散,其這個對象針對的是響應式對象而非普通對象。總結起來有如下三點:
reactrive
封裝的)而非普通對象。先來了解幾條規則:
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>
複製代碼
此時瀏覽器的顯示效果以下:
瞭解完 ref
後,咱們來實現這個功能看起來會清晰不少。咱們先經過 ref
對 x
和 y
作響應式操做,以後經過 .value
來修改值,最終達到時刻獲取鼠標定位的效果。同時,若是咱們時刻保持着鼠標移動時不斷改變值,這樣子是很是耗費性能的,而且很容易致使內存泄漏。因此,咱們須要在組件進行 onUnmounted
的時候,及時去銷燬它和解綁它。
你們能夠發現,當隱藏的時候,隨後會觸發 onUnmounted
生命週期,組件內容隨之被銷燬。也就是說,使用的時候調用,不使用的時候及時銷燬,這樣子能夠很大程度上提高性能。
經過上文的學習,咱們能夠知道, ref
、 toRef
和 toRefs
是 vue3
中 Composition API
的新特性,且 vue3
通常經過 ref
、 toRef
和 toRefs
來實現數據響應式。有了這三個內容,實現數據響應式看起來方便許多,而再也不像 vue2
中那種處理起來很困難。
到這裏,關於 ref
、 toRef
和 toRefs
的內容就講完啦!但願對你們有幫助!
若有疑問或文章有誤歡迎評論區留言或私信我交流~
- 關注公衆號 星期一研究室 ,第一時間關注學習乾貨,更多有趣的專欄待你解鎖~
- 若是這篇文章對你有用,記得 點個贊加個關注 再走哦!
- 咱們下期見!🥂🥂🥂