偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Vue3響應式核心:ref vs reactive深度對比

開發(fā) 前端
Vue3 提供了兩種創(chuàng)建響應式數(shù)據(jù)的方式:??ref?? 和 ??reactive??。它們有什么區(qū)別?在開發(fā)中該如何選擇?本文將詳細講解它們的用法、適用場景,并介紹相關的輔助 API

前言

Vue3 提供了兩種創(chuàng)建響應式數(shù)據(jù)的方式:ref 和 reactive。它們有什么區(qū)別?在開發(fā)中該如何選擇?本文將詳細講解它們的用法、適用場景,并介紹相關的輔助 API,如:

  • shallowRef 和 shallowReactive(淺層響應式)
  • triggerRef(手動觸發(fā) DOM 更新)
  • customRef(自定義響應式邏輯)
  • readonly(防止數(shù)據(jù)被修改)

讀完本文,你將徹底理解 Vue3 的響應式系統(tǒng),并能在項目中正確使用這些 API!

ref

?

ref接受任意類型值,返回響應式對象,通過.value訪問

需要注意的是被ref包裝之后需要.value 來進行取值或賦值,模版除外

比如:

<template>
    <!-- 無需.value -->
    <p>{{ name }}</p>
</template>
<script setup lang="ts">
const name = ref('南玖')
// 需要.value
name.value = 'nanjiu'
</script>

接收任意值

?

ref可以接收基本類型、引用類型的數(shù)據(jù)以及DOM的ref的屬性值

const name = ref('南玖')
const obj = ref({
    name: '南玖',
    age: 20
})
console.log(name)
console.log(obj)


圖片


  • 如果ref接收的是一個基本類型的數(shù)據(jù),那么.value保存的就是就是該原始值
  • 如果ref接收的是一個引用類型的數(shù)據(jù),那么.value保存的就是代理了該引用數(shù)據(jù)的proxy對象
  • 無論是基本數(shù)據(jù)類型還是引用數(shù)據(jù)類型,最終返回的都是由 RefImpl 類構造出來的對象

響應式

ref默認提供深層響應式,也就是說即使我們修改嵌套的引用類型數(shù)據(jù),vue也能夠檢測到并觸發(fā)頁面更新

<template>
    <p>{{ num }}</p>
    <button @click="num++">num++</button>
    <p>{{ person.info.age }}</p>
    <button @click="person.info.age++">age++</button>
</template>

<script setup lang="ts">
const num = ref(1)
const person = ref({
    name: '鹿',
    info: {
        age: 20,
    }
})
</script>


圖片


也就是說無論嵌套多深,vue都能夠監(jiān)聽到數(shù)據(jù)的變化,說到監(jiān)聽數(shù)據(jù)變化,這就得提一下watch方法了,雖然vue能夠監(jiān)聽到嵌套數(shù)據(jù)的變化,但是watch函數(shù)如果監(jiān)聽的是ref定義的引用類型數(shù)據(jù),默認是不會開啟深度監(jiān)聽的

<template>
    <p>{{ person.info.age }}</p>
    <button @click="person.info.age++">age++</button>
</template>

<script setup lang="ts">

const person = ref({
    name: '鹿',
    info: {
        age: 20,
    }
})


watch(() => person.value, (newValue, oldValue) => {
    console.log('person changed from', oldValue, 'to', newValue)
})
</script>


圖片


雖然頁面視圖更新了,但是watch是無法監(jiān)聽到數(shù)據(jù)變化的,想要監(jiān)聽到這一變化,我們需要手動開啟深度監(jiān)聽

watch(() => person.value, (newValue, oldValue) => {
    console.log('person changed from', oldValue, 'to', newValue)
}, {
    deep: true // 深度監(jiān)聽
})


圖片


shallowRef

由于ref默認是深層響應式,但有時候我們?yōu)榱诵阅芸紤],也可以通過 shallowRef 來放棄深層響應性。對于淺層 ref,只有 .value 的訪問會被追蹤。

<template>
    <p>ref: {{ person.info.age }}</p>
    <button @click="person.info.age++">age++</button>
    <p>shallowRef: {{ animal.age }}</p>
    <button @click="animalAgeAdd">age++</button>
</template>

<script setup lang="ts">
const person = ref({
    name: '鹿',
    info: {
        age: 20,
    }
})
const animal = shallowRef({
    name: '小鹿',
    age: 5
})

const animalAgeAdd = () => {
    // 修改淺響應式對象的屬性
    animal.value.age++
    console.log('animal age changed to', animal.value.age)
}
</script>


圖片


修改屬性值,雖然數(shù)據(jù)變化了,但是頁面并不會更新,并且無法通過watch監(jiān)聽數(shù)據(jù)變化。

??這里還有一點需要注意的是,ref與shallowRef最好不要一起使用,否則shallowRef會被影響

比如:

