This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money

This article source version Vue3.2.11, Vue2 responsive source code analysis point here simple Vue2 responsive principle source code analysis

We know that Vue3 is a significant upgrade to Vue2. X’s responsiveness; There are many changes in Vue3.2 compared to version 3.0

Vue3 and Vue2 are different in response

Responsive performance improvements

Uvu released Vue3.2 on August 10.

  • More efficientrefImplementation, read about promotion260%, write about the promotion50%
  • Depending on the collection speed increase about40%
  • Reduce memory consumption by about17%

Differences in use

Vue2 automatically responds to properties written to the object returned by the data function in the component

Vue3 uses REF to define common type reactive and Reactive to define complex type reactive data

<script setup>
  import { ref, reactive, toRefs } from "vue"
  const name = ref('MuHua')
  const obj = reactive({ name: 'MuHua' })
  constdata = { ... toRefs(obj) } </script>Copy the code

Extension: ToRefs allows you to turn reactive objects into normal objects because reactive objects defined by Reactive elements are destroyed when they are deconstructed (expanded) or destroyed. The deconstruction is lost because there are so many properties in the Reactive instance. So toRefs can be used when you need to deconstruct and remain responsive

Source directory structure differences

The core of Vue2’s responsive source code is in the SRC /core/ Observer directory, but it also introduces a lot of other directory things, not independent, relatively high coupling degree

The Vue3 responsive source code is all in the Packages/Reactivity directory. It is not involved anywhere else. It is functional and distributed separately as an NPM package that can be integrated into other frameworks

A difference in principle

We know that reactive objects are implemented using Object.defineProperty in Vue2, and this approach has some drawbacks

  • Based on attribute interception, all attributes will be recursed in the initial localization, which will have certain impact on performance. In addition, new attributes added to the object later cannot trigger the response type. The corresponding solution is to passVue.set()Method to add a new property
  • Unable to detect changes inside the array, the solution was to override seven methods that would change the original array

Vue3 uses Proxy refactoring to completely replace defineProperty because Proxy can hijack the entire object and this problem doesn’t exist

So how are these problems solved?

object

See first Vue2 in rendering of reactive processing for the first time, the source address: SRC/core/observer/index. Js – 157 line

. Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { ... }, set: function reactiveSetter (newVal) { ... }})...Copy the code

Obj [key] = obj[key] = obj[key] = obj[key] = obj[key] That’s why the attributes added later are not responsive, right

This is the case with Vue3

/ / ref source ` packages/reactivity/ref. ` ts - 142
/ / reactive source ` packages/reactivity/reactive. ` ts - 173
new Proxy(target,{  // target is the object returned by the component's data
  get(target, key){},
  set(target, key, value){}})Copy the code

As you can see from the parameters, you don’t need to know what fields are in the object when you start creating a response, because you don’t need to pass a specific key, so you can intercept the fields that are added later

That means you don’t recursively iterate over everything that you don’t use and set it to be reactive, which speeds up the first rendering

An array of

In Vue2

  • One is becauseObject.definePropertyThe API does not listen for changes in array length
  • And two, because arrays can be very long, for examplelenthThe Vue itself is not designed to listen for direct subscript modification of array elements, especially for performance and user experience

To extend the question, why can’t we listen for changes in array length? Look at the picture first

As shown in the figure, the corresponding value can be changed only if the 64x is true, and therefore can be monitored. The length of the array cannot be monitored if the length of the array changes

Any different browser vendors and JS engine do not allow any modifications of the Length 64x, which works without any additional control system. This works without any additional control system

/ / ` SRC/core/observer/index. ` js - 144
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
    return
}
Copy the code

So, in order to better operating array and triggers the response type, will rewrite the 7 method, will change the original array by ob. Dep., notify () distributed update manually, source address: SRC/core/observer/array. Js

Use Proxy in Vue3. Proxy stands for Proxy. Review the syntax

new Proxy(target,{
  get(target, key){},
  set(target, key, value){}})Copy the code

According to the description of Proxy in MDN, this is true

  • target: objects virtualized by Proxy. It is often used as the storage back end of an agent. Verify about the object against the targetUnextensible or unconfigurable propertiesInvariant of (invariant semantics)

Note that the length of the array is a non-configurable property, so the Proxy naturally listens for array length changes

Dependency collection difference

