第七節(jié):帶你全面理解vue3: 其他響應(yīng)式進(jìn)階API

前言:

針對(duì)vue3官網(wǎng)中, 響應(yīng)式:進(jìn)階API 中, 我們?cè)谏弦徽轮薪o大家講解了shallowRef, shallowReactive, shallowReadonly幾個(gè)API的使用.

本章主要對(duì)剩下的API 進(jìn)行講解, 我們先看一下官網(wǎng)中進(jìn)階API 都有哪些

image.png

對(duì)于剩下這些API, 你需要了解他們創(chuàng)建目的, 是為了解決之前的API存在的那些痛點(diǎn)問(wèn)題, 這樣你就能更好的了解使用他們的細(xì)節(jié).工作中就可以有的放矢的選擇不同的API.

1. triggerRef

我們首先來(lái)分析一下triggerRefAPI 的使用

1.1. triggerRef 針對(duì)的痛點(diǎn)問(wèn)題

我們先看一個(gè)痛點(diǎn)問(wèn)題:

對(duì)于ref響應(yīng)式數(shù)據(jù)的變化, vue幫我們處理副作用. 比如,頁(yè)面的更新, watchEffect偵聽器回調(diào)函數(shù)的調(diào)用等.

但對(duì)于淺層響應(yīng)數(shù)據(jù), 比如shallowRef創(chuàng)建的數(shù)據(jù), 其深層并不具有響應(yīng)性, 也就是說(shuō)vue并沒(méi)有監(jiān)測(cè)這些數(shù)據(jù)的變化, 當(dāng)對(duì)深層數(shù)據(jù)進(jìn)行修改時(shí), 并不會(huì)觸發(fā)副作用, 比如頁(yè)面不會(huì)自動(dòng)刷新.

triggerRefAPI 就是為了解決shallowRef淺層響應(yīng)式數(shù)據(jù)深層修改問(wèn)題.

當(dāng)深層修改時(shí), 會(huì)強(qiáng)制觸發(fā)依賴于一個(gè)淺層 ref 的副作用偎血,這通常在對(duì)淺引用的內(nèi)部值進(jìn)行深度變更后使用。

1.2. triggerRef 類型

類型:

function triggerRef(ref: ShallowRef): void

triggerRefAPI 函數(shù)接收一個(gè)shallowRefAPI 創(chuàng)建的數(shù)據(jù), 作用就是強(qiáng)制觸發(fā)這個(gè)淺層ref數(shù)據(jù)的副作用.

1.3. triggerRef 使用示例

示例:

<template>
  <div>
    <h3>shallowReadonly</h3>
    <div>{{ count }}</div>
    <div>{{ count2 }}</div>
    <button @click="change">修改數(shù)據(jù)源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, readonly, ref, shallowReadonly, shallowRef, triggerRef, watchEffect } from 'vue'

export default defineComponent({
  setup() {
    const count = ref({ num: 0 })
    const count2 = shallowRef({ num: 0 })


    // 對(duì)于ref 數(shù)據(jù), 是深層響應(yīng)式,
    // 因此當(dāng)我們通過(guò)count.value.num++ 修改數(shù)據(jù)時(shí),依然會(huì)觸發(fā)watchEffect副作用函數(shù)
    watchEffect(() => {
      console.log('count.value.num', count.value.num)
    })


    // 因?yàn)閟hallowRef 數(shù)據(jù)不是深層響應(yīng)式, 只有.value 整體修改才會(huì)觸發(fā)響應(yīng)式
    // 因?yàn)楫?dāng)我們通過(guò)count2.value.num++ 修改數(shù)據(jù)時(shí),不會(huì)出發(fā)watchEffect 副作用函數(shù)
    // 同時(shí)視圖也不會(huì)發(fā)生更改
    watchEffect(() => {
      console.log('count2.value.num', count2.value.num)
    })


    // 修改數(shù)據(jù)
    const change = () => {
      // count.value.num++
      count2.value.num++

      // 如果希望shallowRef 深層數(shù)據(jù)修改后,觸發(fā)視圖更新
      // 那么就需要使用triggerRef 手動(dòng)觸發(fā)更新
      triggerRef(count2)  // 手動(dòng)更新count2
    }
    return { count, count2, change }
  }
})
</script>

