ref再複習

寫了一陣子 VUE,也用了一陣子的 ref(),結果看了 Alex 的直播才發現自己和 ref() 有點陌生,回頭去好好讀了 VUE3 的官方文件,整理了一下筆記,方便回頭複習。

ref 重點整理

  • ref() 會將傳入參數的值轉換為帶有 .value 屬性的 ref 物件
  • 如果 ref()放入的是物件,則 .value 屬性的值是proxy物件
  • ref() 是 refimpl 的物件
  • ref() 的.value 是響應式的,當值為物件時,會用 reactive() 轉換 .value
  • ref() 的參數如果放入物件型別,可以響應式的替換整個物件
  • ref() 被傳遞給函式或是解構時,不會失去響應性
  • ref() 讓我們能夠創造對任意值得引用 (reference),並在不失去響應性的前提下傳遞這些引用
  • ref() 在 template渲染 上下文頂層屬性時才會自動解包
  • 當一個 ref 被放入reactive() 物件中,作為屬性被訪問或更改時,會自動解包
  • 如果將一個新的 ref() 賦值給一個已經有 ref 的屬性,那它會替換舊的 ref

ref() 會將傳入參數的值轉換為帶有 .value 屬性的 ref 物件

在 VUE 裡面要使用 ref(),作法是在 () 括號中放入 基本型別的字串、數字或是布林值,也可以放入陣列或是物件。

ref()的樣子看起來就是一個函式,() 括號中放入的就是參數,參數會變成帶有 .value 的 ref 物件。

參數可以傳入基本型別的字串、數字或是布林值,也可以放入陣列或是物件。

以下面 VUE 官網的例子來說,console.log(count) 得出一個 { value: 0 } 的物件,因此要取得 count 的值必須使用 count.value

const count = ref(0)

console.log(count) // { value: 0 }
console.log(count.value) // 0

count.value++
console.log(count.value) // 1

ref() 的.value 是響應式的

ref() 的.value 是響應式的,當值為物件時,會用 reactive() 轉換為 .value,所以裡面運作的是proxy 機制,但如果是基本型別的話則否。

在 SFC 的 <script> 區塊要透過 .value 取值,而在<template> 模板中取用 ref() 不需要加上 .value。

ref() 的參數如果放入物件型別,可以響應式的替換整個物件

<script setup>
import { ref } from 'vue'

const objectRef = ref({ count: 0 })

// 物件替換後,仍為響應式
objectRef.value = { count: 1 }

</script>

<template>
  <h1>{{ msg }}</h1>
  {{ objectRef.count }} //1
  {{ objectRef.count+1 }} //2
</template>

ref() 被傳遞給函式或是解構時,不會失去響應性

<script setup>
import { ref } from 'vue'
const obj = {
  foo: ref(1),
  bar: ref(2)
}

// add 函式接收一個 ref
// 需要透過 value 取值
// 但是會維持響應性
function add(v){
  v.value++;
}
// 仍然是响应式的
const { foo, bar } = obj
</script>

<template>
  <h1>{{ msg }}</h1>
  {{ foo }} //1
  <button @click="add(obj.foo)"> 加加 {{foo}}</button>
</template>

ref() 讓我們能夠創造對任意值的參考 (reference),並在不失去響應性的前提下傳遞這些參考。

甚麼是「解包」

「解包」這兩個字其實就是英文的 「unwrap」,指的是當在 <template> 中引用 ref 的頂層屬性不需要使用 .value。

<script setup>
import { ref } from 'vue'

const count = ref(0)

function increment() {
  count.value++
}
</script>

<template>
  <button @click="increment">
    {{ count }} <!-- 模板中引用不需要使用 .value -->
  </button>
</template>

頂層屬性的 ref 才能自動解包

也就是如果是在物件的屬性中包入 ref() 雖然能傳遞計算後的最終值,但是如果在模板中做處理效果會不如預期。例如下面的範例,{{ object.foo + 1 }} 會變成 [object Object]1

但是如果是在函式中做運算,則可以正常運作。

<script setup>
import { ref } from 'vue'

const count = ref(0)
const object = { foo: ref(1) }


const { foo } = object
function increment() {
  foo.value++  
}
 function add(){
   object.foo.value++
 }
  console.log(object.foo === foo)
  // true
</script>

<template>
   <br>
   <button @click="add">
    object.foo = {{ object.foo }} <!-- 无需 .value -->
  </button>
 {{object.foo.value}}
  <br>
  object.foo + 1 =
  {{ object.foo + 1 }}
  <!-- [object Object]1 -->
  <br>
  foo =
  {{ foo }}
  <!-- 1 -->
  <br>
   <button @click="increment">
    foo = {{ foo }} <!-- 无需 .value -->
  </button>
</template>

由上面的範例也可以看到,當 ref()作為物件的屬性時,即使經過解構,判斷 object.foo === foo 仍然為 true,指向相同的參考。

ref() 在 reactive() 作為屬性解包

当一个 ref 被嵌套在一个响应式对象中,作为属性被访问或更改时,它会自动解包,因此会表现得和一般的属性一样:

當 ref() 作為一個 reactive()物件的屬性被操作時,它會自動解包,表現和一般屬性一樣:

const count = ref(0)
const state = reactive({
  count
})

console.log(state.count) // 0

state.count = 1
console.log(count.value) // 1

一個已經是 ref() 的常數可以被新的 ref() 賦值

俗話說:「只見新人笑,不見舊人哭」,如果一個常數已經被某個 ref()賦值,仍然可以拿一個新的 ref() 賦值給它,這時這個常數就會指向新的 ref()。

const otherCount = ref(2)

state.count = otherCount
console.log(state.count) // 2
// 原始 ref 现在已经和 state.count 失去联系
console.log(count.value) // 1

ref再複習
https://popeye-ux.github.io/2023/06/17/vue-reviewREF/
作者
POPEYE
發布於
2023年6月17日
許可協議