中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Vue3.0響應式系統的示例分析

發布時間:2021-08-19 09:17:32 來源:億速云 閱讀:135 作者:小新 欄目:web開發

這篇文章主要介紹Vue3.0響應式系統的示例分析,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

引子

先來段代碼,大家可以直接復制哦,注意引用的文件

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <meta http-equiv="X-UA-Compatible" content="ie=edge">
 <title>Document</title>
 <script src="../packages/vue/dist/vue.global.js"></script>
</head>
<body>
 <div id="app"></div>
 <script>
  const { reactive, computed, effect, watch, createApp } = Vue
  const App = {
   template: `
    <div id="box">
      <button @click="increment">{{ state.count }}</button>
    </div> 
   `,
   setup() {
    const state = reactive({
     count: 0
    })
    function increment(e) {
     state.count++
    }
    effect(() => {
     console.log('count改變', state.count);
    })
    return {
     state,
     increment
    }
   }
  }
  createApp().mount(App, '#app')
 </script>
</body>
</html>

這段代碼,想必大家都看得懂,點擊后count增加,視圖也隨之更新,effect監聽了count改變,那么為什么effect能觀察到count變化呢,還有為什么reactive可以實現響應式?

effect

為什么要先說這個函數呢,因為它和其他函數都息息相關,只有先了解它才能更好的理解其他響應式API

上源碼

export function effect(
 fn: Function,
 options: ReactiveEffectOptions = EMPTY_OBJ
): ReactiveEffect {
 if ((fn as ReactiveEffect).isEffect) {
  fn = (fn as ReactiveEffect).raw
 }
 const effect = createReactiveEffect(fn, options)
 if (!options.lazy) {
  effect()
 }
 return effect
}

if判斷,判斷如果傳入的fn函數,它已經是effect了,也就是一個標識,直接獲取該函數上的raw屬性,這個屬性后面會講到

調用createReactiveEffect

如果options中有lazy,就會立即調用effect,其實本質上調用的還是傳入的fn函數

// 了解一下options有哪些
{
 lazy?: boolean // 是否立即調用fn
 computed?: boolean // 是否是computed
 scheduler?: (run: Function) => void // 在調用fn之前執行
 onTrack?: (event: DebuggerEvent) => void // 在依賴收集完成之后調用
 onTrigger?: (event: DebuggerEvent) => void // 在調用fn之前執行,源碼上來看和scheduler調用時機一樣,只是傳入參數不同
 onStop?: () => void // 清除依賴完成后調用
}

返回effect

createReactiveEffect

上面提到了createReactiveEffect函數,我們來看看它的實現

function createReactiveEffect(
 fn: Function,
 options: ReactiveEffectOptions
): ReactiveEffect {
 // 又包裝了一層函數
 const effect = function effect(...args): any {
  return run(effect as ReactiveEffect, fn, args)
 } as ReactiveEffect
 effect.isEffect = true // 標識effect
 effect.active = true // 如果active
 effect.raw = fn // 傳入的回調
 effect.scheduler = options.scheduler
 effect.onTrack = options.onTrack
 effect.onTrigger = options.onTrigger
 effect.onStop = options.onStop
 effect.computed = options.computed
 effect.deps = [] // 用于收集依賴
 return effect
}

注意,敲黑板,這里有個run函數,很重要,因為它保存了依賴

function run(effect: ReactiveEffect, fn: Function, args: any[]): any {
 if (!effect.active) {
  return fn(...args)
 }
 if (activeReactiveEffectStack.indexOf(effect) === -1) {
  cleanup(effect)
  try {
   activeReactiveEffectStack.push(effect)
   return fn(...args)
  } finally {
   activeReactiveEffectStack.pop()
  }
 }
}

他把依賴存儲在了一個全局的數組中activeReactiveEffectStack, 他以棧的形式存儲,調用完依賴后,會彈出,大家要留意一下這里,后面會用到

怎么樣,是不是很簡單~

reactive

