最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

vue3響應式原理之Ref用法及說明

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

theme: fancy

一. Ref 用法

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

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)響應化的值
  • _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)換為響應式,所以ref中也可以實現(xiàn)對象的響應式管理,我們將他放在下篇文章中來講,它是實現(xiàn)對象響應式的主要方法,我們今天只講基本類型。

源碼路徑: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值時,通知對應的組件更新。我們來看下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ù),所以該值應該是一個變化的值,它是如何準確無誤的將每個組件更新函數(shù)來放入到對應的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ù)到頁面的響應式渲染。

// 源碼路徑: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()
? ? ? }
? ? }
? }
}

五. 總結

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

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

相關文章

  • vue3中的ref與reactive使用方法對比

    vue3中的ref與reactive使用方法對比

    Vue3 提供了兩個新的 API:ref 和 reactive,它們可以幫助我們更好地管理和處理響應式數(shù)據(jù),這篇文章主要介紹了vue3中的ref與reactive的區(qū)別和使用方法,需要的朋友可以參考下
    2023-04-04
  • 關于vue-cli-service:command?not?found報錯引發(fā)的實戰(zhàn)案例

    關于vue-cli-service:command?not?found報錯引發(fā)的實戰(zhàn)案例

    這篇文章主要給大家介紹了關于vue-cli-service:command?not?found報錯引發(fā)的相關資料,文中通過實例代碼將解決的過程介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2023-02-02
  • vue?perfect-scrollbar(特定框架里使用非該框架定制庫/插件)

    vue?perfect-scrollbar(特定框架里使用非該框架定制庫/插件)

    這篇文章主要為大家介紹了vue?perfect-scrollbar在特定框架里使用一款并非為該框架定制的庫/插件如何實現(xiàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪<BR>
    2023-05-05
  • vue+js實現(xiàn)微信上傳圖片選擇功能

    vue+js實現(xiàn)微信上傳圖片選擇功能

    這篇文章主要為大家詳細介紹了vue+js實現(xiàn)微信上傳圖片選擇功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • Vue.js系列之項目結構說明(2)

    Vue.js系列之項目結構說明(2)

    這篇文章主要介紹了Vue.js系列之項目結構說明(2)的相關資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2017-01-01
  • vue.js根據(jù)圖片url進行圖片下載

    vue.js根據(jù)圖片url進行圖片下載

    最近在做一個前端vue.js對接的功能模塊時,需要實現(xiàn)一個下載圖片的功能,本文就介紹了vue.js根據(jù)圖片url進行圖片下載,感興趣的可以了解一下
    2021-06-06
  • Vue實現(xiàn)隨機驗證碼功能

    Vue實現(xiàn)隨機驗證碼功能

    這篇文章主要為大家詳細介紹了Vue實現(xiàn)隨機驗證碼功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • element?Drawer?抽屜無法渲染問題及解決

    element?Drawer?抽屜無法渲染問題及解決

    這篇文章主要介紹了element?Drawer?抽屜無法渲染問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • Element-UI 使用el-row 分欄布局的教程

    Element-UI 使用el-row 分欄布局的教程

    這篇文章主要介紹了Element-UI 使用el-row 分欄布局的教程,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • VUE+node(express)實現(xiàn)前后端分離

    VUE+node(express)實現(xiàn)前后端分離

    在本篇文章里小編給大家分享的是關于VUE+node(express)前后端分離實例內(nèi)容,有需要的朋友們參考下。
    2019-10-10

最新評論

子洲县| 章丘市| 安徽省| 长宁区| 吐鲁番市| 扎赉特旗| 宁国市| 贺兰县| 兰州市| 永城市| 翁源县| 鸡泽县| 固安县| 墨竹工卡县| 铜梁县| 连云港市| 杨浦区| 新化县| 眉山市| 响水县| 漾濞| 明水县| 临武县| 诸城市| 平安县| 廉江市| 安化县| 南涧| 高阳县| 永顺县| 临湘市| 大埔区| 太谷县| 巢湖市| 固安县| 犍为县| 尼勒克县| 宁城县| 扬州市| 朔州市| 收藏|