Inhaltsverzeichnis
1. Effektverwendung
1. Grundlegende Verwendung
2、lazy属性为true
3、options中包含onTrack
二、源码分析
1、effect方法的实现
2、ReactiveEffect函数源码
三、依赖收集相关
1、如何触发依赖收集
2、track源码
3、trackEffects(dep, eventInfo)源码解读
四、触发依赖
1、trigger依赖更新
2、triggerEffects(deps[0], eventInfo)
3、triggerEffect(effect, debuggerEventExtraInfo)
Heim Web-Frontend View.js So nutzen Sie den Effekt des Vue3-Reaktionskerns

So nutzen Sie den Effekt des Vue3-Reaktionskerns

May 10, 2023 am 11:19 AM
vue3 effect

Normalerweise verwenden wir den Effekt nicht direkt, da der Effekt eine Low-Level-API ist. Wenn wir Vue3 verwenden, ruft Vue standardmäßig den Effekt auf. Effekt wird als Effekt übersetzt, was bedeutet, dass es funktioniert. Die Funktion, die es zum Funktionieren bringt, ist die Funktion, die wir übergeben. Die Funktion des Effekts besteht also darin, die Funktion, die wir übergeben, wirksam zu machen, das heißt, diese Funktion auszuführen. Das einfache Diagramm des Ausführungsprozesses sieht wie folgt aus:

So nutzen Sie den Effekt des Vue3-Reaktionskerns

Als nächstes werden wir zunächst die grundlegende Verwendung von Effekten anhand von Beispielen verstehen und dann das Prinzip verstehen.

1. Effektverwendung

1. Grundlegende Verwendung

const obj = reactive({count: 1})

const runner = effect(() => {
  console.log(obj.count)
})

obj.count++
Nach dem Login kopieren

Das Ergebnis wird zuerst 1 und dann 2 nach obj.count++ gedruckt. obj.count++之后打印出2。

流程简图如下:

So nutzen Sie den Effekt des Vue3-Reaktionskerns

运行effect(fun)

// 先执行
fun()  // 打印出1

const runner = new ReactiveEffect(fn)

return runner

runner: {
  run() {
    this.fun() //执行fun
  },
  stop() {

  }
}
Nach dem Login kopieren

console.log(obj.count)track依赖收集 结构如下:

So nutzen Sie den Effekt des Vue3-Reaktionskerns

obj.count++触发依赖,执行runner.run(), 实际运行的是

() => {
  console.log(obj.count)
}
Nach dem Login kopieren

所以又打印出2

2、lazy属性为true

此值为 true 时,只有在第一次手动调用 runner 后,依赖数据变更时,才会自动执行 effect 的回调,可以理解为 effect 的是在手动调用 runner 后才首次执行

const obj = reactive({count: 1})

const runner = effect(() => {
  console.log(obj.count)
}, {
  lazy: true
})
runner()
obj.count++
Nach dem Login kopieren

只会打印出2

原因是effect源码中有如下逻辑:

So nutzen Sie den Effekt des Vue3-Reaktionskerns

3、options中包含onTrack

let events = []
const onTrack = (e) => {
  events.push(e)
}
const obj = reactive({ foo: 1, bar: 2 })
const runner = effect(
  () => {
    console.log(obj.foo)
  },
  { onTrack }
)
console.log('runner', runner)
obj.foo++
console.log("events", events)
Nach dem Login kopieren

看下events的打印结果:

So nutzen Sie den Effekt des Vue3-Reaktionskerns

[
  {
    effect: runner,  // effect 函数的返回值
    target: toRaw(obj),  // 表示的是哪个响应式数据发生了变化
    type: TrackOpTypes.GET,  // 表示此次记录操作的类型。 get 表示获取值
    key: 'foo'
 }
]
Nach dem Login kopieren

二、源码分析

1、effect方法的实现

// packages/reactivity/src/effect.ts
export interface ReactiveEffectOptions extends DebuggerOptions {
  lazy?: boolean
  scheduler?: EffectScheduler
  scope?: EffectScope
  allowRecurse?: boolean
  onStop?: () => void
}

export function effect<T = any>(
  fn: () => T, // 副作用函数
  options?: ReactiveEffectOptions // 结构如上
): ReactiveEffectRunner {
  // 如果 fn 对象上有 effect 属性
  if ((fn as ReactiveEffectRunner).effect) {
    // 那么就将 fn 替换为 fn.effect.fn
    fn = (fn as ReactiveEffectRunner).effect.fn
  }
  // 创建一个响应式副作用函数
  const _effect = new ReactiveEffect(fn)
  if (options) {
    // 将配置项合并到响应式副作用函数上
    extend(_effect, options)
    // 如果配置项中有 scope 属性(该属性的作用是指定副作用函数的作用域)
    if (options.scope) recordEffectScope(_effect, options.scope)
  }
  if (!options || !options.lazy) { // options.lazy 不为true
    _effect.run() // 执行响应式副作用函数 首次执行fn()
  }
  // _effect.run作用域绑定到_effect
  const runner = _effect.run.bind(_effect) as ReactiveEffectRunner
  // 将响应式副作用函数赋值给 runner.effect
  runner.effect = _effect
  return runner
}
Nach dem Login kopieren

