VUE3源碼學(xué)習(xí)(一)

最近工作不是很忙,從尤雨溪公布VUE3源碼以后团搞,就想著找個機(jī)會讀一下绞旅,也是比較忙摆尝,也是不太會TypeScript所以一直就擱置了,這段時間工作不是很忙因悲,趁著上班時間學(xué)了一天TypeScript堕汞,因?yàn)橛蠧#和JS基礎(chǔ),所以學(xué)起來也挺快的晃琳,然后就迫不及待的翻開了源碼讯检,emmmmm,這都是啥蝎土?视哑??

我水平不行誊涯,直接點(diǎn)開看的話挡毅,真的是,連猜帶懵暴构,猜這些個代碼是啥意思跪呈,這個模塊是干嘛的,看了一上午實(shí)在是看不下去了取逾,我受不了這種瞎撞的感覺耗绿,就在網(wǎng)上找一找有沒有大牛已經(jīng)讀過了,至少把結(jié)構(gòu)什么的能讓我搞懂砾隅,就找到了這個误阻,VUE3源碼導(dǎo)讀,在這里真的感謝這位老哥,讓我少走了很多彎路究反。

但是光知道結(jié)構(gòu)和一點(diǎn)點(diǎn)基礎(chǔ)寻定,看起來還是挺麻煩的,因?yàn)閂UE2到VUE3過渡實(shí)在是太大了精耐,用之前的定型思維去看VUE3的代碼還是有點(diǎn)難受狼速,這里我推薦先看RFC,看完以后對VUE的整個過渡會有一個大體的了解卦停,然后就可以快樂的閱讀源碼啦向胡。

我先看的是reactivity模塊的,這是一個極其重要的模塊惊完,它是一個數(shù)據(jù)響應(yīng)式系統(tǒng)僵芹。其暴露的主要 API 有 ref(數(shù)據(jù)容器)、reactive(基于 Proxy 實(shí)現(xiàn)的響應(yīng)式數(shù)據(jù))专执、computed(計(jì)算數(shù)據(jù))淮捆、effect(副作用) 等幾部分:

export {
  reactive,
  isReactive,
  readonly,
  isReadonly,
  toRaw,
  markReadonly,
  markNonReactive
} from './reactive'
export {
  computed,
  ComputedRef,
  WritableComputedRef,
  WritableComputedOptions
} from './computed'
export {
  effect,
  stop,
  pauseTracking,
  resumeTracking,
  ITERATE_KEY,
  ReactiveEffect,
  ReactiveEffectOptions,
  DebuggerEvent
} from './effect'
export { lock, unlock } from './lock'
export { OperationTypes } from './operations'

我到現(xiàn)在只看了關(guān)于reactive的一些代碼,所以這篇文章也只會介紹一些reactive本股,關(guān)于其他的如ref,computed以后會慢慢進(jìn)行補(bǔ)充桐腌。

VUE3通過reactive()來創(chuàng)建一個可觀察對象拄显,類似于Vue.observable(),例如const obj = reactive({ count: 0 })案站,
那么在調(diào)用reactive()之后到底做了什么呢躬审,我先把相關(guān)源碼貼出來

//reactive.ts
import { isObject, toRawType } from '@vue/shared'
import {
  mutableHandlers,
  readonlyHandlers,
  shallowReadonlyHandlers
} from './baseHandlers'
import {
  mutableCollectionHandlers,
  readonlyCollectionHandlers
} from './collectionHandlers'
import { UnwrapRef, Ref } from './ref'
import { makeMap } from '@vue/shared'

// WeakMaps that store {raw <-> observed} pairs.
const rawToReactive = new WeakMap<any, any>()
const reactiveToRaw = new WeakMap<any, any>()
const rawToReadonly = new WeakMap<any, any>()
const readonlyToRaw = new WeakMap<any, any>()

// WeakSets for values that are marked readonly or non-reactive during
// observable creation.
const readonlyValues = new WeakSet<any>()
const nonReactiveValues = new WeakSet<any>()

const collectionTypes = new Set<Function>([Set, Map, WeakMap, WeakSet])
const isObservableType = /*#__PURE__*/ makeMap(
  'Object,Array,Map,Set,WeakMap,WeakSet'
)

const canObserve = (value: any): boolean => {
  return (
    !value._isVue &&
    !value._isVNode &&
    isObservableType(toRawType(value)) &&
    !nonReactiveValues.has(value)
  )
}

// only unwrap nested ref
type UnwrapNestedRefs<T> = T extends Ref ? T : UnwrapRef<T>

export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
export function reactive(target: object) {
  // if trying to observe a readonly proxy, return the readonly version.
  if (readonlyToRaw.has(target)) {
    return target
  }
  // target is explicitly marked as readonly by user
  if (readonlyValues.has(target)) {
    return readonly(target)
  }
  return createReactiveObject(
    target,
    rawToReactive,
    reactiveToRaw,
    mutableHandlers,
    mutableCollectionHandlers
  )
}

