欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

vue3響應(yīng)式原理之Ref用法及說明

 更新時間:2022年12月03日 11:12:21   作者:石頭山_S  
這篇文章主要介紹了vue3響應(yīng)式原理之Ref用法及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

theme: fancy

一. Ref 用法

這是 ref 最基本的用法,返回來的count是一個響應(yīng)式的代理值

const count = ref(0)

二. 實現(xiàn)

1. ref 函數(shù)

我們調(diào)用的ref函數(shù),傳進來一個 val 值,調(diào)用 createRef 函數(shù),我們來看下該函數(shù)的實現(xiàn)

源碼路徑:packages/reactivity/src/ref.ts

function ref(value?: unknown) {
? return createRef(value, false)
}

2. createRef 函數(shù)

該函數(shù)里邊做了一個判斷,該值如果已經(jīng)是一個Ref,則直接返回,否則創(chuàng)建一個 Ref 實例,讓我們看下 RefImpl 類的實現(xiàn)

源碼路徑:packages/reactivity/src/ref.ts

function createRef(rawValue: unknown, shallow: boolean) {
? if (isRef(rawValue)) {
? ? return rawValue
? }
? return new RefImpl(rawValue, shallow)
}

3. RefImpl 類的實現(xiàn)

定義了四個變量

  • _value: 用來保存加工后實現(xiàn)響應(yīng)化的值
  • _rawValue: 用來保存當前未經(jīng)加工過的值
  • dep: 用來收集依賴,是一個 Set類型
  • _v_isRef: 用來標識該值是否經(jīng)過 ref 加工

在constructor中,我們?yōu)開rawValue 和_value實現(xiàn)了初始化,分別調(diào)用了toRaw和toReactive函數(shù),toReactive將 object類型轉(zhuǎn)換為響應(yīng)式,所以ref中也可以實現(xiàn)對象的響應(yīng)式管理,我們將他放在下篇文章中來講,它是實現(xiàn)對象響應(yīng)式的主要方法,我們今天只講基本類型。

源碼路徑:packages/reactivity/src/ref.ts

class RefImpl<T> {
? private _value: T
? private _rawValue: T

? public dep?: Dep = undefined
? public readonly __v_isRef = true

? constructor(value: T, public readonly __v_isShallow: boolean) {
? ? this._rawValue = __v_isShallow ? value : toRaw(value)
? ? this._value = __v_isShallow ? value : toReactive(value)
? }

? get value() {
? ? trackRefValue(this)
? ? return this._value
? }

? set value(newVal) {
? ? newVal = this.__v_isShallow ? newVal : toRaw(newVal)
? ? if (hasChanged(newVal, this._rawValue)) {
? ? ? this._rawValue = newVal
? ? ? this._value = this.__v_isShallow ? newVal : toReactive(newVal)
? ? ? triggerRefValue(this, newVal)
? ? }
? }
}

4. trackRefValue 依賴收集

從上邊代碼中我們可以看到,我們使用 get 和 set對 value 屬性實現(xiàn)了攔截,其實如同Object.defineProperty,在 get中實現(xiàn)依賴收集,set中通知依賴更新,而 vue3中是通過調(diào)用trackRefValue來實現(xiàn)跟蹤依賴。

在該方法中,調(diào)用了trackEffect方法,在收集第一個依賴時執(zhí)行createDep方法來作為參數(shù)傳入。

我們接著往下看。

源碼路徑:packages/reactivity/src/ref.ts

function trackRefValue(ref: RefBase<any>) {
? if (shouldTrack && activeEffect) {
? ? ref = toRaw(ref)
? ? if (__DEV__) {
? ? ? trackEffects(ref.dep || (ref.dep = createDep()), {
? ? ? ? target: ref,
? ? ? ? type: TrackOpTypes.GET,
? ? ? ? key: 'value'
? ? ? })
? ? } else {
? ? ? trackEffects(ref.dep || (ref.dep = createDep()))
? ? }
? }
}

5. createDep 創(chuàng)建依賴容器

其實就是創(chuàng)建了一個 Set 類型,之后我們進入到下一步 trackEffects。

源碼路徑:packages/reactivity/src/dep.ts

export const createDep = (effects?: ReactiveEffect[]): Dep => {
? const dep = new Set<ReactiveEffect>(effects) as Dep
? dep.w = 0
? dep.n = 0
? return dep
}

6. trackEffects

可以看到這里我們將activeEffect 放入的我們的Ref的dep中,也就是Set類型,那這個activeEffect是什么呢?