通過(guò)示例的運(yùn)行結(jié)果, 你也可以看出. shallowRef創(chuàng)建響應(yīng)式數(shù)據(jù), 在深層數(shù)據(jù)發(fā)生變化時(shí), 不會(huì)觸發(fā)頁(yè)面更新 和watchEffect的處理函數(shù). 因?yàn)樯顚硬痪哂许憫?yīng)性.

當(dāng)我們手動(dòng)調(diào)用triggerRef函數(shù), 并將shallowRef創(chuàng)建數(shù)據(jù)作為參數(shù), 就是告訴vue , 我們需要強(qiáng)制執(zhí)行shallowRef數(shù)據(jù)的副作用. 此時(shí)頁(yè)面將會(huì)更新, watchEffect處理函數(shù)也會(huì)自動(dòng)執(zhí)行

1.4. triggerRef 使用小結(jié)

在理解triggerRefAPI 的使用后, 針對(duì)該API, 我做了以下小結(jié)

  • triggerRef常與shallowRef搭配使用
  • triggerRef會(huì)強(qiáng)制更新以shallowRef數(shù)據(jù)作為依賴的副作用盯漂,ref數(shù)據(jù)會(huì)自動(dòng)觸發(fā)這些副作用

我們需要注意的是: vue3只提供了triggerRef這個(gè)方法颇玷,但沒(méi)有提供triggerReactive的方法。 也就是說(shuō)triggerRef【不可以】去更改 shallowReactive創(chuàng)建的數(shù)據(jù)

2. toRaw

根據(jù)一個(gè) Vue 創(chuàng)建的代理返回其原始對(duì)象

2.1. toRaw 針對(duì)的問(wèn)題

vue3中, 我們通過(guò)reactive()就缆、readonly()帖渠、shallowReactive() shallowReadonly()四個(gè)API 創(chuàng)建的響應(yīng)式數(shù)據(jù), 本質(zhì)上就是通過(guò)Proxy創(chuàng)建的代理對(duì)象.

但有時(shí)我們?cè)谧鰯?shù)據(jù)傳輸時(shí), 我們并不需要傳響應(yīng)式數(shù)據(jù), 我們只想傳最基本的原始對(duì)象.

toRawAPI 的作用就是返回 reactive()readonly()竭宰、shallowReactive(),shallowReadonly() 創(chuàng)建的代理對(duì)應(yīng)的原始對(duì)象空郊。

2.2. toRaw 類型

toRaw 函數(shù)簽名

function toRaw<T>(proxy: T): T

toRawAPI 函數(shù)接收一個(gè)Proxy代理對(duì)象(響應(yīng)式對(duì)象)作為參數(shù),

2.3. toRaw 使用示例

示例:

<template>
  <div>
    <h3>shallowReactive</h3>
    <div>{{ user }}</div>
    <button @click="change">修改數(shù)據(jù)源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive, toRaw } from 'vue'

export default defineComponent({
  setup() {
    // 代理目標(biāo)對(duì)象
    const obj = { name: '張三', age: 18 }

    // reactive 處理的代理對(duì)象
    const user = reactive(obj)

    // 控制觸發(fā)代理對(duì)象
    console.log('user', user)

    // 使用toRaw, 參數(shù)是代理對(duì)象, 返回代理對(duì)象的目標(biāo)對(duì)象
    console.log('toRaw(user)', toRaw(user))
    console.log('toRaw(user) === obj', toRaw(user) === obj)  // true

    // 修改數(shù)據(jù)
    const change = () => {
      user.name = '李四'

    }
    return { user, change }
  }
})
</script>

通過(guò)控制臺(tái)輸出結(jié)果, 你可以看出, toRaw 就是獲取代理對(duì)象的原目標(biāo)對(duì)象.

這是一個(gè)可以用于臨時(shí)讀取而不引起代理訪問(wèn)/跟蹤開銷,或是寫入而不觸發(fā)更改的特殊方法切揭。不建議保存對(duì)原始對(duì)象的持久引用狞甚,請(qǐng)謹(jǐn)慎使用。