Vue2 is through the Observer, Dep, Watcher these three classes to achieve the dependency collection, the detailed process can see my other article in depth Vue responsive principle source code analysis

Vue3 collects dependencies through track and triggers updates through trigger. In essence, it is realized by WeakMap, Map and Set. For details, you can see the implementation process of the source code below

Disadvantages difference

In Vue2, defineProperty does not monitor the internal changes of new object attributes/arrays. If the attribute value is an object, the observe() recursive traversal will be called many times. In addition, it will monitor all data attributes, including those that are not used

Vue3 is mostly about using new Es6+ features, which are not as compatible on older browsers

Vue3 responsive source code parsing

Take a look at the several flags defined in Vue3 to mark the type of the target object, starting with the enumerated properties

Source address: packages/reactivity/reactive. Ts – 16 rows

export const enum ReactiveFlags {
  SKIP = '__v_skip',
  IS_REACTIVE = '__v_isReactive',
  IS_READONLY = '__v_isReadonly',
  RAW = '__v_raw'
}
exportinterface Target { [ReactiveFlags.SKIP]? : boolean// Data that is not processed in a responsive manner[ReactiveFlags.IS_REACTIVE]? : boolean// Target is responsive[ReactiveFlags.IS_READONLY]? : boolean// Target is read-only[ReactiveFlags.RAW]? : any// Indicates the source data corresponding to proxy. This attribute is available when target is already a proxy object
}
Copy the code

And then start to parse it out

reactive()

Source address: packages/reactivity/SRC/reactive. Ts – 87