export function reactive(target: object) {
 // 如果target是已經被readonly對象,那么直接返回對應的proxy對象
 if (readonlyToRaw.has(target)) {
  return target
 }

 // 如果target是已經被readonly對象,那么直接返回對應的真實對象
 if (readonlyValues.has(target)) {
  return readonly(target)
 }
 return createReactiveObject(
  target,
  rawToReactive,
  reactiveToRaw,
  mutableHandlers,
  mutableCollectionHandlers
 )
}

前兩個if是用來處理這種情況的

// 情況一
const state1 = readonly({ count: 0 })
const state2 = reactive(state1)

// 情況二
const obj = { count: 0 }
const state1 = readonly(obj)
const state2 = reactive(obj)
可以看到reactive它的參數是被readonly的對象,reactive不會對它再次創建響應式,而是通過Map映射,拿到對應的對象,即Proxy <==> Object的相互轉換。


createReactiveObject創建響應式對象,注意它的參數
createReactiveObject(
  target,
  rawToReactive,  // Object ==> Proxy
  reactiveToRaw,  // Proxy ==> Object
  mutableHandlers, // get set has ...
  mutableCollectionHandlers // 很少會用,不講了~
)

以上就是reative一開始所做的一些事情,下面繼續分析createReactiveObject

createReactiveObject

function createReactiveObject(
 target: any,
 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
 }

 let observed = toProxy.get(target)
 // 如果目標對象已經有proxy對象,直接返回
 if (observed !== void 0) {
  return observed
 }

 // 如果目標對象是proxy的對象,并且有對應的真實對象,那么也直接返回
 if (toRaw.has(target)) {
  return target
 }
 // 如果它是vnode或者vue,則不能被觀測
 if (!canObserve(target)) {
  return target
 }
 // 判斷被觀測的對象是否是set,weakSet,map,weakMap,根據情況使用對應proxy的,配置對象
 const handlers = collectionTypes.has(target.constructor)
  ? collectionHandlers
  : baseHandlers
 observed = new Proxy(target, handlers)
 toProxy.set(target, observed)
 toRaw.set(observed, target)
 if (!targetMap.has(target)) {
  targetMap.set(target, new Map())
 }
 return observed
}

第一個if,判斷是否是對象,否則報出警告

toProxy拿到觀測對象的Proxy對象,如果存在直接返回

// 這種情況
const obj = { count: 0 }
const state1 = reative(obj)
const state2 = reative(obj)

toRaw拿到Proxy對象對應的真實對象,如果存在直接返回

// 這種情況
const obj = { count: 0 }
const state1 = reative(obj)
const state2 = reative(state1)

有些情況無法被觀測,則直接返回觀測對象本身

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

設置handlers,即get,set等屬性訪問器, 注意:collectionHandlers是用來處理觀測對象為Set,Map等情況,很少見,這里就不講了

 const handlers = collectionTypes.has(target.constructor)
  ? collectionHandlers
  : baseHandlers

然后創建了Proxy對象,并把觀測對象和Proxy對象,分別做映射

 observed = new Proxy(target, handlers)
 toProxy.set(target, observed)
 toRaw.set(observed, target)

然后在targetMap做了target ==> Map的映射,這又是干嘛,注意:targetMap是全局的

export const targetMap: WeakMap<any, KeyToDepMap> = new WeakMap()
 if (!targetMap.has(target)) {
  targetMap.set(target, new Map())
 }

在這里先給大家賣個關子,targetMap非常重要,是用來保存依賴的地方

講完了reactive,可以回到一開始的引子

依賴收集

說到依賴收集,不得不提到,依賴的創建,那么Vue3.0是在哪里創建了渲染依賴呢,大家可以找到下面這段代碼以及文件