核心代码:

创建一个响应式副作用函数const _effect = new ReactiveEffect(fn),其运行结果如下:

So nutzen Sie den Effekt des Vue3-Reaktionskerns

非lazy状态执行响应式副作用函数_effect.run()

if (!options || !options.lazy) { // options.lazy 不为true
  _effect.run() // 执行响应式副作用函数 首次执行fn()
}
Nach dem Login kopieren

_effect.run作用域绑定到_effect

// _effect.run作用域绑定到_effect
  const runner = _effect.run.bind(_effect) as ReactiveEffectRunner
Nach dem Login kopieren

返回副作用函数runner

2、ReactiveEffect函数源码

export class ReactiveEffect<T = any> {
  active = true
  deps: Dep[] = [] // 响应式依赖项的集合
  parent: ReactiveEffect | undefined = undefined

  /**
   * Can be attached after creation
   * @internal
   */
  computed?: ComputedRefImpl<T>
  /**
   * @internal
   */
  allowRecurse?: boolean
  /**
   * @internal
   */
  private deferStop?: boolean

  onStop?: () => void
  // dev only
  onTrack?: (event: DebuggerEvent) => void
  // dev only
  onTrigger?: (event: DebuggerEvent) => void

  constructor(
    public fn: () => T,
    public scheduler: EffectScheduler | null = null,
    scope?: EffectScope
  ) {
    // 记录当前 ReactiveEffect 对象的作用域
    recordEffectScope(this, scope)
  }

  run() {
    // 如果当前 ReactiveEffect 对象不处于活动状态,直接返回 fn 的执行结果
    if (!this.active) {
      return this.fn()
    }
    // 寻找当前 ReactiveEffect 对象的最顶层的父级作用域
    let parent: ReactiveEffect | undefined = activeEffect
    let lastShouldTrack = shouldTrack // 是否要跟踪
    while (parent) {
      if (parent === this) {
        return
      }
      parent = parent.parent
    }
    try {
      // 记录父级作用域为当前活动的 ReactiveEffect 对象
      this.parent = activeEffect
      activeEffect = this  // 将当前活动的 ReactiveEffect 对象设置为 “自己”
      shouldTrack = true // 将 shouldTrack 设置为 true (表示是否需要收集依赖)
      // effectTrackDepth 用于标识当前的 effect 调用栈的深度,执行一次 effect 就会将 effectTrackDepth 加 1
      trackOpBit = 1 << ++effectTrackDepth

      if (effectTrackDepth <= maxMarkerBits) {
        // 初始依赖追踪标记
        initDepMarkers(this)
      } else {
        // 清除依赖追踪标记
        cleanupEffect(this)
      }
      // 返回副作用函数执行结果
      return this.fn()
    } finally {
      // 如果 effect调用栈的深度 没有超过阈值
      if (effectTrackDepth <= maxMarkerBits) {
        // 确定最终的依赖追踪标记
        finalizeDepMarkers(this)
      }
      // 执行完毕会将 effectTrackDepth 减 1
      trackOpBit = 1 << --effectTrackDepth
      // 执行完毕,将当前活动的 ReactiveEffect 对象设置为 “父级作用域”
      activeEffect = this.parent
      // 将 shouldTrack 设置为上一个值
      shouldTrack = lastShouldTrack
      // 将父级作用域设置为 undefined
      this.parent = undefined
      // 延时停止,这个标志是在 stop 方法中设置的
      if (this.deferStop) {
        this.stop()
      }
    }
  }

  stop() {
    // stopped while running itself - defer the cleanup
    // 如果当前 活动的 ReactiveEffect 对象是 “自己”
    // 延迟停止,需要执行完当前的副作用函数之后再停止
    if (activeEffect === this) {
      // 在 run 方法中会判断 deferStop 的值,如果为 true,就会执行 stop 方法
      this.deferStop = true
    } else if (this.active) {// 如果当前 ReactiveEffect 对象处于活动状态
      cleanupEffect(this) // 清除所有的依赖追踪标记
      if (this.onStop) { 
        this.onStop()
      }
      this.active = false // 将 active 设置为 false
    }
  }
}
Nach dem Login kopieren
  • run方法的作用就是执行副作用函数,并且在执行副作用函数的过程中,会收集依赖;

  • stop方法的作用就是停止当前的ReactiveEffect对象,停止之后,就不会再收集依赖了;

  • activeEffect和this并不是每次都相等的,因为activeEffect会跟着调用栈的深度而变化,而this则是固定的;

三、依赖收集相关

1、如何触发依赖收集

在副作用函数中, obj.count就会触发依赖收集

const runner = effect(() => {
  console.log(obj.count) 
})
Nach dem Login kopieren

触发的入口在get拦截器里面