const animalAgeAdd = () => {
    // 修改深響應式對象的屬性
    person.value.info.age++
    // 修改淺響應式對象的屬性
    animal.value.age++
    // 這樣會導致頁面上的animal.age 也會更新
}


圖片


triggerRef

?

強制觸發(fā)依賴于一個淺層 ref的副作用,這通常在對淺引用的內(nèi)部值進行深度變更后使用。

當一個淺層ref的屬性值發(fā)生改變又想觸發(fā)頁面更新時,可以手動調(diào)用triggerRef來實現(xiàn)

const animal = shallowRef({
    name: '小鹿',
    age: 5
})

const animalAgeAdd = () => {
    // 修改淺響應式對象的屬性
    animal.value.age++

    triggerRef(animal) // 手動觸發(fā)更新
}

customRef

?

創(chuàng)建一個自定義的 ref,顯式聲明對其依賴追蹤和更新觸發(fā)的控制方式。

customRef() 接收一個工廠函數(shù)作為參數(shù),該函數(shù)接收 track 和 trigger 兩個函數(shù)作為參數(shù),并返回一個帶有 get 和 set 方法的對象。

  • track:用于收集依賴項。在 get 方法中調(diào)用,收集該 ref 所依賴的響應式數(shù)據(jù)。
  • trigger:用于觸發(fā)更新。在 set 方法中調(diào)用,通知依賴項更新視圖。
const myRef = customRef((track, trigger) => {
    let value = 0
    return {
        get() {
            track()
            return value
        },
        set(newValue) {
            if (newValue !== value) {
                value = newValue
                trigger()
            }
        }
    }
})
console.log(myRef)

customRef允許我們通過獲取或設置一個變量的值時進行一些額外的操作,而不需要偵聽這個變量進行額外的操作。

比如,我們可以使用cusromRef實現(xiàn)一個自帶防抖的響應式數(shù)據(jù)

const useDebounceRef = (value: any, delay?: number) => {

    return customRef((track, trigger) => {
        let timer: ReturnType<typeof setTimeout>
        return {
            get() {
                track()
                return value
            },
            set(newValue) {
                clearTimeout(timer)
                timer = setTimeout(() => {
                    value = newValue
                    trigger()
                    console.log('value changed to', value)
                }, delay || 100)
            }
        }
    })
}

const inputValue = useDebounceRef('', 1000)

reactive

?

reactive用于將一個引用類型數(shù)據(jù)聲明為響應式數(shù)據(jù),返回的是一個Proxy對象。

只接受引用類型數(shù)據(jù)

const car = reactive({
    brand: 'GTR',
    model: 'Corolla',
    year: 2020,
    info: {
        color: 'red',
        mileage: 15000
    }
})
const carNum = reactive(100)

console.log('引用數(shù)據(jù)類型', car)
console.log('基本數(shù)據(jù)類型', carNum)

圖片

重要限制reactive只接受對象類型,基本類型會原樣返回并產(chǎn)生警告

從上圖我們還能看到,正常使用的reactive返回的是一個Proxy對象,也就是說reactive 實現(xiàn)響應式就是基于ES6 Proxy 實現(xiàn)的。

響應式

ref一樣,reactive默認也是深層響應式,并且watch的監(jiān)聽是默認開啟深度監(jiān)聽的

const car = reactive({
    brand: 'GTR',
    model: 'Corolla',
    year: 2020,
    info: {
        color: 'red',
        mileage: 15000,
        total: 10
    }
})

watch(car, (newValue, oldValue) => {
    console.log('car changed from', oldValue.info.total, 'to', newValue.info.total)
})


圖片


會丟失響應式的幾個操作

  • 對象引用發(fā)生變化

由于 Vue 的響應式跟蹤是通過屬性訪問實現(xiàn)的,因此必須始終保持對響應式對象的相同引用。

let person = reactive({
    name: 'nanjiu'
})
// 重新賦值
person = {
    name: '南玖22',
}
// 這里再修改數(shù)據(jù),頁面并不會更新
const changeNameProxy = () => {
    person.name = '小鹿' // 修改代理對象的屬性
    console.log('修改代理對象后', person) // Proxy(Object) {name: '小鹿'}
}


圖片


  • 解構

當我們將響應式對象的原始類型屬性解構為本地變量時,或者將該屬性傳遞給函數(shù)時,也將丟失響應性

let person = reactive({
    name: 'nanjiu'
})
let { name } = person
const changeNameProxy = () => {
    name = '小鹿' // 修改解構后的屬性,頁面不會更新,person.name也不會更新
    console.log('修改代理對象后', person) // Proxy(Object) {name: 'nanjiu'}
}

原始對象與代理對象

  • reactive() 返回的是一個原始對象的 Proxy代理對象,兩者是不相等的
const raw = {
    name: '南玖'
}
const person = reactive(raw)

console.log('原始對象', raw)
console.log('響應式對象', person)
console.log('person === raw', person === raw) // false


圖片


  • 原始對象與代理對象是相互影響的