這句話來(lái)自于官網(wǎng), 這句話你可以這么理解,

代理對(duì)象具有響應(yīng)性, 可以理解為vue在監(jiān)測(cè)這個(gè)數(shù)據(jù)的變化, 這個(gè)監(jiān)測(cè)會(huì)消耗性能. 如果你的操作不要觸發(fā)副作用, 就沒(méi)有必要 使用具有響應(yīng)性的代理對(duì)象.

比如調(diào)用接口時(shí)傳入的參數(shù), 就可以使用toRaw去掉代理對(duì)象的外殼, 獲取到原始對(duì)象傳入接口.

3. markRaw

markRaw 函數(shù)的作用就是將一個(gè)對(duì)象轉(zhuǎn)為不可代理對(duì)象.

如果使用reactiveAPI , 也不會(huì)代理markRaw函數(shù)返回的對(duì)象, 會(huì)直接返回原對(duì)象.

示例:

<template>
  <div>
    <h3>shallowReactive</h3>
    <div>{{ user }}</div>
    <button @click="change">修改數(shù)據(jù)源</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, markRaw, reactive } from 'vue'

export default defineComponent({
  setup() {
    // 代理目標(biāo)對(duì)象
    const obj = { name: '張三', age: 18 }

    // 將obj原始對(duì)象標(biāo)記為不可代理
    const markObj = markRaw(obj)

    // reactive 處理的代理對(duì)象
    const user = reactive(markObj)

    // user 不是代理對(duì)象
    console.log('user', user)


    // 修改數(shù)據(jù)
    const change = () => {
      user.name = '李四'

    }
    return { user, change }
  }
})
</script>

控制臺(tái)輸出:

image.png

通過(guò)控制臺(tái)輸出結(jié)果, 可以看出, 通過(guò)markRaw 處理過(guò)的對(duì)象具有一個(gè)__v_skip的屬性, 用于標(biāo)記這個(gè)對(duì)象不能創(chuàng)建代理對(duì)象, 即響應(yīng)式數(shù)據(jù).

盡管你將該對(duì)象傳入reactive, 返回的也不是一個(gè)代理對(duì)象, 而是原對(duì)象.

既然不是響應(yīng)數(shù)據(jù),修改user.name 時(shí), 就不會(huì)觸發(fā)視圖更新

該API的作用就是, 幫助你給一些你不希望創(chuàng)建為代理對(duì)象的原始對(duì)象添加標(biāo)記.

4. effectScope

4.1. effectScope 作用

vue3的使用過(guò)程中,我們可能會(huì)針對(duì)同一個(gè)響應(yīng)式數(shù)據(jù)創(chuàng)建多個(gè)副作用.比如computed, watch, watchEffect等.

再次過(guò)程中, 如果關(guān)閉某個(gè)副作用, 比如watch創(chuàng)建的偵聽器, 就需要通過(guò)返回值關(guān)閉. 那么多個(gè)副作用你就需要一個(gè)一個(gè)關(guān)閉. 使用相對(duì)麻煩

effectScope字面意思就是副作用作用域, 可以理解為, 該函數(shù)創(chuàng)建一個(gè)作用域, 將所有的副作用放在共同一個(gè)作用域中, 如果以后想統(tǒng)一關(guān)閉副作用, 就可以使用作用域整體關(guān)閉.

4.2. effectScope

類型

function effectScope(detached?: boolean): EffectScope

interface EffectScope {
  run<T>(fn: () => T): T | undefined // 如果作用域不活躍就為 undefined
  stop(): void
}

effectScope函數(shù)返回一個(gè)作用域?qū)ο? 即EffectScope類型.

該作用域?qū)ο笊暇哂?code>run, stop方法, 同時(shí)run方法接收一個(gè)回調(diào)函數(shù)作為參數(shù).

4.3. effectScope 使用方式

通過(guò)effectScope函數(shù)創(chuàng)建一個(gè) effect 作用域伴箩,可以捕獲其中所創(chuàng)建的響應(yīng)式副作用 (即計(jì)算屬性和偵聽器)入愧,這樣捕獲到的副作用可以一起處理鄙漏。

示例:

<template>
  <div>
    <h3>shallowReactive</h3>
    <div>{{ count }}</div>
    <button @click="change">修改數(shù)據(jù)源</button>
  </div>
</template>

<script lang="ts">
import { computed, defineComponent, effectScope, markRaw, reactive, ref, shallowReactive, toRaw, watch, watchEffect } from 'vue'

export default defineComponent({
  setup() {
    // 創(chuàng)建ref 數(shù)據(jù)
    const count = ref(10)

    // 創(chuàng)建副作用作用域
    const scope = effectScope()
    // 控制臺(tái)輸出 effect 作用域
    console.log("scope", scope);
    
    // 收集運(yùn)行的副作用
    scope.run(() => {
      // 計(jì)算屬性副作用
      const computedCount = computed(() => count.value * 2
      )

      // watch 偵聽副作用
      watch(
        count,
        () => {
          console.log('computedCount', computedCount.value)
          console.log('watch count', count.value)
        }
      )

      // watchEffect 副作用
      watchEffect(() => {
        console.log('watchEffect count', count.value)
      })

    })


    console.log('scope', scope) 
    // 2秒以后關(guān)閉所有的副作用
    setTimeout(() => {
      scope.stop()
    }, 2000)
    
    // 修改數(shù)據(jù)
    const change = () => {
      count.value++

    }
    return { count, change }
  }
})
</script>

控制臺(tái)輸出結(jié)果:

image.png

通過(guò)控制臺(tái)輸出的effect作用域?qū)ο? 你可以看到, 作用域?qū)⒒卣{(diào)函數(shù)中的副作用進(jìn)行了收集, 存儲(chǔ)在effects屬性上.

同時(shí)effect作用域?qū)ο笤蛯?duì)象上具有run收集副作用的方法, stop關(guān)閉副作用的方法.

5. getCurrentScope

getCurrentScope函數(shù)返回當(dāng)前活躍的 effect 作用域嗤谚。

在前一個(gè)API中, 給大家講解了effectScope函數(shù), 該函數(shù)執(zhí)行后會(huì)返回一個(gè)effect作用域, 通過(guò)調(diào)用effect作用域?qū)ο蟮?code>run方法收集所有副作用. 我們就可以在run方法的回調(diào)函數(shù)中, 通過(guò)getCurrentScope函數(shù)獲取到正在活躍的effect作用域?qū)ο?

示例:

// 創(chuàng)建副作用作用域
const scope = effectScope();
console.log("scope", scope);

// 收集運(yùn)行的副作用
scope.run(() => {
  // 計(jì)算屬性副作用
  const computedCount = computed(() => count.value * 2);

  // watch 偵聽副作用
  watch(count, () => {
    console.log("computedCount", computedCount.value);
    console.log("watch count", count.value);
  });

  // watchEffect 副作用
  watchEffect(() => {
    console.log("watchEffect count", count.value);
  });

  // 通過(guò)  getCurrentScope() 獲取當(dāng)前真正活躍的 effect 作用域?qū)ο?  const effectScope = getCurrentScope();

  console.log("getCurrentScope", effectScope === scope);
  // 控制臺(tái)輸出結(jié)果: getCurrentScope true
});

示例中, 我們通過(guò)effectScope創(chuàng)建了一個(gè)effect作用域?qū)ο? 當(dāng)調(diào)用該作用域?qū)ο蟮?code>run方法,傳入回調(diào)函數(shù), 會(huì)自動(dòng)執(zhí)行回調(diào)函數(shù), 收集副作用, 并將收集到的副作用保存在副作用effect作用域中. 也就是說(shuō), 在執(zhí)行回調(diào)函數(shù)時(shí), 我們創(chuàng)建的scope就是活躍的effect作用域

之后,我們通過(guò)執(zhí)行getCurrentScope函數(shù)獲取當(dāng)前活躍的副作用作用域, 和之前我們創(chuàng)建的作用域?qū)Ρ? 發(fā)現(xiàn)getCurrentScope 獲取的就是我們創(chuàng)建的effect作用域.