// vue-next\packages\runtime-core\src\createRenderer.ts
 function setupRenderEffect(
  instance: ComponentInternalInstance,
  parentSuspense: HostSuspsenseBoundary | null,
  initialVNode: HostVNode,
  container: HostElement,
  anchor: HostNode | null,
  isSVG: boolean
 ) {
  // create reactive effect for rendering
  let mounted = false
  instance.update = effect(function componentEffect() {
 // ...
  }, __DEV__ ? createDevEffectOptions(instance) : prodEffectOptions)
 }

代碼特別長,我剪掉了中間部分,大家還記得effect有個選項lazy嗎,沒錯,它默認是false,也就會立即調用傳入的componentEffect回調,在它內部調用了patch實現了組件的掛載。

敲黑板,關鍵來了,還記得effect調用,內部會調用run方法嗎

function run(effect: ReactiveEffect, fn: Function, args: any[]): any {
 if (!effect.active) {
  return fn(...args)
 }
 if (activeReactiveEffectStack.indexOf(effect) === -1) {
  cleanup(effect)
  try {
   activeReactiveEffectStack.push(effect)
   return fn(...args)
  } finally {
   activeReactiveEffectStack.pop()
  }
 }
}

這里進行了第一步的依賴收集,保存在全局數組中,為了方便觸發get的對象,將依賴收集到自己的deps中
然后就是調用patch,進行組件掛載

if (!mounted) {
  const subTree = (instance.subTree = renderComponentRoot(instance))
  // beforeMount hook
  if (instance.bm !== null) {
    invokeHooks(instance.bm)
  }
  patch(null, subTree, container, anchor, instance, parentSuspense, isSVG)
  initialVNode.el = subTree.el
  // mounted hook
  if (instance.m !== null) {
    queuePostRenderEffect(instance.m, parentSuspense)
  }
  mounted = true
}

至于它內部實現,我就不講了,不是本文重點,然后我們去編譯的地方看看

//vue-next\packages\runtime-core\src\component.ts
function finishComponentSetup(
 instance: ComponentInternalInstance,
 parentSuspense: SuspenseBoundary | null
) {
 const Component = instance.type as ComponentOptions
 if (!instance.render) {
  if (Component.template && !Component.render) {
   if (compile) {
    Component.render = compile(Component.template, {
     onError(err) {}
    })
   } else if (__DEV__) {
    warn(
     `Component provides template but the build of Vue you are running ` +
      `does not support on-the-fly template compilation. Either use the ` +
      `full build or pre-compile the template using Vue CLI.`
    )
   }
  }
  if (__DEV__ && !Component.render) {
   warn(
    `Component is missing render function. Either provide a template or ` +
     `return a render function from setup().`
   )
  }
  instance.render = (Component.render || NOOP) as RenderFunction
 }

 // ...其他
}

上面的代碼是編譯部分,我們來看看例子中編譯后是什么樣

