有人抵觸Ref?有人抵觸Reactive?
前言
大家好,我是林三心,用最通俗易懂的話講最難的知識點是我的座右銘,基礎(chǔ)是進階的前提是我的初心~
背景
這幾天看到好多文章標題都是類似于:
- 不用 ref 的 xx 個理由
- 不用 reactive 的 xx 個理由
- 歷數(shù) ref 的 xx 宗罪
我就很不解,到底是什么原因?qū)е掠羞@兩批人:
- 抵觸 ref 的人
- 抵觸 reactive 的人
看了這些文章,我可以總結(jié)出他們的想法
抵觸 reactive 的人
抵觸 reactive 的人,他們的想法大概就是:
- 1、Vue 官方推薦 ref
- 2、reactive 有類型限制,ref 沒有
- 3、reactive 使用不當(dāng)會丟失響應(yīng)式,比如解構(gòu)
- 4、reactive 無法修改整個對象的值
抵觸 ref 的人
抵觸 ref 的人,他們的想法大概就是:
- 1、ref 的底層其實就是 reactive,用 ref 相當(dāng)于多了一層,耗費性能
- 2、ref 的 .value 用起來很麻煩,增加使用者心里負擔(dān)
- 3、ref 到模板的時候會解掉 value 這一層,這時候也會耗費性能
把我整笑了~
說實話,看到這些文章,有點把我整笑了,其實你要用 ref 或者 reactive 都沒錯,但是沒比必要那么抵觸,編程很多時候并不是非黑即白啊。。。
既然 Vue3 推出了 ref 和 reactive,那就說明他們都有存在的必要,在項目中不同的場景去運用他們,我覺得才是最好的,而不是用一個不用另一個,不止這兩個,還有很多其他好用的 Vue3 API
我想針對這兩批人的想法做一個回應(yīng):
回應(yīng) -> 抵觸 reactive 的人
- 1、官方是推薦,不是抵觸
- 2、reactive 既然有類型限制,那就在特定時候用 reactive 就行
- 3、使用不當(dāng)會丟失響應(yīng)式?那就是開發(fā)者對于 Vue3 API 的使用還不熟
- 4、用 Object.assign 就可以修改整個對象的值
回應(yīng) -> 抵觸 ref 的人
- 1、耗費性能的話,這么久了,也沒人貼出到底耗費了多少性能?
- 2、.value 不麻煩,我覺得 .value 可以起到辨別響應(yīng)式和非響應(yīng)式數(shù)據(jù)的效果,而且現(xiàn)在編輯器都有插件提供的代碼補全了,多個 .value 也花不了多少時間吧?
靈活使用 Vue3 API 才是王道
其實在平時開發(fā)中,我覺得基本數(shù)據(jù)類型和數(shù)組,都可以用 ref 來管理,而對象的話可以使用 reactive 來管理,比如表單對象、狀態(tài)對象
其實 Vue3 不止有這兩個 API ,還有很多其他 API ,也很好用,大家只要去靈活使用它們,能讓你的Vue3 項目上一個層次
readonly
顧名思義,就是只讀的意思,如果你的數(shù)據(jù)被這個 API 包裹住的話,那么修改之后并不會觸發(fā)響應(yīng)式,并且會提示警告
圖片
圖片
readonly 的用途一般用于一些 hooks 暴露出來的變量,不想外界去修改,比如我封裝一個 hooks,這樣去做的話,那么外界只能用變量,但是不能修改變量,這樣大大保護了 hooks 內(nèi)部的邏輯~
圖片
shallowRef
shallowRef 用來包住一個基礎(chǔ)類型或者引用類型,如果是基礎(chǔ)類型那么跟 ref 基本沒區(qū)別,如果是引用類型的話,那么直接改深層屬性是不能觸發(fā)響應(yīng)式的,除非直接修改引用地址,如下:
圖片
注意:改深層屬性能改數(shù)據(jù),只是沒觸發(fā)響應(yīng)式,所以當(dāng)下一次響應(yīng)式觸發(fā)的時候,你修改的深層數(shù)據(jù)會渲染到頁面上~
shallowRef 的用處主要用于一些比較大的但又變化不大的數(shù)據(jù),比如我有一個表格數(shù)據(jù),通過接口直接獲取,并且主要用在前端展示,需要修改一些深層的屬性,但是這些屬性并不需要立即表現(xiàn)在頁面上,比如以下例子,我只需要展示 name、age 字段,至于 isOld 字段并不需要展示,我想要計算 isOld 但是又不想觸發(fā)響應(yīng)式更新,所以可以用 shallowRef 包起來,進而減少響應(yīng)式更新,優(yōu)化性能
圖片
shallowReactive
shallowReactive 用來包住一個引用類型,被包住后,修改第一層才會觸發(fā)響應(yīng)式更新,也就是淺層的屬性,修改深層的屬性并不會觸發(fā)響應(yīng)式更新
注意:改深層屬性能改數(shù)據(jù),只是沒觸發(fā)響應(yīng)式,所以當(dāng)下一次響應(yīng)式觸發(fā)的時候,你修改的深層數(shù)據(jù)會渲染到頁面上~
圖片
shallowReactive 用的比較少,shallowReactive 的用處跟 shallowRef 比較像,都是為了讓一些比較大的數(shù)據(jù)能減少響應(yīng)式更新,進而優(yōu)化性能
toRef & toRefs
先說說 toRef 吧,我們平時在使用 reactive 的時候會有一個苦惱,那就是解構(gòu),比如看以下例子,我們?yōu)榱松傩┮恍┐a,解構(gòu)出來了 name 并放到模板里渲染,但是當(dāng)我們想改原數(shù)據(jù)的時候,發(fā)現(xiàn) name 并不會更新,這就是解構(gòu)出來基礎(chǔ)類型的苦惱
圖片
圖片
這時我們可以使用 toRef,這個時候我們直接修改 name 也會觸發(fā)原數(shù)據(jù)的修改,修改原數(shù)據(jù)也會觸發(fā) name 的修改
圖片
但是如果是屬性太多了,我們想一個一個去用 toRef 的話會寫很多代碼
圖片
所以我們可以使用 toRefs 一次性解構(gòu)
圖片
toRaw & markRaw & unref
toRaw 可以把一個響應(yīng)式 reactive 轉(zhuǎn)成普通對象,也就是把響應(yīng)式對象轉(zhuǎn)成非響應(yīng)式對象
圖片
toRaw 主要用在回調(diào)傳參中,比如我封裝一個 hooks,我想要把 hooks 內(nèi)維護的響應(yīng)式變量轉(zhuǎn)成普通數(shù)據(jù),當(dāng)做參數(shù)傳給回調(diào)函數(shù),可以用 toRaw
圖片
markRaw 可以用來標記響應(yīng)式對象里的某個屬性不被追蹤,如果你的響應(yīng)式對象里有某個屬性數(shù)據(jù)量比較大,但又不想被追蹤,你可以使用 markRaw
圖片
unref 相當(dāng)于返回 ref 的 value
圖片
effectScope & onScopeDispose
effectScope 可以有兩個作用:
- 收集副作用
- 全局狀態(tài)管理
收集副作用
比如我們封裝一個共用的 hooks,為了減少頁面隱患,肯定會統(tǒng)一收集副作用,并且在組件銷毀的時候去統(tǒng)一消除,比如以下代碼:
圖片
但是這么收集很麻煩, effectScope 能幫我們做到統(tǒng)一收集,并且通過 stop 方法來進行清除,且 stop 執(zhí)行的時候會觸發(fā) effectScope 內(nèi)部的 onScopeDispose
圖片
我們可以利用 effectScope & onScopeDispose 來做一些性能優(yōu)化,比如下面這個例子,我們封裝一個鼠標監(jiān)聽的 hooks
圖片
但是如果在頁面里調(diào)用多次的話,那么勢必會往 window 身上監(jiān)聽很多多余的事件,造成性能負擔(dān),所以解決方案就是,無論頁面里調(diào)用再多次 useMouse,我們只往 window 身上加一個鼠標監(jiān)聽事件
圖片
全局狀態(tài)管理
現(xiàn)在 Vue3 最火的全局狀態(tài)管理工具肯定是 Pinia 了,那么你們知道 Pinia 的原理是什么嗎?原理就是依賴了 effectScope
圖片
所以我們完全可以自己使用 effectScope 來實現(xiàn)自己的局部狀態(tài)管理,比如我們封裝一個通用組件,這個組件層級比較多,并且需要共享一些數(shù)據(jù),那么這個時候肯定不會用 Pinia 這種全局狀態(tài)管理,而是會自己寫一個局部的狀態(tài)管理,這個時候 effectScope 就可以排上用場了
vueuse 中的 createGlobalState 就是為了這個而生
圖片
圖片
provide & inject
Vue3 用來提供注入的 API,主要是用在組件的封裝,比如那種層級較多的組件,且子組件需要依賴父組件甚至爺爺組件的數(shù)據(jù),那么可以使用 provide & inject,最典型的例子就是 Form 表單組件,可以去看看各個組件庫的源碼,表單組件大部分都是用 provide & inject 來實現(xiàn)的,比如 Form、Form-Item、Input這三個需要互相依賴對方的規(guī)則、字段名、字段值,所以用 provide & inject 會更好。具體用法看文檔吧~https://cn.vuejs.org/guide/components/provide-inject.html
圖片