export function reactive<T extends object> (target: T) :UnwrapNestedRefs<T>
export function reactive(target: object) {/ / iftargetObjects of read-only type are returned directlyif (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
    return target
  }
  return createReactiveObject(
    target, // Create a responsive target object, data
    false.// Not read-only
    mutableHandlers,
    mutableCollectionHandlers,
    reactiveMap // const reactiveMap = new WeakMap<Target, any>())}Copy the code

The code here is very simple and basically calls createReactiveObject()

createReactiveObject()

Source address: packages/reactivity/SRC/reactive. Ts – 173

function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<any>,
  proxyMap: WeakMap<Target, any>
) {
  // Typeof is not object and is returned directly
  if(! isObject(target)) {if (__DEV__) console.warn(`value cannot be made reactive: The ${String(target)}`)
    return target
  }
  // If it is already responsive, return it directly
  if( target[ReactiveFlags.RAW] && ! (isReadonly && target[ReactiveFlags.IS_REACTIVE])) {return target
  }
  // If it already exists in the map, return it directly
  const existingProxy = proxyMap.get(target)
  if (existingProxy) {
    return existingProxy
  }
  // Do not do reactive, direct return
  const targetType = getTargetType(target)
  if (targetType === TargetType.INVALID) {
    return target
  }
  // Change target to proxy
  const proxy = new Proxy(
    target,
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
  )
  // Add to map
  proxyMap.set(target, proxy)
  return proxy
}
Copy the code

Now that we know what we’re going to do in this method, what are the parameters that we’re passing in

The parameter configuration definition looks like this

const get = /*#__PURE__*/ createGetter()
const set = /*#__PURE__*/ createSetter()
export const mutableHandlers: ProxyHandler<object> = {
  get, // Get attributes
  set, // Modify attributes
  deleteProperty, // Delete attributes
  has, // Whether an attribute is owned
  ownKeys // Collect keys, including symbol type or non-enumerable keys
}
Copy the code

Track () set and deleteProperty trigger update trigger()

Two important methods are createGetter and createSetter for get and set

createGetter()

Source address: packages/reactivity/SRC/baseHandlers. Ts – 80

function createGetter(isReadonly = false, shallow = false) {
  return function get(target: Target, key: string | symbol, receiver: object) {
    // Access the corresponding flag bit
    if (key === ReactiveFlags.IS_REACTIVE) {
      return! isReadonly }else if (key === ReactiveFlags.IS_READONLY) {
      return isReadonly
    } else if (
      // Receiver points to the caller to ensure that it is the proxy itself that triggers the interception handle and not its heirs
      // There are two ways to trigger a block: one is to access the properties of the proxy object itself, and the other is to access the properties of the object with the proxy object in the prototype chain, because the query will look down the prototype chain
      key === ReactiveFlags.RAW &&
      receiver ===
        (isReadonly
          ? shallow ? shallowReadonlyMap : readonlyMap
          : shallow ? shallowReactiveMap : reactiveMap
        ).get(target)
    ) {
      // Return target itself, the original value of the reactive object
      return target
    }
    // Is an array
    const targetIsArray = isArray(target)
    && is not a read-only type && is an array && triggers a method in the arrayInstrumentations tool set
    if(! isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {ArrayInstrumentations [key] this must point to the proxy
      return Reflect.get(arrayInstrumentations, key, receiver)
    }
    // proxy prereturns a value
    const res = Reflect.get(target, key, receiver)
    // The key is symbol or accesses the __proto__ attribute
    if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
      return res
    }
    // Target that is not read-only collects dependencies. Because read-only types don't change, you can't fire setters, so you fire updates
    if(! isReadonly) {// Collect dependencies and store them in the corresponding global repository
      track(target, TrackOpTypes.GET, key)
    }
    Reactive () is not called recursively when an object has an attribute value.
    if (shallow) {
      return res
    }
    // The attribute accessed is already the ref object
    if (isRef(res)) {
      // Return ref. Value, except for arrays
      constshouldUnwrap = ! targetIsArray || ! isIntegerKey(key)return shouldUnwrap ? res.value : res
    }
    // Since the proxy can only delegate one layer, if the child elements are objects, the proxy needs to continue recursively
    if (isObject(res)) {
      return isReadonly ? readonly(res) : reactive(res)
    }

    return res
  }
}
Copy the code

Track () relies on the collection to be placed later, along with the distribution of updates

createSetter()

Source address: packages/reactivity/SRC/baseHandlers. Ts – 80

function createSetter(shallow = false) {
  return function set(target: object, key: string | symbol, value: unknown, receiver: object) :boolean {
    let oldValue = (target as any)[key]
    if(! shallow) {// Take the original value of the new value and the old value, because the new value may be reactive data, and there is no point in comparing it directly with the original value on target
      value = toRaw(value)
      oldValue = toRaw(oldValue)
      // The old value is ref and the new value is not ref
      if(! isArray(target) && isRef(oldValue) && ! isRef(value)) { oldValue.value = valuereturn true}}else {
      // in shallow mode, objects are set as-is regardless of reactive or not
    }
    // Get the key value
    const hadKey =
      isArray(target) && isIntegerKey(key)
        ? Number(key) < target.length
        : hasOwn(target, key)
    // Target [key] = value
    const result = Reflect.set(target, key, value, receiver)
    // Receiver is a proxy instance that dispatches updates to prevent interceptors from triggering updates via the prototype chain
    if (target === toRaw(receiver)) {
      if(! hadKey) {// If the target does not have a key, it is added
        trigger(target, TriggerOpTypes.ADD, key, value)
      } else if (hasChanged(value, oldValue)) {
        // If the old and new values are not equal
        trigger(target, TriggerOpTypes.SET, key, value, oldValue)
      }
    }
    return result
  }
}
Copy the code

Trigger () dispatches updates later

Get () and reflect.set () instead of target[key].

Set () returns a Boolean, for example reflect.set () returns a Boolean on whether the modification was successful, target[key] = newValue, and does not return true. And no matter how the Proxy modifies the default behavior, you can get the default behavior through Reflect. The get () in the same way

The next step is to rely on the collection and distribution of updates related to the core content, the relevant code is all in the effect. Ts file, which deals with some side effects, the main content is as follows:

  • Create the effect entry function
  • Track relies on collecting
  • Trigger send out updates
  • CleanupEffect removal effect
  • Stop stop effect
  • TrackStack Collection stack pauseTracking, recovery (enableTracking) and resetTracking

Let’s start with the entry function

effect()

Source address: packages/reactivity/SRC/effect. The ts – 145

The main thing here is to expose a method for creating an effect

export function effect<T = any> (fn: () => T, options? : ReactiveEffectOptions) :ReactiveEffectRunner {
  // If it is already an effect function, take the original one
  if ((fn as ReactiveEffectRunner).effect) {
    fn = (fn as ReactiveEffectRunner).effect.fn
  }
  / / create the effect
  const _effect = new ReactiveEffect(fn)
  if (options) {
    extend(_effect, options)
    if (options.scope) recordEffectScope(_effect, options.scope)
  }
  // Effect is executed once if lazy is not true. The lazy of the evaluated attribute is true
  if(! options || ! options.lazy) { _effect.run() }/ / return
  const runner = _effect.run.bind(_effect) as ReactiveEffectRunner
  runner.effect = _effect
  return runner
}
Copy the code

The main thing is that new ReactiveEffect creates an _effect instance in effect, and the runner method returned by the function is the run method in ReactiveEffect

This shows that when the effect method is executed, the run method is actually executed

So we need to know what’s going on in this ReactiveEffect and the run method that it returns

Let’s move on

ReactiveEffect

Source address: packages/reactivity/SRC/effect. The ts line – 53

The main work here is to use the stack data structure effectStrack to debug effect execution before dependency collection to ensure that the current effect has the highest priority and clear the memory of dependency collection in time

Fn () is the enclosing function of the side effect function. If it is a component rendering function, fn() is the component rendering function. When it is executed, it accesses the data and fires the getter for target[key]. Then track is triggered for dependency collection, which is the dependency collection process of Vue3

// Temporarily store reactive functions
const effectStack: ReactiveEffect[] = []
// Rely on the collection stack
const trackStack: boolean[] = []
// Maximum nesting depth
const maxMarkerBits = 30
export class ReactiveEffect<T = any> {
  active = true
  deps: Dep[] = [] computed? : boolean allowRecurse? : boolean onStop? :() = > void
  // dev onlyonTrack? :(event: DebuggerEvent) = > void
  // dev onlyonTrigger? :(event: DebuggerEvent) = > void
  constructor(
    public fn: () => T,
    public scheduler: EffectScheduler | null = null, scope? : EffectScope |null
  ) {
    // effectScope related processing, in a separate file, but here not much expansion
    recordEffectScope(this, scope)
  }
  run() {
    if (!this.active) {
      return this.fn()
    }
    // If there is no current effect in the stack
    if(! effectStack.includes(this)) {
      try {
        // activeEffect Specifies the effect currently being processed by the dependent collection system
        // set the current effect to a globally activeEffect. In the getter, all effects held by activeEffect are collected
        // Then push it
        effectStack.push((activeEffect = this))
        // Resume dependency collection because it is suspended while the setup function is running on its own
        enableTracking()
        // Record the recursive depth bits
        trackOpBit = 1 << ++effectTrackDepth
        // If the number of nesting layers does not exceed 30, it cannot be exceeded
        if (effectTrackDepth <= maxMarkerBits) {
          // To mark dependencies, we iterate over the DEps attribute in the _effect instance, marking the w attribute of each DEP as the value of trackOpBit
          initDepMarkers(this)}else {
          // Clear current effect-related dependencies when exceeded usually not
          cleanupEffect(this)}// When an effect function is executed, such as target[key], the getter is triggered
        return this.fn()
      } finally {
        if (effectTrackDepth <= maxMarkerBits) {
          // Complete the dependency tag
          finalizeDepMarkers(this)}// Revert to the previous level
        trackOpBit = 1 << --effectTrackDepth
        // Reset the dependency collection state
        resetTracking()
        / / out of the stack
        effectStack.pop()
        // Get the stack length
        const n = effectStack.length
        // Point the current activeEffect to the last effect on the stack
        activeEffect = n > 0 ? effectStack[n - 1] : undefined}}}stop() {
    if (this.active) {
      cleanupEffect(this)
      if (this.onStop) {
        this.onStop()
      }
      this.active = false}}}Copy the code

track()

Source address: packages/reactivity/SRC/effect. The ts – 188

Track is a dependency collector, responsible for collecting dependencies into a unified dependency management center

// targetMap is a dependency manager used to store mappings between responsive functions, target objects, and keys
// This is equivalent to this
// targetMap(weakmap)={
// target1(map):{
// key1(dep):[effect1,effect2]
// key2(dep):[effect1,effect2]
/ /}
// }
// Create a map for each target with a deP for each key
// Use deP to collect dependent functions, listen for key changes, and trigger dependent functions in deP
const targetMap = new WeakMap<any, KeyToDepMap>()
export function isTracking() {
  returnshouldTrack && activeEffect ! = =undefined
}
export function track(target: object, type: TrackOpTypes, key: unknown) {
  // If effect is not currently active, do not collect
  if(! isTracking()) {return
  }
  // Get the target from the dependency manager
  let depsMap = targetMap.get(target)
  if(! depsMap) {// If not, create one
    targetMap.set(target, (depsMap = new Map()))}// Get the deP set for key
  let dep = depsMap.get(key)
  if(! dep) {// Create one
    depsMap.set(key, (dep = createDep()))
  }
  // Development and non-development environments
  const eventInfo = __DEV__
    ? { effect: activeEffect, target, type, key }
    : undefined
  trackEffects(dep, eventInfo)
}
Copy the code