const raw = {
    name: '南玖'
}
const person = reactive(raw)

raw.name = '小鹿' // 修改原始對象的屬性
// person.name = '小鹿' // 修改響應式對象的屬性

console.log('原始對象', raw)  // {name: '小鹿'}
console.log('響應式對象', person) // Proxy(Object) {name: '小鹿'}

當原始對象里面的數(shù)據(jù)發(fā)生改變時,代理對象的數(shù)據(jù)也會發(fā)生變化;當代理對象里面的數(shù)據(jù)發(fā)生變化時,對應的原始數(shù)據(jù)也會發(fā)生變化

既然兩者可以相互影響,那么修改原始對象會不會觸發(fā)頁面更新呢???

答案是不會的,只有代理對象是響應式的,更改原始對象不會觸發(fā)更新。因此,使用 Vue 的響應式系統(tǒng)的最佳實踐是僅使用你聲明對象的代理版本。

代理一致性

為保證訪問代理的一致性,對同一個原始對象調(diào)用 reactive() 會總是返回同樣的代理對象,而對一個已存在的代理對象調(diào)用 reactive() 會返回其本身:

// 在同一個對象上調(diào)用 reactive() 會返回相同的代理
console.log(reactive(raw) === proxy) // true

// 在一個代理上調(diào)用 reactive() 會返回它自己
console.log(reactive(proxy) === proxy) // true

依靠深層響應行,響應式對象內(nèi)的嵌套屬性依然是代理對象

const raw = {
    name: '南玖'
}
const obj = {}
const person = reactive(raw)

person.hobby = obj
console.log('hobby', person.hobby) // Proxy(Object) {}
console.log('hobby === obj', person.hobby === obj) // false

shallowReactive

與shallowRef類似,shallowReactive也是用于聲明一個淺層的響應式對象,用于性能優(yōu)化處理

const shallowObj = shallowReactive({
    name: '南玖',
    age: 20,
    info: {
        hobby: 'run'
    }
})

const changeNameProxy = () => {
    shallowObj.info.hobby = 'swim' // 修改嵌套對象的屬性, 頁面不會更新
    console.log('修改后的代理對象', shallowObj) 
}


圖片


但如果同時修改頂層屬性與嵌套屬性的話,頁面也是會同時更新頂層值與嵌套值的渲染,一般來說我們要避免這樣使用,這會讓數(shù)據(jù)流難以理解和調(diào)試

const changeNameProxy = () => {
    shallowObj.name = '小鹿' // 修改對象的頂層屬性
    shallowObj.info.hobby = 'swim' // 修改嵌套對象的屬性
    console.log('修改后的代理對象', shallowObj) 
}


圖片


readonly

?

接受一個對象 (不論是響應式還是普通的) 或是一個 ref,返回一個原值的只讀代理。常用于數(shù)據(jù)保護

const shallowObjReadonly = readonly(shallowObj) // 創(chuàng)建只讀的淺響應式對象

shallowObjReadonly.name = 'nanjiu' // 只讀對象不能修改屬性, 會拋出錯誤

總結

特性

ref

reactive

接受類型

任意類型

僅對象類型

訪問方式

通過.value訪問

直接訪問屬性

模板解包

自動解包(無需.value)

無需解包

深層響應

默認支持

默認支持

性能優(yōu)化

shallowRef

shallowReactive

watch

對于引用類型,watch默認不會開啟深度監(jiān)聽

默認開啟深度監(jiān)聽

引用替換

保持響應(.value=新引用)

完全丟失響應

解構處理

需保持.value引用

需配合toRefs

適用場景

基本類型、組件模板引用、跨函數(shù)傳遞

復雜對象、狀態(tài)管理、局部狀態(tài)


責任編輯:龐桂玉 來源: 前端南玖
相關推薦

2025-07-31 09:05:38

2022-12-06 08:39:27

Vue3Reactive

2021-12-02 05:50:35

Vue3 插件Vue應用

2021-12-28 08:46:00

Vue3refreactive

2022-06-26 00:00:02

Vue3響應式系統(tǒng)

2024-04-11 13:10:00

Vue3Reactive響應性

2021-09-27 06:29:47

Vue3 響應式原理Vue應用

2025-06-06 06:17:48

VueReact前端

2023-08-09 10:21:07

Vue 3Reactive

2025-08-06 13:39:39

Vue3React響應性

2022-06-23 07:46:34

VueMobx系統(tǒng)

2023-02-06 08:39:01

PreactVue3響應式

2025-02-17 08:58:06

2023-12-06 07:43:56

Vue如何定義事件

2022-07-12 08:14:15

vue3refIsRef

2020-06-09 11:35:30

Vue 3響應式前端

2025-03-21 08:30:00

Vue3前端框架

2023-06-13 08:01:27

qwikSolidJS?reactive

2024-05-06 00:00:00

RefReactive性能

2022-01-19 18:05:47

Vue3前端代碼
點贊
收藏

51CTO技術棧公眾號