function createGetter(isReadonly = false, shallow = false) {
  // 闭包返回 get 拦截器方法
  return function get(target: Target, key: string | symbol, receiver: object) {
    // ...
    if (!isReadonly) {
      track(target, TrackOpTypes.GET, key)
    }
    // ...
  }
Nach dem Login kopieren

2、track源码

const targetMap = new WeakMap();
/**
 * 收集依赖
 * @param target target 触发依赖的对象,例子中的obj
 * @param type 操作类型 比如obj.count就是get
 * @param key 指向对象的key, 比如obj.count就是count
 */
export function track(target: object, type: TrackOpTypes, key: unknown) {
  if (shouldTrack && activeEffect) { // 是否应该依赖收集 & 当前的new ReactiveEffect()即指向的就是当前正在执行的副作用函数

    // 如果 targetMap 中没有 target,就会创建一个 Map
    let depsMap = targetMap.get(target)
    if (!depsMap) {
      targetMap.set(target, (depsMap = new Map()))
    }
    let dep = depsMap.get(key)
    if (!dep) {
      depsMap.set(key, (dep = createDep())) // createDep 生成dep = { w:0, n: 0}
    }

    const eventInfo = __DEV__
      ? { effect: activeEffect, target, type, key }
      : undefined

    trackEffects(dep, eventInfo)
  }
}
Nach dem Login kopieren

shouldTrack在上面也讲过,它的作用就是控制是否收集依赖;

activeEffect就是我们刚刚讲的ReactiveEffect对象,它指向的就是当前正在执行的副作用函数;

track方法的作用就是收集依赖,它的实现非常简单,就是在targetMap中记录下target和key;

targetMap是一个WeakMap,它的键是target,值是一个Map,这个Map的键是key,值是一个Set;

targetMap的结构伪代码如下:

targetMap = {
  target: { 
    key: dep
  },
  // 比如:
  obj: { 
    count: {
       w: 0, 
       n: 0
    }
  }
}
Nach dem Login kopieren

So nutzen Sie den Effekt des Vue3-Reaktionskerns

以上是最原始的depMap

dev环境为增加响应式调试会增加eventInfo

const eventInfo = __DEV__
  ? { effect: activeEffect, target, type, key }
  : undefined
Nach dem Login kopieren

eventInfo结构如下:

So nutzen Sie den Effekt des Vue3-Reaktionskerns

trackEffects(dep, eventInfo)

如果 dep 中没有当前的 ReactiveEffect 对象,就会添加进去, 作用就把对象的属性操作与副作用函数建立关联,接下来看trackEffects

3、trackEffects(dep, eventInfo)源码解读

export function trackEffects(
  dep: Dep,
  debuggerEventExtraInfo?: DebuggerEventExtraInfo
) {
  let shouldTrack = false
  if (effectTrackDepth <= maxMarkerBits) {
    if (!newTracked(dep)) {
      // 执行之前 dep = Set(0) {w: 0, n: 0}

      // 执行之后 dep = Set(0) {w: 0, n: 2}
      dep.n |= trackOpBit // set newly tracked
  
      shouldTrack = !wasTracked(dep) 
    }
  } else {
    // Full cleanup mode.
    shouldTrack = !dep.has(activeEffect!)
  }

  if (shouldTrack) {
    // 将activeEffect添加到dep
    dep.add(activeEffect!)
    activeEffect!.deps.push(dep)
    if (__DEV__ && activeEffect!.onTrack) { // onTrack逻辑
      activeEffect!.onTrack(
        extend(
          {
            effect: activeEffect!
          },
          debuggerEventExtraInfo!
        )
      )
    }
  }
}
Nach dem Login kopieren

dep.add(activeEffect!) 如果 dep 中没有当前的 ReactiveEffect 对象,就会添加进去

So nutzen Sie den Effekt des Vue3-Reaktionskerns

最终生成的depTarget结构如下:

So nutzen Sie den Effekt des Vue3-Reaktionskerns

四、触发依赖

比如例子中代码obj.count++

Das Prozessdiagramm sieht wie folgt aus: 🎜🎜So nutzen Sie den Effekt von Vue3 Responsive Core🎜🎜Run effect(fun)🎜
function createSetter(shallow = false) {
  return function set(
    target: object,
    key: string | symbol,
    value: unknown,
    receiver: object
  ): boolean {
    //...
    const result = Reflect.set(target, key, value, receiver)
    // don&#39;t trigger if target is something up in the prototype chain of original
    if (target === toRaw(receiver)) {
      if (!hadKey) {
        trigger(target, TriggerOpTypes.ADD, key, value) // 触发ADD依赖更新
      } else if (hasChanged(value, oldValue)) {
        trigger(target, TriggerOpTypes.SET, key, value, oldValue) //触发SET依赖更新
      }
    }
    //...
  }
Nach dem Login kopieren
Nach dem Login kopieren
🎜console.log(obj.count)Track-Abhängigkeitssammlungsstruktur ist wie folgt: 🎜🎜So nutzen Sie den Effekt des Vue3 Responsive Core🎜🎜obj.count++< /code> löst Abhängigkeiten aus, führt runner.run() aus, was tatsächlich ausgeführt wird, ist 🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>// 路径:packages/reactivity/src/effect.ts export function trigger( target: object, type: TriggerOpTypes, key?: unknown, newValue?: unknown, oldValue?: unknown, oldTarget?: Map&lt;unknown, unknown&gt; | Set&lt;unknown&gt; ) { const depsMap = targetMap.get(target) // 获取depsMap, targetMap是在track中创建的依赖 if (!depsMap) { // never been tracked return } let deps: (Dep | undefined)[] = [] if (type === TriggerOpTypes.CLEAR) { // collection being cleared // trigger all effects for target deps = [...depsMap.values()] } else if (key === &amp;#39;length&amp;#39; &amp;&amp; isArray(target)) { const newLength = Number(newValue) depsMap.forEach((dep, key) =&gt; { if (key === &amp;#39;length&amp;#39; || key &gt;= newLength) { deps.push(dep) } }) } else { // schedule runs for SET | ADD | DELETE if (key !== void 0) { deps.push(depsMap.get(key)) } // also run for iteration key on ADD | DELETE | Map.SET switch (type) { case TriggerOpTypes.ADD: if (!isArray(target)) { deps.push(depsMap.get(ITERATE_KEY)) if (isMap(target)) { deps.push(depsMap.get(MAP_KEY_ITERATE_KEY)) } } else if (isIntegerKey(key)) { // new index added to array -&gt; length changes deps.push(depsMap.get(&amp;#39;length&amp;#39;)) } break case TriggerOpTypes.DELETE: if (!isArray(target)) { deps.push(depsMap.get(ITERATE_KEY)) if (isMap(target)) { deps.push(depsMap.get(MAP_KEY_ITERATE_KEY)) } } break case TriggerOpTypes.SET: if (isMap(target)) { deps.push(depsMap.get(ITERATE_KEY)) } break } } const eventInfo = __DEV__ ? { target, type, key, newValue, oldValue, oldTarget } : undefined if (deps.length === 1) { if (deps[0]) { if (__DEV__) { triggerEffects(deps[0], eventInfo) } else { triggerEffects(deps[0]) } } } else { const effects: ReactiveEffect[] = [] for (const dep of deps) { if (dep) { effects.push(...dep) } } if (__DEV__) { triggerEffects(createDep(effects), eventInfo) } else { triggerEffects(createDep(effects)) } } }</pre><div class="contentsignin">Nach dem Login kopieren</div></div><div class="contentsignin">Nach dem Login kopieren</div></div>🎜, also wird 2🎜🎜2 ausgegeben, das Lazy-Attribut ist wahr🎜🎜Wenn dieser Wert wahr ist, nur nach dem ersten manuellen Aufruf von Läufer, wenn sich die abhängigen Daten ändern, wird der Rückruf des Effekts automatisch ausgeführt, nachdem der Läufer 🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'> const eventInfo = __DEV__ ? { target, type, key, newValue, oldValue, oldTarget } : undefined if (deps.length === 1) { if (deps[0]) { if (__DEV__) { triggerEffects(deps[0], eventInfo) } else { triggerEffects(deps[0]) } } }</pre><div class="contentsignin">Nach dem Login kopieren</div></div><div class="contentsignin">Nach dem Login kopieren</div></div>🎜 nur 2🎜🎜ausgedruckt hat dass der Effekt-Quellcode die folgende Logik hat: 🎜🎜<img src="/static/imghw/default1.png" data-src="https://img.php.cn/upload/article/000/887/227/168368876252696.png" class="lazy" alt="So verwenden Sie den Effekt von Vue3 Responsive Core“ />🎜🎜3. Zu den Optionen gehören onTrack🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>export function triggerEffects( dep: Dep | ReactiveEffect[], debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { // spread into array for stabilization const effects = isArray(dep) ? dep : [...dep] for (const effect of effects) { if (effect.computed) { triggerEffect(effect, debuggerEventExtraInfo) } } for (const effect of effects) { if (!effect.computed) { triggerEffect(effect, debuggerEventExtraInfo) } } }</pre><div class="contentsignin">Nach dem Login kopieren</div></div><div class="contentsignin">Nach dem Login kopieren</div></div>🎜Sehen Sie sich Ereignisse an. Das Druckergebnis: 🎜🎜<img src="/static/imghw/default1.png" data-src="https://img.php.cn/upload/article/000/887/227/168368876252696.png" class="lazy" alt="So nutzen Sie den Effekt des Vue3 Responsive Core" />🎜 <div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>function triggerEffect( effect: ReactiveEffect, debuggerEventExtraInfo?: DebuggerEventExtraInfo ) { if (effect !== activeEffect || effect.allowRecurse) { // 如果 effect.onTrigger 存在,就会执行,只有开发模式下才会执行 if (__DEV__ &amp;&amp; effect.onTrigger) { effect.onTrigger(extend({ effect }, debuggerEventExtraInfo)) } // 如果 effect 是一个调度器,就会执行 scheduler if (effect.scheduler) { effect.scheduler() } else { // 其它情况执行 effect.run() effect.run() } } }</pre><div class="contentsignin">Nach dem Login kopieren</div></div><div class="contentsignin">Nach dem Login kopieren</div></div>🎜2. Quellcode-Analyse🎜🎜1. Implementierung der Effektmethode🎜rrreee🎜Kerncode:🎜🎜Erstellen Sie eine reaktive Nebeneffektfunktion < code>const _effect = new ReactiveEffect(fn) und seine laufenden Ergebnisse sind wie folgt:🎜 🎜So nutzen Sie den Effekt des Vue3-Reaktionskerns🎜🎜Ausführung im nicht verzögerten Zustand Reaktive Nebeneffektfunktion _effect.run() 🎜rrreee🎜_effect Der .run-Bereich ist an _effect🎜rrreee🎜 gebunden und gibt den Nebeneffekt der Funktion runner🎜 🎜2 zurück li>Die Funktion der run-Methode besteht darin, die Nebeneffektfunktion auszuführen, und während der Ausführung der Nebeneffektfunktion werden Abhängigkeiten gesammelt 🎜
  • Die Funktion der 🎜stop-Methode besteht darin, zu stoppen Das aktuelle ReactiveEffect-Objekt wird nicht mehr erfasst. 🎜
  • 🎜activeEffect ist nicht jedes Mal gleich, da sich activeEffect mit der Tiefe des Aufrufstapels ändert behoben; 🎜
  • 🎜 3. Abhängigkeitssammlung im Zusammenhang mit 🎜🎜1. So lösen Sie die Abhängigkeitssammlung aus 🎜🎜In der Nebeneffektfunktion löst obj die Abhängigkeitssammlung aus 🎜Der Triggereintrag befindet sich im Get-Interceptor🎜rrreee🎜2. Track-Quellcode🎜rrreee🎜shouldTrack ist auch oben erwähnt. Seine Funktion besteht darin, zu steuern, ob Abhängigkeiten gesammelt werden sollen;🎜🎜activeEffect Es ist das ReactiveEffect-Objekt, über das wir gerade gesprochen haben zeigt auf die Nebeneffektfunktion, die gerade ausgeführt wird. Die Funktion der 🎜🎜track-Methode besteht darin, Abhängigkeiten zu sammeln, nämlich das Ziel und den Schlüssel in der 🎜🎜targetMap zu erfassen ist Ziel und sein Wert ist eine Karte, sein Schlüssel ist Schlüssel und sein Wert ist ein Set 🎜🎜Der strukturelle Pseudocode von targetMap lautet wie folgt: 🎜rrreee🎜So nutzen Sie den Effekt des reaktionsfähigen Vue3-Kerns🎜🎜Das Obige ist die originellste depMap🎜🎜Die Entwicklungsumgebung wird zunehmen, wenn reaktionsfähiges Debugging hinzugefügt wird<-Code >eventInfo🎜rrreee🎜eventInfo hat die folgende Struktur: 🎜🎜Vue3 Antwort So verwenden Sie den Effekt in der Kernformel🎜🎜trackEffects(dep, eventInfo)🎜🎜Wenn in dep kein aktuelles ReactiveEffect-Objekt vorhanden ist, wird es hinzugefügt und der Effekt wird angezeigt Die Eigenschaftsoperation und Nebeneffektfunktion des Objekts Stellen Sie die Zuordnung her und schauen Sie sich dann trackEffects an🎜🎜3, trackEffects(dep, eventInfo) Quellcode-Interpretation🎜rrreee🎜dep.add(activeEffect! ) Wenn kein aktueller ReactiveEffect in dep vorhanden ist, wird das Objekt hinzugefügt🎜🎜So nutzen Sie den Effekt des Vue3 Responsive Core🎜🎜Die endgültig generierte depTarget-Struktur lautet wie folgt:🎜🎜Vue3 Responsive Core Effect So verwenden Sie 🎜🎜4. Abhängigkeiten auslösen🎜🎜Zum Beispiel löst der Code im Beispiel obj.count++ das Abfangen von Sätzen aus und Abhängigkeitsaktualisierungen auslösen🎜
    function createSetter(shallow = false) {
      return function set(
        target: object,
        key: string | symbol,
        value: unknown,
        receiver: object
      ): boolean {
        //...
        const result = Reflect.set(target, key, value, receiver)
        // don&#39;t trigger if target is something up in the prototype chain of original
        if (target === toRaw(receiver)) {
          if (!hadKey) {
            trigger(target, TriggerOpTypes.ADD, key, value) // 触发ADD依赖更新
          } else if (hasChanged(value, oldValue)) {
            trigger(target, TriggerOpTypes.SET, key, value, oldValue) //触发SET依赖更新
          }
        }
        //...
      }
    Nach dem Login kopieren
    Nach dem Login kopieren

    1、trigger依赖更新

    // 路径:packages/reactivity/src/effect.ts
    export function trigger(
      target: object,
      type: TriggerOpTypes,
      key?: unknown,
      newValue?: unknown,
      oldValue?: unknown,
      oldTarget?: Map<unknown, unknown> | Set<unknown>
    ) {
      const depsMap = targetMap.get(target) // 获取depsMap, targetMap是在track中创建的依赖
      if (!depsMap) {
        // never been tracked
        return
      }
    
      let deps: (Dep | undefined)[] = []
      if (type === TriggerOpTypes.CLEAR) {
        // collection being cleared
        // trigger all effects for target
        deps = [...depsMap.values()]
      } else if (key === &#39;length&#39; && isArray(target)) {
        const newLength = Number(newValue)
        depsMap.forEach((dep, key) => {
          if (key === &#39;length&#39; || key >= newLength) {
            deps.push(dep)
          }
        })
      } else {
        // schedule runs for SET | ADD | DELETE
        if (key !== void 0) {
          deps.push(depsMap.get(key))
        }
    
        // also run for iteration key on ADD | DELETE | Map.SET
        switch (type) {
          case TriggerOpTypes.ADD:
            if (!isArray(target)) {
              deps.push(depsMap.get(ITERATE_KEY))
              if (isMap(target)) {
                deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))
              }
            } else if (isIntegerKey(key)) {
              // new index added to array -> length changes
              deps.push(depsMap.get(&#39;length&#39;))
            }
            break
          case TriggerOpTypes.DELETE:
            if (!isArray(target)) {
              deps.push(depsMap.get(ITERATE_KEY))
              if (isMap(target)) {
                deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))
              }
            }
            break
          case TriggerOpTypes.SET:
            if (isMap(target)) {
              deps.push(depsMap.get(ITERATE_KEY))
            }
            break
        }
      }
    
      const eventInfo = __DEV__
        ? { target, type, key, newValue, oldValue, oldTarget }
        : undefined
    
      if (deps.length === 1) {
        if (deps[0]) {
          if (__DEV__) {
            triggerEffects(deps[0], eventInfo)
          } else {
            triggerEffects(deps[0])
          }
        }
      } else {
        const effects: ReactiveEffect[] = []
        for (const dep of deps) {
          if (dep) {
            effects.push(...dep)
          }
        }
        if (__DEV__) {
          triggerEffects(createDep(effects), eventInfo)
        } else {
          triggerEffects(createDep(effects))
        }
      }
    }
    Nach dem Login kopieren
    Nach dem Login kopieren

    const depsMap = targetMap.get(target) 获取 targetMap 中的 depsMap targetMap结构如下:

    So nutzen Sie den Effekt des Vue3-Reaktionskerns

    执行以上语句之后的depsMap结构如下:

    So nutzen Sie den Effekt des Vue3-Reaktionskerns

    将 depsMap 中 key 对应的 ReactiveEffect 对象添加到 deps 中deps.push(depsMap.get(key))之后的deps结构如下:

    So nutzen Sie den Effekt des Vue3-Reaktionskerns

    triggerEffects(deps[0], eventInfo)

      const eventInfo = __DEV__
        ? { target, type, key, newValue, oldValue, oldTarget }
        : undefined
      if (deps.length === 1) {
        if (deps[0]) {
          if (__DEV__) {
            triggerEffects(deps[0], eventInfo)
          } else {
            triggerEffects(deps[0])
          }
        }
      }
    Nach dem Login kopieren
    Nach dem Login kopieren

    trigger函数的作用就是触发依赖,当我们修改数据的时候,就会触发依赖,然后执行依赖中的副作用函数。

    在这里的实现其实并没有执行,主要是收集一些需要执行的副作用函数,然后在丢给triggerEffects函数去执行,接下来看看triggerEffects函数。

    2、triggerEffects(deps[0], eventInfo)

    export function triggerEffects(
      dep: Dep | ReactiveEffect[],
      debuggerEventExtraInfo?: DebuggerEventExtraInfo
    ) {
      // spread into array for stabilization
      const effects = isArray(dep) ? dep : [...dep]
      for (const effect of effects) {
        if (effect.computed) {
          triggerEffect(effect, debuggerEventExtraInfo)
        }
      }
      for (const effect of effects) {
        if (!effect.computed) {
          triggerEffect(effect, debuggerEventExtraInfo)
        }
      }
    }
    Nach dem Login kopieren
    Nach dem Login kopieren

    主要步骤

    const effects = isArray(dep) ? dep : [...dep]获取effects

    So nutzen Sie den Effekt des Vue3-Reaktionskerns

    triggerEffect(effect, debuggerEventExtraInfo)执行effect,接下来看看源码

    3、triggerEffect(effect, debuggerEventExtraInfo)

    function triggerEffect(
      effect: ReactiveEffect,
      debuggerEventExtraInfo?: DebuggerEventExtraInfo
    ) {
      if (effect !== activeEffect || effect.allowRecurse) {
         // 如果 effect.onTrigger 存在,就会执行,只有开发模式下才会执行
        if (__DEV__ && effect.onTrigger) {
          effect.onTrigger(extend({ effect }, debuggerEventExtraInfo))
        }
        // 如果 effect 是一个调度器,就会执行 scheduler
        if (effect.scheduler) {
          effect.scheduler()
        } else {
          // 其它情况执行 effect.run()
          effect.run()
        }
      }
    }
    Nach dem Login kopieren
    Nach dem Login kopieren

    effect.run()就是执行副作用函数

    Das obige ist der detaillierte Inhalt vonSo nutzen Sie den Effekt des Vue3-Reaktionskerns. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Erklärung dieser Website
    Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

    Heiße KI -Werkzeuge

    Undresser.AI Undress

    Undresser.AI Undress

    KI-gestützte App zum Erstellen realistischer Aktfotos

    AI Clothes Remover

    AI Clothes Remover

    Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

    Undress AI Tool

    Undress AI Tool

    Ausziehbilder kostenlos

    Clothoff.io

    Clothoff.io

    KI-Kleiderentferner

    AI Hentai Generator

    AI Hentai Generator

    Erstellen Sie kostenlos Ai Hentai.

    Heißer Artikel

    R.E.P.O. Energiekristalle erklärten und was sie tun (gelber Kristall)
    3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O. Beste grafische Einstellungen
    3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O. So reparieren Sie Audio, wenn Sie niemanden hören können
    3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌

    Heiße Werkzeuge

    Notepad++7.3.1

    Notepad++7.3.1

    Einfach zu bedienender und kostenloser Code-Editor

    SublimeText3 chinesische Version

    SublimeText3 chinesische Version

    Chinesische Version, sehr einfach zu bedienen

    Senden Sie Studio 13.0.1

    Senden Sie Studio 13.0.1

    Leistungsstarke integrierte PHP-Entwicklungsumgebung

    Dreamweaver CS6

    Dreamweaver CS6

    Visuelle Webentwicklungstools

    SublimeText3 Mac-Version

    SublimeText3 Mac-Version

    Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

    vue3+vite: So beheben Sie den Fehler bei der Verwendung von require zum dynamischen Importieren von Bildern in src vue3+vite: So beheben Sie den Fehler bei der Verwendung von require zum dynamischen Importieren von Bildern in src May 21, 2023 pm 03:16 PM

    vue3+vite:src verwendet „require“, um Bilder dynamisch zu importieren, und vue3+vite importiert dynamisch mehrere Bilder. Wenn Sie „requireisnotdefined“ verwenden, wird eine Fehlermeldung angezeigt like vue2 like imgUrl:require(' .../assets/test.png') wird importiert, da Typescript Require nicht unterstützt, daher wird Import verwendet. So lösen Sie das Problem: Verwenden Sieawaitimport

    So verwenden Sie tinymce im Vue3-Projekt So verwenden Sie tinymce im Vue3-Projekt May 19, 2023 pm 08:40 PM

    tinymce ist ein voll funktionsfähiges Rich-Text-Editor-Plug-in, aber die Einführung von tinymce in Vue ist nicht so reibungslos wie bei anderen Vue-Rich-Text-Plug-ins. Tinymce selbst ist nicht für Vue geeignet, und @tinymce/tinymce-vue muss eingeführt werden. und Es handelt sich um ein ausländisches Rich-Text-Plug-in, das die chinesische Version nicht bestanden hat. Sie müssen das Übersetzungspaket von der offiziellen Website herunterladen (möglicherweise müssen Sie die Firewall umgehen). 1. Installieren Sie die zugehörigen Abhängigkeiten npminstalltinymce-Snpminstall@tinymce/tinymce-vue-S2. 3. Führen Sie den Skin und das chinesische Paket ein. Erstellen Sie einen neuen Tinymce-Ordner im öffentlichen Ordner des Projekts und laden Sie ihn herunter

    Wie Vue3 Markdown analysiert und Code-Hervorhebung implementiert Wie Vue3 Markdown analysiert und Code-Hervorhebung implementiert May 20, 2023 pm 04:16 PM

    Um das Blog-Frontend mit Vue zu implementieren, müssen Sie die Markdown-Analyse implementieren. Wenn Code vorhanden ist, müssen Sie die Code-Hervorhebung implementieren. Es gibt viele Markdown-Parsing-Bibliotheken für Vue, wie z. B. markdown-it, vue-markdown-loader, markiert, vue-markdown usw. Diese Bibliotheken sind alle sehr ähnlich. Hier wird Markiert verwendet, und highlights.js wird als Code-Hervorhebungsbibliothek verwendet. Die spezifischen Implementierungsschritte lauten wie folgt: 1. Installieren Sie abhängige Bibliotheken. Öffnen Sie das Befehlsfenster unter dem Vue-Projekt und geben Sie den folgenden Befehl ein: npminstallmarked-save//marked, um Markdown in htmlnpmins zu konvertieren

    So aktualisieren Sie einen Teilinhalt der Seite in Vue3 So aktualisieren Sie einen Teilinhalt der Seite in Vue3 May 26, 2023 pm 05:31 PM

    Um eine teilweise Aktualisierung der Seite zu erreichen, müssen wir nur das erneute Rendern der lokalen Komponente (dom) implementieren. In Vue lässt sich dieser Effekt am einfachsten mit der v-if-Direktive erzielen. In Vue2 können wir zusätzlich zur Verwendung der v-if-Anweisung zum erneuten Rendern des lokalen Doms auch eine neue leere Komponente erstellen. Wenn wir die lokale Seite aktualisieren müssen, springen wir zu dieser leeren Komponentenseite und springen dann wieder hinein der beforeRouteEnter-Schutz in der leeren Komponente. Wie in der Abbildung unten gezeigt, wie man in Vue3.X auf die Schaltfläche „Aktualisieren“ klickt, um das DOM im roten Feld neu zu laden und den entsprechenden Ladestatus anzuzeigen. Da der Guard in der Komponente in der scriptsetup-Syntax in Vue3.X nur o hat

    So verwenden Sie wiederverwendbare Vue3-Komponenten So verwenden Sie wiederverwendbare Vue3-Komponenten May 20, 2023 pm 07:25 PM

    Vorwort Ob Vue oder React: Wenn wir auf mehrere wiederholte Codes stoßen, werden wir darüber nachdenken, wie wir diese Codes wiederverwenden können, anstatt eine Datei mit einer Reihe redundanter Codes zu füllen. Tatsächlich können sowohl Vue als auch React eine Wiederverwendung durch Extrahieren von Komponenten erreichen. Wenn Sie jedoch auf einige kleine Codefragmente stoßen und keine andere Datei extrahieren möchten, kann React im Vergleich dazu verwendet werden Deklarieren Sie das entsprechende Widget in der Datei , oder implementieren Sie es über die Renderfunktion, wie zum Beispiel: constDemo:FC=({msg})=>{returndemomsgis{msg}}constApp:FC=()=>{return(

    So wählen Sie einen Avatar aus und schneiden ihn in Vue3 zu So wählen Sie einen Avatar aus und schneiden ihn in Vue3 zu May 29, 2023 am 10:22 AM

    Der letzte Effekt besteht darin, die VueCropper-Komponente „garnaddvue-cropper@next“ zu installieren. Wenn es sich um Vue3 handelt oder Sie andere Methoden als Referenz verwenden möchten, besuchen Sie bitte die offizielle npm-Adresse. Es ist auch sehr einfach, es in einer Komponente zu referenzieren und zu verwenden. Sie müssen nur die entsprechende Komponente und ihre Stildatei einführen. Ich verweise hier nicht global, sondern nur auf import{userInfoByRequest}from'../js/api ' in meiner Komponentendatei. import{VueCropper}from'vue-cropper&

    So verwenden Sie defineCustomElement zum Definieren von Komponenten in Vue3 So verwenden Sie defineCustomElement zum Definieren von Komponenten in Vue3 May 28, 2023 am 11:29 AM

    Verwenden von Vue zum Erstellen benutzerdefinierter Elemente. WebComponents ist ein Sammelname für eine Reihe webnativer APIs, die es Entwicklern ermöglichen, wiederverwendbare benutzerdefinierte Elemente (Customelements) zu erstellen. Der Hauptvorteil von benutzerdefinierten Elementen besteht darin, dass sie mit jedem Framework verwendet werden können, auch ohne Framework. Sie sind ideal, wenn Sie Endbenutzer ansprechen, die möglicherweise einen anderen Front-End-Technologie-Stack verwenden, oder wenn Sie die endgültige Anwendung von den Implementierungsdetails der verwendeten Komponenten entkoppeln möchten. Vue und WebComponents sind komplementäre Technologien und Vue bietet hervorragende Unterstützung für die Verwendung und Erstellung benutzerdefinierter Elemente. Sie können benutzerdefinierte Elemente in bestehende Vue-Anwendungen integrieren oder Vue zum Erstellen verwenden

    So kapseln Sie ECharts-Komponenten in vue3 So kapseln Sie ECharts-Komponenten in vue3 May 20, 2023 pm 03:22 PM

    1. Vorwort Bei der Frontend-Entwicklung müssen häufig ECharts-Diagramme zum Rendern von Dateninformationen verwendet werden. Die Entscheidung, ECharts-Komponenten zur Wiederverwendung zu kapseln, kann die Codemenge reduzieren und die Entwicklungseffizienz erhöhen. 2. Kapselung von ECharts-Komponenten Warum sollten wir Komponenten kapseln, um eine doppelte Arbeitslast zu vermeiden, die Wiederverwendbarkeit zu verbessern, die Codelogik klarer zu machen und die spätere Wartung des Projekts zu erleichtern? kann einem Team eine bessere und hierarchische Bedienung der gekapselten ECharts-Komponente ermöglichen und die folgenden Funktionen implementieren: Verwenden Sie die Komponente, um das Optionsattribut in ECharts zu übergeben. Manuelles/automatisches Festlegen der Diagrammgröße. Anpassen der Breite und Höhe des Diagramms, dynamische Anzeige

    See all articles