當前位置: 妍妍網 > 碼農

看尤雨溪說:為什麽Vue3 中應該使用 Ref 而不是 Reactive?

2024-02-22碼農

前言


大家好,我是林三心,用最通俗易懂的話講最難的知識點是我的座右銘,基礎是進階的前提是我的初心~

每次有同學學習到 vue3 的時候,總會問我:「 Sunday 老師, ref reactive 我們應該用哪個呢?」 我告訴他:「我們應該使用 ref ,而不是 reactive 」。那麽此時同學就會有疑惑:「為什麽呢? ref 還需要 .value 處理, reactive 看起來會更加簡單呢?」

嗯....每當這個時候,我都需要進行一次長篇大論來解釋這個問題。不過以後應該不需要了,因為這篇文章將會把這個事情解釋的非常清楚......

為什麽推薦使用ref而不是reactive

reactive 在使用過程中存在一些局限性,如果不額外註意這些問題,可能會給開發帶來一些不便。與此不同, ref 更像是Vue2時代的 option API 中的 data 的替代品,可以存放任何數據型別,而 reactive 聲明的數據型別則僅限於物件。

總體來說,非必要的情況下最好避免使用 reactive 。官方文件也強烈推薦使用 ref() 作為聲明響應式狀態的主要API。以下是詳細原因:

  1. 局限性問題: reactive 本身存在一些局限性,可能會在開發過程中引發一些問題。這需要額外的註意力和處理,否則可能對開發造成麻煩。

  2. 數據型別限制: reactive 聲明的數據型別僅限於物件,而 ref 則更加靈活,可以容納任何數據型別。這使得 ref 更適合一般的響應式狀態的聲明。

  3. 官方推薦: 官方文件強烈建議使用 ref() 作為聲明響應式狀態的首選。這是因為 ref 更簡單、更直觀,同時避免了 reactive 可能引發的一些問題。

總的來說:除非有特定的需求需要使用 reactive ,否則在大多數情況下更推薦使用 ref()

最懂Vue的人都這麽說了:推薦ref!!!!!!

reactive ref 對比