export function readonly<T extends object>(
  target: T
): Readonly<UnwrapNestedRefs<T>> {
  // value is a mutable observable, retrieve its original and return
  // a readonly version.
  if (reactiveToRaw.has(target)) {
    target = reactiveToRaw.get(target)
  }
  return createReactiveObject(
    target,
    rawToReadonly,
    readonlyToRaw,
    readonlyHandlers,
    readonlyCollectionHandlers
  )
}

// @internal
// Return a reactive-copy of the original object, where only the root level
// properties are readonly, and does not recursively convert returned properties.
// This is used for creating the props proxy object for stateful components.
export function shallowReadonly<T extends object>(
  target: T
): Readonly<{ [K in keyof T]: UnwrapNestedRefs<T[K]> }> {
  return createReactiveObject(
    target,
    rawToReadonly,
    readonlyToRaw,
    shallowReadonlyHandlers,
    readonlyCollectionHandlers
  )
}

function createReactiveObject(
  target: unknown,
  toProxy: WeakMap<any, any>,
  toRaw: WeakMap<any, any>,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<any>
) {
  if (!isObject(target)) {
    if (__DEV__) {
      console.warn(`value cannot be made reactive: ${String(target)}`)
    }
    return target
  }
  // target already has corresponding Proxy
  let observed = toProxy.get(target)
  if (observed !== void 0) {
    return observed
  }
  // target is already a Proxy
  if (toRaw.has(target)) {
    return target
  }
  // only a whitelist of value types can be observed.
  if (!canObserve(target)) {
    return target
  }
  const handlers = collectionTypes.has(target.constructor)
    ? collectionHandlers
    : baseHandlers
  observed = new Proxy(target, handlers)
  toProxy.set(target, observed)
  toRaw.set(observed, target)
  return observed
}

export function isReactive(value: unknown): boolean {
  return reactiveToRaw.has(value) || readonlyToRaw.has(value)
}

export function isReadonly(value: unknown): boolean {
  return readonlyToRaw.has(value)
}

export function toRaw<T>(observed: T): T {
  return reactiveToRaw.get(observed) || readonlyToRaw.get(observed) || observed
}

export function markReadonly<T>(value: T): T {
  readonlyValues.add(value)
  return value
}

export function markNonReactive<T>(value: T): T {
  nonReactiveValues.add(value)
  return value
}

很明顯,在使用reactive創(chuàng)建對象的時候蟆盐,先會判斷是否要創(chuàng)建的是一個只讀對象或者已經(jīng)被用戶明確的標(biāo)記只讀承边,然后調(diào)用createReactiveObject,進(jìn)行檢查石挂,是否要創(chuàng)建的對象是否已經(jīng)被定義成了一個proxy或者對象本身就是一個proxy博助,然后判斷這個對象是否可以被創(chuàng)建成一個proxy,然后接下來
const handlers = collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers,這句就對要創(chuàng)建的proxy對象進(jìn)行判斷痹愚,選擇要提供陷阱函數(shù)富岳,這里分為baseHandlers和collectionHandlers,在這里我就不把源碼貼出來了拯腮,在創(chuàng)建完proxy之后窖式,就大功告成了,但是還是需要把創(chuàng)建好的proxy存下來动壤,以便下一次創(chuàng)建時進(jìn)行判斷

  toProxy.set(target, observed)
  toRaw.set(observed, target)

這就是reactive的核心了萝喘,還有一些關(guān)于readonly的方法在這里就不再一一贅述了,感興趣的可以深入進(jìn)行了解。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末阁簸,一起剝皮案震驚了整個濱河市弦蹂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌强窖,老刑警劉巖凸椿,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異翅溺,居然都是意外死亡脑漫,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進(jìn)店門咙崎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來优幸,“玉大人,你說我怎么就攤上這事褪猛⊥耍” “怎么了?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵伊滋,是天一觀的道長碳却。 經(jīng)常有香客問我,道長笑旺,這世上最難降的妖魔是什么昼浦? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮筒主,結(jié)果婚禮上关噪,老公的妹妹穿的比我還像新娘。我一直安慰自己乌妙,他們只是感情好使兔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著藤韵,像睡著了一般虐沥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上荠察,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天置蜀,我揣著相機(jī)與錄音,去河邊找鬼悉盆。 笑死盯荤,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的焕盟。 我是一名探鬼主播秋秤,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼宏粤,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了灼卢?” 一聲冷哼從身側(cè)響起绍哎,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鞋真,沒想到半個月后崇堰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涩咖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年海诲,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片檩互。...
    茶點(diǎn)故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡特幔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出闸昨,到底是詐尸還是另有隱情蚯斯,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布饵较,位于F島的核電站拍嵌,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏告抄。R本人自食惡果不足惜撰茎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望打洼。 院中可真熱鬧,春花似錦逆粹、人聲如沸募疮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽阿浓。三九已至,卻和暖如春蹋绽,著一層夾襖步出監(jiān)牢的瞬間芭毙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工卸耘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留退敦,地道東北人。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓蚣抗,卻偏偏與公主長得像侈百,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評論 2 353

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