(function anonymous(
) {
const _Vue = Vue
const _createVNode = Vue.createVNode

const _hoisted_1 = { id: "box" }

return function render() {
 with (this) {
  const { toString: _toString, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue
  
  return (_openBlock(), _createBlock("div", _hoisted_1, [
   _createVNode("button", { onClick: increment }, _toString(state.count), 9 /* TEXT, PROPS */, ["onClick"])
  ]))
 }
}
})

可以看到,編譯的代碼中,有使用到state.count,那么就會觸發get訪問器,從而收集依賴,至于為什么能直接訪問到屬性,原因是由于with設置了上下文,下面我們具體分析get

get

// vue-next\packages\reactivity\src\baseHandlers.ts
function createGetter(isReadonly: boolean) {
 return function get(target: any, key: string | symbol, receiver: any) {
  const res = Reflect.get(target, key, receiver)
  if (typeof key === 'symbol' && builtInSymbols.has(key)) {
   return res
  }
  // _isRef
  if (isRef(res)) {
   return res.value
  }
  track(target, OperationTypes.GET, key)
  // 如果該屬性對應的值還是對象,就繼續遞歸創建響應式
  return isObject(res)
   ? isReadonly
    ? // need to lazy access readonly and reactive here to avoid
     // circular dependency
     readonly(res)
    : reactive(res)
   : res
 }
}

調用Reflect.get獲取屬性值

如果key是symbol并且是Symbol的一個屬性,就直接返回該值

// 這種情況
const key = Symbol('key')
const state = reative({
  [key]: 'symbol value'
})
state[key]

如果值為Ref返回該值的value,看到這里如果大家有了解過ref api的話就知道了,由于ref它自己實現了自己的get,set,所以不再需要執行后面的邏輯,這個在后面會講

調用track

遞歸深度觀測,使整個對象都為響應式

下面我會詳細講解

track

在講它之前,先了解它有哪些參數

 target: any, // 目標對象
 type: OperationTypes, // 追蹤數據變化類型,這里是get
 key?: string | symbol // 需要獲取的key
 export const enum OperationTypes {
   SET = 'set',
   ADD = 'add',
   DELETE = 'delete',
   CLEAR = 'clear',
   GET = 'get',
   HAS = 'has',
   ITERATE = 'iterate' 
 }
export function track(
 target: any,
 type: OperationTypes,
 key?: string | symbol
) {
 if (!shouldTrack) {
  return
 }
 // 獲取activeReactiveEffectStack中的依賴
 const effect = activeReactiveEffectStack[activeReactiveEffectStack.length - 1]
 if (effect) {
  if (type === OperationTypes.ITERATE) {
   key = ITERATE_KEY
  }
  // 獲取目標對象對應的依賴map
  let depsMap = targetMap.get(target)
  if (depsMap === void 0) {
   targetMap.set(target, (depsMap = new Map()))
  }
  // 獲取對應屬性的依賴
  let dep = depsMap.get(key as string | symbol)
  // 如果該依賴不存在
  if (!dep) {
   // 設置屬性對應依賴
   depsMap.set(key as string | symbol, (dep = new Set()))
  }
  // 如果屬性對應依賴set中不存在該依賴
  if (!dep.has(effect)) {
   // 添加到依賴set中
   dep.add(effect)
   effect.deps.push(dep)
   if (__DEV__ && effect.onTrack) {
    // 調用onTrack鉤子
    effect.onTrack({
     effect,
     target,
     type,
     key
    })
   }
  }
 }
}

activeReactiveEffectStack我兩次提到,從它這里拿到了依賴,注意后面執行完依賴后,會從它里面彈出

如果effect存在

從targetMap中獲取對象,對飲的Map,具體的數據結構類似這樣

const state = reative({
  count: 0
})
effect(() => {
 console.log(state.count) 
})

// 依賴大致結構(隨便寫的,不太規范)
{
  target(state):Map {
    count: Set (componentEffect渲染依賴, user自己添加的依賴)
  }
}

如果該對象不存在Map,就初始化一個

如果該Map中屬性對應的Set不存在,就初始化一個Set

添加依賴到Set中

添加依賴到effect自身的deps數組中

最后調用onTrack回調

// 調用onTrack鉤子
effect.onTrack({
  effect,
  target,
  type,
  key
})

OK,Track實現大體就這樣,是不是也很簡單,有了這些基礎,后面要講的一些API就很容易理解了

set

當我們點擊按鈕后,就會觸發set屬性訪問器

function set(
 target: any,
 key: string | symbol,
 value: any,
 receiver: any
): boolean {
 value = toRaw(value)
 const hadKey = hasOwn(target, key)
 const oldValue = target[key]
 // 如果舊的值是ref,而新的值不是ref
 if (isRef(oldValue) && !isRef(value)) {
  // 直接更改原始ref即可
  oldValue.value = value
  return true
 }
 const result = Reflect.set(target, key, value, receiver)
 // don't trigger if target is something up in the prototype chain of original
 if (target === toRaw(receiver)) {
  /* istanbul ignore else */
  if (__DEV__) {
   const extraInfo = { oldValue, newValue: value }
   if (!hadKey) {
    trigger(target, OperationTypes.ADD, key, extraInfo)
   } else if (value !== oldValue) {
    trigger(target, OperationTypes.SET, key, extraInfo)
   }
  } else {
   if (!hadKey) {
    trigger(target, OperationTypes.ADD, key)
   } else if (value !== oldValue) {
    trigger(target, OperationTypes.SET, key)
   }
  }
 }
 return result
}

判斷舊值是ref,新值不是ref

// 這種情況
const val = ref(0)
const state = reative({
  count: val
})
state.count = 1
// 其實state.count最終還是ref,還是能通過value訪問
state.count.value // 1

調用Reflect.set修改值

開發環境下,拿到新舊值組成的對象,調用trigger,為什么開發環境要這么做呢,其實是為了方便onTrigger能拿到新舊值

trigger(target, OperationTypes.ADD, key, extraInfo)

可以看到第二個參數和track是一樣的enum,有兩種情況,一種我們設置了新的屬性和值,另一種修改了原有屬性值,下面我們來看看trigger實現。

trigger

export function trigger(
 target: any,
 type: OperationTypes,
 key?: string | symbol,
 extraInfo?: any
) {
 const depsMap = targetMap.get(target)
 if (depsMap === void 0) {
  // never been tracked
  return
 }
 // effect set
 const effects: Set<ReactiveEffect> = new Set()
 // computed effect set
 const computedRunners: Set<ReactiveEffect> = new Set()

 if (type === OperationTypes.CLEAR) {
  depsMap.forEach(dep => {
   addRunners(effects, computedRunners, dep)
  })
 } else {
  // 添加effect到set中
  if (key !== void 0) {
   addRunners(effects, computedRunners, depsMap.get(key as string | symbol))
  }
  // also run for iteration key on ADD | DELETE
  if (type === OperationTypes.ADD || type === OperationTypes.DELETE) {
   const iterationKey = Array.isArray(target) ? 'length' : ITERATE_KEY
   addRunners(effects, computedRunners, depsMap.get(iterationKey))
  }
 }

 // 執行set中的effect
 const run = (effect: ReactiveEffect) => {
  scheduleRun(effect, target, type, key, extraInfo)
 }
 computedRunners.forEach(run)
 effects.forEach(run)
}

看到這個函數開始的targetMap,大家應該很清楚要干嘛了吧,沒錯,拿到對象的Map,它包含了屬性的所有依賴

  1. 如果沒有Map直接返回

  2. 創建了兩個Set,要干嘛用呢

 // 用來保存將要執行的依賴
 const effects: Set<ReactiveEffect> = new Set()
 // computed依賴,因為trigger不僅是要處理effect,watch,還要處理computed惰性求值的情況
 const computedRunners: Set<ReactiveEffect> = new Set()

處理三種情況CLEAR,ADD,DELETE,SET(這里沒有標識)

// effect set
const effects: Set<ReactiveEffect> = new Set()
// computed effect set
const computedRunners: Set<ReactiveEffect> = new Set()

function addRunners(
 effects: Set<ReactiveEffect>,
 computedRunners: Set<ReactiveEffect>,
 effectsToAdd: Set<ReactiveEffect> | undefined
) {
 if (effectsToAdd !== void 0) {
  effectsToAdd.forEach(effect => {
   if (effect.computed) {
    computedRunners.add(effect)
   } else {
    effects.add(effect)
   }
  })
 }
}

可以看到,三種情況實際上都差不多,唯一的區別就是,如果添加的對象是數組,就會拿到length屬性的依賴,用于修改數組長度

if (type === OperationTypes.ADD || type === OperationTypes.DELETE) {
  const iterationKey = Array.isArray(target) ? 'length' : ITERATE_KEY
  addRunners(effects, computedRunners, depsMap.get(iterationKey))
}

執行屬性對應的依賴

 // 執行set中的effect
 const run = (effect: ReactiveEffect) => {
  scheduleRun(effect, target, type, key, extraInfo)
 }

 computedRunners.forEach(run)
 effects.forEach(run)
function scheduleRun(
 effect: ReactiveEffect,
 target: any,
 type: OperationTypes,
 key: string | symbol | undefined,
 extraInfo: any
) {
 if (__DEV__ && effect.onTrigger) {
  effect.onTrigger(
   extend(
    {
     effect,
     target,
     key,
     type
    },
    extraInfo // { oldValue, newValue: value }
   )
  )
 }
 if (effect.scheduler !== void 0) {
  effect.scheduler(effect)
 } else {
  effect()
 }
}

最后調用了scheduleRun,它內部會分別執行onTrigger,scheduler,effect

需要注意的是,只有開發環境才會執行onTrigger,這也是為什么,前面要這么判斷

if (__DEV__) {
  const extraInfo = { oldValue, newValue: value }
  if (!hadKey) {
    trigger(target, OperationTypes.ADD, key, extraInfo)
  } else if (value !== oldValue) {
    trigger(target, OperationTypes.SET, key, extraInfo)
  }
}

readonly

有了前面的基礎,readonly看起來會非常簡單,唯一的區別就是rawToReadonly,rawToReadonly, readonlyHandlers

export function readonly(target: object) {

 if (reactiveToRaw.has(target)) {
  target = reactiveToRaw.get(target)
 }
 return createReactiveObject(
  target,
  rawToReadonly,
  readonlyToRaw,
  readonlyHandlers,
  readonlyCollectionHandlers
 )
}

前兩個大家應該能猜出來了,關鍵是最后這個readonlyHandlers,區別就在set

 set(target: any, key: string | symbol, value: any, receiver: any): boolean {
  if (LOCKED) {
   if (__DEV__) {
    console.warn(
     `Set operation on key "${key as any}" failed: target is readonly.`,
     target
    )
   }
   return true
  } else {
   return set(target, key, value, receiver)
  }
 }

它的實現很簡單,不過LOCKED有是什么鬼,大家可以找到lock.ts

//vue-next\packages\reactivity\src\lock.ts
export let LOCKED = true

export function lock() {
 LOCKED = true
}

export function unlock() {
 LOCKED = false
}

看似簡單,但是卻非常重要,它能夠控制被readonly的對象能夠暫時被更改,就比如我們常用的props,它是無法被修改的,但是Vue內部又要對他進行更新,那怎么辦,話不多說,我們再源碼中看他具體應用

// vue-next\packages\runtime-core\src\componentProps.ts
export function resolveProps(
 instance: ComponentInternalInstance,
 rawProps: any,
 _options: ComponentPropsOptions | void
) {
 const hasDeclaredProps = _options != null
 const options = normalizePropsOptions(_options) as NormalizedPropsOptions
 if (!rawProps && !hasDeclaredProps) {
  return
 }
 const props: any = {}
 let attrs: any = void 0

 const propsProxy = instance.propsProxy
 const setProp = propsProxy
  ? (key: string, val: any) => {
    props[key] = val
    propsProxy[key] = val
   }
  : (key: string, val: any) => {
    props[key] = val
   }

 unlock()
 
 // 省略一些修改props操作。。
  
 lock()

 instance.props = __DEV__ ? readonly(props) : props
 instance.attrs = options
  ? __DEV__ && attrs != null
   ? readonly(attrs)
   : attrs
  : instance.props
}

這里前后分別調用了unlock和lock,這樣就可以控制對readonly屬性的修改

那么readonly的講解就到這了

computed

export function computed<T>(
 getterOrOptions: (() => T) | WritableComputedOptions<T>
): any {
 const isReadonly = isFunction(getterOrOptions)
 const getter = isReadonly
  ? (getterOrOptions as (() => T))
  : (getterOrOptions as WritableComputedOptions<T>).get
 const setter = isReadonly
  ? null
  : (getterOrOptions as WritableComputedOptions<T>).set

 let dirty: boolean = true
 let value: any = undefined

 const runner = effect(getter, {
  lazy: true,
  computed: true,
  scheduler: () => {
   dirty = true
  }
 })
 return {
  _isRef: true,
  // expose effect so computed can be stopped
  effect: runner,
  get value() {
   if (dirty) {
    value = runner()
    dirty = false
   }
   trackChildRun(runner)
   return value
  },
  set value(newValue) {
   if (setter) {
    setter(newValue)
   } else {
    // TODO warn attempting to mutate readonly computed value
   }
  }
 }
}

首先是前面這段

 const isReadonly = isFunction(getterOrOptions)
 const getter = isReadonly
  ? (getterOrOptions as (() => T))
  : (getterOrOptions as WritableComputedOptions<T>).get
 const setter = isReadonly
  ? null
  : (getterOrOptions as WritableComputedOptions<T>).set

大家都知道computed是可以單獨寫一個函數,或者get,set訪問的,這里不多講

然后調用了effect,這里lazy設置為true, scheduler可以更改dirty為true

const runner = effect(getter, {
  lazy: true,
  computed: true,
  scheduler: () => {
    dirty = true
  }
})

然后我們具體來看看,返回的對象

{
  _isRef: true,
  // expose effect so computed can be stopped
  effect: runner,
  get value() {
   if (dirty) {
    value = runner()
    dirty = false
   }
   trackChildRun(runner)
   return value
  },
  set value(newValue) {
   if (setter) {
    setter(newValue)
   } else {
    // TODO warn attempting to mutate readonly computed value
   }
  }
 }

先說說set吧,尤大似乎還沒寫完,只是單純能修改值

然后是get,注意dirty的變化,如果computed依賴了state中的值,初次渲染時,他會調用依賴,然后dirty = false,關鍵來了,最后執行了trackChildRun

function trackChildRun(childRunner: ReactiveEffect) {
 const parentRunner =
  activeReactiveEffectStack[activeReactiveEffectStack.length - 1]
 if (parentRunner) {
  for (let i = 0; i < childRunner.deps.length; i++) {
   const dep = childRunner.deps[i]
   if (!dep.has(parentRunner)) {
    dep.add(parentRunner)
    parentRunner.deps.push(dep)
   }
  }
 }
}

由于computed是依賴了state中的屬性的,一旦在初始時觸發了get,執行runner,就會將依賴收集到activeReactiveEffectStack中,最后才是自己的依賴,棧的頂部是state屬性的依賴

if (!dep.has(parentRunner)) {
  dep.add(parentRunner)
  parentRunner.deps.push(dep)
}

所以最后這段代碼實現了state屬性變化后,才導致了computed依賴的調用,從而惰性求值

ref

const convert = (val: any): any => (isObject(val) ? reactive(val) : val)
export function ref<T>(raw: T): Ref<T> {
 raw = convert(raw)
 const v = {
  _isRef: true,
  get value() {
   track(v, OperationTypes.GET, '')
   return raw
  },
  set value(newVal) {
   raw = convert(newVal)
   trigger(v, OperationTypes.SET, '')
  }
 }
 return v as Ref<T>
}

ref的實現真的很簡單了,前面已經學習了那么多,相信大家都能看懂了,區別就是convert(raw)對傳入的值進行了簡單判斷,如果是對象就設置為響應式,否則返回原始值。

以上是“Vue3.0響應式系統的示例分析”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

太原市| 龙井市| 玉环县| 两当县| 许昌市| 鱼台县| 名山县| 连江县| 镇雄县| 潍坊市| 大田县| 黎城县| 屯昌县| 满城县| 神农架林区| 芒康县| 牡丹江市| 江津市| 黑龙江省| 景德镇市| 蓬溪县| 中宁县| 富蕴县| 七台河市| 吉水县| 邵东县| 黔江区| 衡东县| 信丰县| 深州市| 隆尧县| 巴青县| 安阳市| 额尔古纳市| 体育| 宜君县| 丰城市| 互助| 夹江县| 江都市| 潢川县|