它其實就是我們的componentUpdateFn,每次更改value值時,通知對應(yīng)的組件更新。我們來看下activeEffect是如何賦值的。

源碼路徑:packages/reactivity/src/effect.ts

export function trackEffects(
? dep: Dep,
? debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
? if (shouldTrack) {
? ? // 放入我們的依賴集合?
? ? dep.add(activeEffect!)
? ? activeEffect!.deps.push(dep)

}

三. activeEffect 的賦值

我們在上邊說過,activeEffect其實就是componentUpdateFn函數(shù),所以該值應(yīng)該是一個變化的值,它是如何準確無誤的將每個組件更新函數(shù)來放入到對應(yīng)的dep中的呢,我們回到setupRenderEffect函數(shù)里邊來看一下。

//源碼路徑 core/packages/runtime-core/src/renderer.ts

? const setupRenderEffect: SetupRenderEffectFn = (
? ? instance,
? ? initialVNode,
? ? container,
? ? anchor,
? ? parentSuspense,
? ? isSVG,
? ? optimized
? ) => {
? ? const componentUpdateFn = () => { }
? ? // create reactive effect for rendering
? ? const effect = (instance.effect = new ReactiveEffect(
? ? ? componentUpdateFn,
? ? ? () => queueJob(instance.update),
? ? ? instance.scope // track it in component's effect scope
? ? ))
? ? const update = (instance.update = effect.run.bind(effect) as SchedulerJob)

? ? update.id = instance.uid
? ? // allowRecurse
? ? // #1801, #2043 component render effects should allow recursive updates
? ? toggleRecurse(instance, true)


? ? update()
? }

從上邊源碼中我們可以看到,在setRenderEffectFn方法中實現(xiàn)了componentUpdateFn的定義,同時在此時創(chuàng)建了一個ReactiveEfect的對象,同時調(diào)用了ReactiveEffect中的run方法,并且使用bind來改變其中的this指向該effect。我們來進一步看下run中發(fā)生了什么。

源碼路徑:packages/reactivity/src/effect.ts

? run() {
? ? if (!this.active) {
? ? ? return this.fn()
? ? }
? ? let parent: ReactiveEffect | undefined = activeEffect
??
? ? try {
? ? ? this.parent = activeEffect
? ? ? activeEffect = this
? ? ? shouldTrack = true

? ? ? trackOpBit = 1 << ++effectTrackDepth

? ? ? if (effectTrackDepth <= maxMarkerBits) {
? ? ? ? initDepMarkers(this)
? ? ? } else {
? ? ? ? cleanupEffect(this)
? ? ? }
? ? ? return this.fn()
? ? } finally {
? ? ? if (effectTrackDepth <= maxMarkerBits) {
? ? ? ? finalizeDepMarkers(this)
? ? ? }

? ? ? trackOpBit = 1 << --effectTrackDepth

? ? ? activeEffect = this.parent
? ? ? shouldTrack = lastShouldTrack
? ? ? this.parent = undefined
? ? }
? }

從上邊代碼中我們看到,在try中,我們先將this賦值給activeEffect,然后調(diào)用傳入的componentUpdateFn函數(shù),在該函數(shù)中會獲取我們定義的ref變量的值,觸發(fā)get,然后將該this也就是effect保存到該Ref的dep中,這就完成了我們的整個依賴收集的過程。

四. 依賴收集

收集依賴后,下一步就是在數(shù)據(jù)改變之后通知依賴更新,我們來看下set中是如何做的。

在set中我們調(diào)用了triggerRefValue方法,傳入了this,也就是當前Ref實例,還有新的值。

源碼路徑:packages/reactivity/src/ref.ts  

? set value(newVal) {
? ? newVal = this.__v_isShallow ? newVal : toRaw(newVal)
? ? if (hasChanged(newVal, this._rawValue)) {
? ? ? this._rawValue = newVal
? ? ? this._value = this.__v_isShallow ? newVal : toReactive(newVal)
? ? ? triggerRefValue(this, newVal)
? ? }
? }

1. triggerRefValue

該方法中調(diào)用了triggerEffects方法,將ref.dep也就是之前收集依賴的Set,傳入。讓我們接著往下看。

// 源碼路徑:packages/reactivity/src/ref.ts

export function triggerRefValue(ref: RefBase<any>, newVal?: any) {
? ref = toRaw(ref)
? if (ref.dep) {
? ? if (__DEV__) {
? ? ? triggerEffects(ref.dep, {
? ? ? ? target: ref,
? ? ? ? type: TriggerOpTypes.SET,
? ? ? ? key: 'value',
? ? ? ? newValue: newVal
? ? ? })
? ? } else {
? ? ? triggerEffects(ref.dep)
? ? }
? }
}

2. triggerEffects

該方法中,遍歷Set,然后調(diào)用每個依賴上的run方法,也就是執(zhí)行更新函數(shù),進而使頁面刷新。實現(xiàn)數(shù)據(jù)到頁面的響應(yīng)式渲染。

// 源碼路徑:packages/reactivity/src/effect.ts

export function triggerEffects(
? dep: Dep | ReactiveEffect[],
? debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
? // spread into array for stabilization
? for (const effect of isArray(dep) ? dep : [...dep]) {
? ? if (effect !== activeEffect || effect.allowRecurse) {
? ? ? if (__DEV__ && effect.onTrigger) {
? ? ? ? effect.onTrigger(extend({ effect }, debuggerEventExtraInfo))
? ? ? }
? ? ? if (effect.scheduler) {
? ? ? ? effect.scheduler()
? ? ? } else {
? ? ? ? effect.run()
? ? ? }
? ? }
? }
}

五. 總結(jié)

vue3 中對Ref的實現(xiàn)基本沒有太大改變,也是利用setter和getter對數(shù)據(jù)實現(xiàn)數(shù)據(jù)劫持,而Reactive的響應(yīng)式原理就與vue2的方案截然不同了。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • vue computed計算屬性顯示undefined的解決

    vue computed計算屬性顯示undefined的解決

    這篇文章主要介紹了vue computed計算屬性顯示undefined的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • vue 項目中使用websocket的正確姿勢

    vue 項目中使用websocket的正確姿勢

    這篇文章主要介紹了vue 項目中使用websocket的實例代碼,通過實例代碼給大家介紹了在utils下新建websocket.js文件的方法,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-01-01
  • vue實現(xiàn)彈框遮罩點擊其他區(qū)域彈框關(guān)閉及v-if與v-show的區(qū)別介紹

    vue實現(xiàn)彈框遮罩點擊其他區(qū)域彈框關(guān)閉及v-if與v-show的區(qū)別介紹

    vue如何簡單的實現(xiàn)彈框,遮罩,點擊其他區(qū)域關(guān)閉彈框, 簡單的思路是以一個div作為遮罩,這篇文章給大家詳細介紹了vue實現(xiàn)彈框遮罩點擊其他區(qū)域彈框關(guān)閉及v-if與v-show的區(qū)別介紹,感興趣的朋友一起看看吧
    2018-09-09
  • vue項目使用高德地圖的定位及關(guān)鍵字搜索功能的實例代碼(踩坑經(jīng)驗)

    vue項目使用高德地圖的定位及關(guān)鍵字搜索功能的實例代碼(踩坑經(jīng)驗)

    這篇文章主要介紹了vue項目使用高德地圖的定位及關(guān)鍵字搜索功能的實例代碼,也是小編踩了無數(shù)坑總結(jié)出來的經(jīng)驗,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-03-03
  • vue+echarts實現(xiàn)多條折線圖

    vue+echarts實現(xiàn)多條折線圖

    這篇文章主要為大家詳細介紹了vue+echarts實現(xiàn)多條折線圖,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • vue.js數(shù)據(jù)響應(yīng)式原理解析

    vue.js數(shù)據(jù)響應(yīng)式原理解析

    這篇文章主要介紹了vue.js數(shù)據(jù)響應(yīng)式原理解析,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下
    2022-08-08
  • VueJS實現(xiàn)用戶管理系統(tǒng)

    VueJS實現(xiàn)用戶管理系統(tǒng)

    這篇文章主要為大家詳細介紹了VueJS實現(xiàn)用戶管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-05-05
  • Vue.js之$emit用法案例詳解

    Vue.js之$emit用法案例詳解

    這篇文章主要介紹了Vue.js之$emit用法案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-09-09
  • vue2利用Bus.js如何實現(xiàn)非父子組件通信詳解

    vue2利用Bus.js如何實現(xiàn)非父子組件通信詳解

    這篇文章主要給大家介紹了關(guān)于vue2利用Bus.js如何實現(xiàn)非父子組件通信的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-08-08
  • Vue實現(xiàn)動態(tài)圓環(huán)百分比進度條

    Vue實現(xiàn)動態(tài)圓環(huán)百分比進度條

    這篇文章主要為大家詳細介紹了Vue實現(xiàn)動態(tài)圓環(huán)百分比進度條,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09

最新評論