其實(shí)每一個(gè)組件都有一個(gè)effect作用域, 用于收集組件內(nèi)所有的副作用. 組件更新函數(shù)本身也就是一個(gè)副作用. 這也就是響應(yīng)式數(shù)據(jù)變化后, 頁(yè)面會(huì)重新渲染的原因.

以及組件被銷毀后, vue3 會(huì)通過(guò)組件的effect作用域清理組件內(nèi)收集的所有副作用

該API 在工作中并不常使用到. 甚至一個(gè)項(xiàng)目里連一次都不會(huì)用到.

6. onScopeDispose

該API 函數(shù)主要用于調(diào)試, 工作中也不怎么常用, 其作用就是在當(dāng)前活躍的副作用(effect)作用域?qū)ο笊献?cè)一個(gè)調(diào)試的回調(diào)函數(shù). 在effect作用域關(guān)閉時(shí), 會(huì)自動(dòng)調(diào)用注冊(cè)的回調(diào)函數(shù),.

示例:

// 創(chuàng)建副作用作用域
const scope = effectScope();
console.log("scope", scope);

// 收集運(yùn)行的副作用
scope.run(() => {
  // 計(jì)算屬性副作用
  const computedCount = computed(() => count.value * 2);

  // watch 偵聽副作用
  watch(count, () => {
    console.log("computedCount", computedCount.value);
    console.log("watch count", count.value);
  });

  // watchEffect 副作用
  watchEffect(() => {
    console.log("watchEffect count", count.value);
  });

  // 在當(dāng)前活躍的 effect 作用域?qū)ο笊献?cè)一個(gè)回調(diào)函數(shù)
  onScopeDispose(() => {
    console.log("當(dāng)前effectScope 停止");
  });
});

// 2秒以后關(guān)閉所有的副作用
setTimeout(() => {
  scope.stop();
}, 2000);

示例中, 我們?cè)?code>effectScope收集副作用時(shí), 通過(guò)onScopeDispose函數(shù)注冊(cè)了一個(gè)回調(diào)函數(shù).

effectScope副作用作用域, 即scope對(duì)象調(diào)用stop方法時(shí), 會(huì)自動(dòng)執(zhí)行注冊(cè)的回調(diào)函數(shù). 多用于功能調(diào)試

7. 結(jié)語(yǔ)

至此, 就把vue3中響應(yīng)式進(jìn)階API 中剩余的API函數(shù)給大家講完了, 這里比較常用的API 有triggerRef, toRaw, markRaw, effectScope, 其余兩個(gè)API 函數(shù)并不怎么常用.

這里尤其要注意effectScope, 使用好了可以給代碼增色不少.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市怔蚌,隨后出現(xiàn)的幾起案子巩步,更是在濱河造成了極大的恐慌,老刑警劉巖桦踊,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件椅野,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡籍胯,警方通過(guò)查閱死者的電腦和手機(jī)竟闪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)杖狼,“玉大人炼蛤,你說(shuō)我怎么就攤上這事〉” “怎么了理朋?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵絮识,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我嗽上,道長(zhǎng)次舌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任兽愤,我火速辦了婚禮彼念,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘浅萧。我一直安慰自己国拇,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布惯殊。 她就那樣靜靜地躺著酱吝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪土思。 梳的紋絲不亂的頭發(fā)上务热,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音己儒,去河邊找鬼崎岂。 笑死,一個(gè)胖子當(dāng)著我的面吹牛闪湾,可吹牛的內(nèi)容都是我干的冲甘。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼途样,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼江醇!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起何暇,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤陶夜,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后裆站,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體条辟,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年宏胯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了羽嫡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡肩袍,死狀恐怖杭棵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情了牛,我是刑警寧澤颜屠,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布辰妙,位于F島的核電站,受9級(jí)特大地震影響甫窟,放射性物質(zhì)發(fā)生泄漏密浑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一粗井、第九天 我趴在偏房一處隱蔽的房頂上張望尔破。 院中可真熱鬧,春花似錦浇衬、人聲如沸懒构。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)胆剧。三九已至,卻和暖如春醉冤,著一層夾襖步出監(jiān)牢的瞬間秩霍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工蚁阳, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留铃绒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓螺捐,卻偏偏與公主長(zhǎng)得像颠悬,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子定血,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容