trackEffects()

Source address: packages/reactivity/SRC/effect. The ts – 212

Here, the currently active effects are collected into the corresponding effect set, that is, DEP

Here’s a look at two identifiers

Dep. n: n is short for newTracked, for whether or not it is latest collected (current level). Dep. w: W is short for wasTracked, for whether or not it has been collected, to avoid repeated collections

export function trackEffects(dep: Dep, debuggerEventExtraInfo? : DebuggerEventExtraInfo) {
  let shouldTrack = false
  If effect does not have more than 30 nested layers, as mentioned above
  if (effectTrackDepth <= maxMarkerBits) {
    if(! newTracked(dep)) {// Mark the new dependency
      dep.n |= trackOpBit
      // Already collected dependencies do not need to be collected againshouldTrack = ! wasTracked(dep) } }else {
    // If the dependency mode is exceeded, switch to clear dependency modeshouldTrack = ! dep.has(activeEffect!) }// If you can collect
  if (shouldTrack) {
    // Collect the currently active effects as dependencies
    dep.add(activeEffect!)
    // The currently active effect collects the DEP collectionactiveEffect! .deps.push(dep)// The onTrack event is triggered in the development environment
    if(__DEV__ && activeEffect! .onTrack) { activeEffect! .onTrack(Object.assign(
          {
            effect: activeEffect!
          },
          debuggerEventExtraInfo
        )
      )
    }
  }
}
Copy the code

trigger()

Source address: packages/reactivity/SRC/effect. The ts – 243

Trigger is the trigger corresponding to the dependencies collected by Track, that is, it is responsible for obtaining responsive functions according to the mapping relationship, and then sending notification triggerEffects to update

export function trigger(target: object, type: TriggerOpTypes, key? : unknown, newValue? : unknown, oldValue? : unknown, oldTarget? :Map<unknown, unknown> | Set<unknown>
) {
  // Get dependencies from the dependency manager
  const depsMap = targetMap.get(target)
  // The dependency that has not been collected is returned directly
  if(! depsMap) {return
  }

  let deps: (Dep | undefined=) [] []// The type passed in when the trigger is triggered is clear type
  if (type === TriggerOpTypes.CLEAR) {
    // Add all associated dependencies to the queue, ready to clear
    deps = [...depsMap.values()]
  } else if (key === 'length' && isArray(target)) {
    // If it is an array type and the length of the array changes
    depsMap.forEach((dep, key) = > {
      // If the array length becomes short, effects and trigger of the deleted array elements need to be done
      // The effects corresponding to the latest length element of the array are queued to be cleared
      if (key === 'length' || key >= (newValue as number)) {
        deps.push(dep)
      }
    })
  } else {
    // If the key is not undefined, add the corresponding dependency to the queue, such as add, modify, delete
    if(key ! = =void 0) {
      deps.push(depsMap.get(key))
    }
    // Add, modify, delete respectively
    switch (type) {
      case TriggerOpTypes.ADD: / / new.break
      case TriggerOpTypes.DELETE: / / delete.break
      case TriggerOpTypes.SET: / / modify.break}}// Get targetMap[target][key] and store it in deps
  TriggerEffects is used to extract the corresponding effect

  // Determine the development environment and pass 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])}}}else {
    const effects: ReactiveEffect[] = []
    for (const dep of deps) {
      if(dep) { effects.push(... dep) } }if (__DEV__) {
      triggerEffects(createDep(effects), eventInfo)
    } else {
      triggerEffects(createDep(effects))
    }
  }
}
Copy the code

triggerEffects()

Source address: packages/reactivity/SRC/effect. The ts – 330

Execute the effect function, which is the update in “distribute updates”

export function triggerEffects(dep: Dep | ReactiveEffect[], debuggerEventExtraInfo? : DebuggerEventExtraInfo) {
  // Iterate over the effect set function
  for (const effect of isArray(dep) ? dep : [...dep]) {
    /** effect! The activeEffect cannot be the same as the current effect. Count. Value ++, if it's an effect, fires the getter, track collects the currently active effect, and then count. Value = count. So filter the current effect */
    if(effect ! == activeEffect || effect.allowRecurse) {if (__DEV__ && effect.onTrigger) {
        effect.onTrigger(extend({ effect }, debuggerEventExtraInfo))
      }
      // If scheduler executes, the calculation property has scheduler
      if (effect.scheduler) {
        effect.scheduler()
      } else {
        // Execute the effect function
        effect.run()
      }
    }
  }
}
Copy the code

Create a ref

Source address: packages/reactivity/SRC/ref. Ts

So we’re going to start dealing with ref correlation, so let’s look at a couple of related functions, and we’ll use them later

// check if it is ref
export function isRef(r: any) :r is Ref {
  return Boolean(r && r.__v_isRef === true)}/ / create the ref
function createRef(rawValue: unknown, shallow: boolean) {
  if (isRef(rawValue)) { // If it is already ref, return it directly
    return rawValue
  }
  // Call RefImpl to create and return ref
  return new RefImpl(rawValue, shallow)
}
// Create a shallow ref
export function shallowRef(value? : unknown) {
  return createRef(value, true)}// Unload a ref
export function unref<T> (ref: T | Ref<T>) :T {
  return isRef(ref) ? (ref.value as any) : ref
}

Copy the code

RefImpl

Source address: packages/reactivity/SRC/ref. Ts – 87

The ref object is created by new RefImpl(). The RefImpl class is simpler to implement

class RefImpl<T> { private _value: T private _rawValue: T public dep? : Dep =undefined
  // Every ref instance has a read-only __v_isRef attribute that identifies it as a ref
  public readonly __v_isRef = true

  constructor(value: T, public readonly _shallow: boolean) {
    // Determine whether the comparison is shallow, if not, take the old value
    this._rawValue = _shallow ? value : toRaw(value)
    Call reactive(); // Call reactive();
    this._value = _shallow ? value : convert(value)
  }
  // ref.value Specifies the value
  get value() {
    // Do dependency collection
    trackRefValue(this)
    return this._value
  }
  set value(newVal) {
    // If the comparison is shallow, take the new value, not take the old value
    newVal = this._shallow ? newVal : toRaw(newVal)
    // Compare the old and new values
    if (hasChanged(newVal, this._rawValue)) {
      // The value is changed, and the value is reassigned
      this._rawValue = newVal
      this._value = this._shallow ? newVal : convert(newVal)
      // Send updates
      triggerRefValue(this, newVal)
    }
  }
}
Copy the code

trackRefValue()

Source address: packages/reactivity/SRC/ref. Ts – 29

Here we mainly do some work before the ref dependency collection, which is mainly to determine whether the effect is activated and whether the dependent effect has been collected. If not, we create a DEP and prepare for the collection, and then call the trackEffects above in this article for dependency collection

export function trackRefValue(ref: RefBase<any>) {
  // Collect if effect is enabled
  if (isTracking()) {
    ref = toRaw(ref)
    // If the property does not collect dependent functions, create a DEP to store dependent effects
    if(! ref.dep) { ref.dep = createDep() }// Development environment
    if (__DEV__) {
      trackEffects(ref.dep, {
        target: ref,
        type: TrackOpTypes.GET,
        key: 'value'})}else {
      // Call trackEffects above to collect dependencies
      trackEffects(ref.dep)
    }
  }
}
Copy the code

triggerRefValue()

Source address: packages/reactivity/SRC/ref. Ts – 47

Here is the ref distribution update, the source code is relatively simple, nothing to say, is to differentiate the development environment, and then execute the above triggerEffects to perform the corresponding effect function update

export function triggerRefValue(ref: RefBase
       
        , 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)
    }
  }
}
Copy the code

At this point, the source code analysis of Vue3’s reactive objects is basically done

Past wonderful

  • 12 Performance optimization tips for Vue development
  • Vue2 response type source code analysis
  • Simple virtual DOM and Diff algorithms, and Vue2 and Vue3 differences
  • Vue3 7 and Vue2 12 components communication, worth collecting
  • What are the latest updates to Vue3.2
  • JavaScript advanced knowledge
  • Front-end anomaly monitoring and DISASTER recovery
  • 20 minutes to help you learn HTTP and HTTPS, and consolidate your HTTP knowledge

conclusion

If this article is of any help to you, please give it a thumbs up. Thank you