reactive ref
❌ 只支持物件和陣列(參照數據型別) ✅ 支持基本數據型別 + 參照數據型別
✅ 在 <script> <template> 中無差別使用 ❌ 在 <script> <template> 使用方式不同(在 <script> 中要使用 .value
❌ 重新分配一個新物件會遺失響應性 ✅ 重新分配一個新物件 不會 失去響應
能直接存取內容 需要使用 .value 存取內容
❌ 將物件傳入函式時,失去響應 ✅ 傳入函式時,不會失去響應
❌ 解構時會遺失響應性,需使用 toRefs ❌ 解構物件時會遺失響應性,需使用 toRefs

即:

  • ref 用於將基本型別的數據和參照數據型別(物件)轉換為響應式數據,透過 .value 存取和修改。

  • reactive 用於將物件轉換為響應式數據,可以直接存取和修改內容,適用於復雜的巢狀物件和陣列。

  • 01: reactive 有限的值型別

    reactive 只能聲明參照數據型別(物件)

    let obj = reactive({
    name'小明',
    age18
    })

    ref 既能聲明基本數據型別,也能聲明物件和陣列

    Vue 提供了 ref() 方法,允許我們建立可以使用 任何值型別 的響應式 ref

    // 物件
    const state = ref({})
    // 陣列
    const state2 = ref([])

    使用 ref ,你可以靈活地聲明基本數據型別、物件或陣列,而不受像 reactive 那樣只能處理參照數據型別的限制。這為開發提供了更大的靈活性,尤其是在處理不同型別的數據時。

    02: reactive 使用不當會失去響應

    使用 reactive 時,如果不當使用,可能導致響應性失效,帶來一些困擾。這可能讓開發者在愉快編碼的同時,突然發現某些操作失去了響應性,不明所以。因此,建議在不了解 reactive 失去響應的情況下慎用,而更推薦使用 ref

    1. 賦值給 reactive 一個整個物件或 reactive 物件

    賦值一個普通物件

    let state = reactive({ count0 })
    // 這個賦值將導致 state 失去響應
    state = { count1 }

    賦值一個 reactive 物件

    <template>
    {{ state }}
    </template>
    <scriptsetup>
    const state = reactive({ count0 })
    // 在 nextTick 異步方法中修改 state 的值
    nextTick(() => {
    // 並不會觸發修改 DOM ,說明失去響應了
    state = reactive({ count11 });
    });
    </script>

    nextTick 中給 state 賦值一個 reactive 的響應式物件,但是 DOM 並沒有更新。

    解決方法:

    1. 不要直接整個物件替換,一個個內容賦值

    let state = reactive({ count0 })
    // state = { count: 1 }
    state.count = 1

    1. 使用 Object.assign

    let state = reactive({ count0 })
    // state = { count: 1 },state 不會失去響應
    state = Object.assign(state, { count1 })

    1. 使用 ref 定義物件

    let state = ref({ count0 })
    state.value = { count1 }

    2. 將 reactive 物件的內容賦值給變量(斷開連線/深拷貝)

    這種操作類似於深拷貝,不再共享同一記憶體地址,而是只是字面量的賦值,對該變量的賦值不會影響原來物件的內容值。

    let state = reactive({ count0 })
    // 賦值給 n,n 和 state.count 不再共享響應性連線
    let n = state.count
    // 不影響原始的 state
    n++
    console.log(state.count) // 0

    解決方案:

  • 避免將 reactive 物件的內容賦值給變量。

  • 3. 直接 reactive 物件解構時

    直接解構會失去響應。

    let state = reactive({ count0 })
    // 普通解構,count 和 state.count 失去了響應性連線
    let { count } = state
    count++ // state.count 值依舊是 0

    解決方案:

    使用 toRefs 解構,解構後的內容是 ref 的響應式變量。

    const state = reactive({ count0 })
    // 使用 toRefs 解構,後的內容為 ref 的響應式變量
    let { count } = toRefs(state)
    count.value++ // state.count 值改變為 1

    建議: ref 一把梭

    推薦使用 ref ,總結原因如下:

    1. reactive 有限的值型別:只能聲明參照數據型別(物件/陣列)。

    2. reactive 在一些情況下會失去響應,這可能導致數據回顯失去響應(數據改了,DOM 沒更新)。

      <template>
      {{ state.a }}
      {{ state.b }}
      {{ state.c }}
      </template>
      <script>
      let state = reactive({ a1b2c3 })
      onMounted(() => {
      // 透過 AJAX 請求獲取的數據,回顯到 reactive,如果處理不好將導致變量失去響應
      // 回顯失敗,給響應式數據賦值一個普通物件
      state = { a11b22c333 }
      // 回顯成功,一個個內容賦值
      state.a = 11
      state.b = 22
      state.c = 33
      })
      </script>

      上面這個例子如果是使用 ref 進行聲明,直接賦值即可,不需要將內容拆分一個個賦值。

      使用 ref 替代 reactive

      <template>
      {{ state.a }}
      {{ state.b }}
      {{ state.c }}
      </template>
      <script>
      let state = ref({ a1b2c3 })
      onMounted(() => {
      // 回顯成功
      state.value = { a11b22c333 }
      })
      </script>

  • 給響應式物件的字面量賦一整個普通物件或 reactive 物件將導致 reactive 聲明的響應式數據失去響應。

  • ref 適用範圍更廣,可聲明基本數據型別和參照數據型別。

  • 雖然使用 ref 聲明的變量在讀取和修改時都需要加 .value 小尾巴,但正因為有這個小尾巴,我們在 review 程式碼的時候就很清楚知道這是一個 ref 聲明的響應式數據。

    ref .value 好麻煩!

    ref 聲明的響應式變量攜帶迷人的 .value 小尾巴,讓我們一眼就能確定它是一個響應式變量。雖然使用 ref 聲明的變量在讀取和修改時都需要加 .value 小尾巴,但是正因為有這個小尾巴,我們在 review 程式碼的時候就很清楚知道這是一個 ref 聲明的響應式數據。

    可能有些人不喜歡這個迷人小尾巴,如果我能自動補全,閣下又如何應對?

    Volar 外掛程式能自動補全 .value (強烈推薦!!!!!)

    推薦 ref 一把梭,但是 ref 又得到處 .value ,那就交給外掛程式來完成吧!

  • Volar 自動補全 .value (不是預設開啟,需要手動開啟)

  • reactive 重新賦值遺失響應是因為參照地址變了,被 proxy 代理的物件已經不是原來的那個,所以遺失響應了。其實 ref 也是一樣的,當把 .value 那一層替換成另外一個有著 .value 的物件也會遺失響應。 ref 定義的內容等價於 reactive({ value: xxx })

    另外,說使用 Object.assign 為什麽可以更新樣版:

    Object.assign 解釋是這樣的:如果目標物件與源物件具有相同的鍵(內容名),則目標物件中的內容將被源物件中的內容覆蓋,後面的源物件的內容將類似地覆蓋前面的源物件的同名內容。

    那個解決方法裏不用重新賦值,直接 Object.assign(state, { count: 1 }) 即可,所以只要 proxy 代理的參照地址沒變,就會一直存在響應性

    結語

    我是林三心

  • 一個待過 小型toG型外包公司、大型外包公司、小公司、潛力型創業公司、大公司 的作死型前端選手;

  • 一個偏前端的全幹工程師;

  • 一個不正經的金塊作者;

  • 逗比的B站up主;

  • 不帥的小紅書博主;

  • 喜歡打鐵的籃球菜鳥;

  • 喜歡歷史的乏味少年;

  • 喜歡rap的五音不全弱雞如果你想一起學習前端,一起摸魚,一起研究簡歷最佳化,一起研究面試進步,一起交流歷史音樂籃球rap,可以來俺的摸魚學習群哈哈,點這個,有7000多名前端小夥伴在等著一起學習哦 -->

  • 廣州的兄弟可以約飯哦,或者約球~我負責打鐵,你負責進球,謝謝~