1. Vue3 compilation process

Template ==> ast === => render

Template ==> Abstract syntax tree ==> Render function

The call stack

Directory location: VUE-next/Packages/Runtime-core/SRC /component.ts

mountComponent —> setupComponent —> finishComponentSetup

MountComponent simplifies the process

Directory: packages \ runtime – core \ SRC \ the renderer ts

// 1. Create an instance
const instance = createComponentInstance(...) // Create a component instance
/ /...
// 2 component setup is similar to vue2_init merge merge options, // attribute initialization // data responsive // slot processing
setupComponent(instance)
/ /...
// Install render function side effect
setupRenderEffect()
Copy the code

Inside the finishComponent method

    // ...
    if(compile && Component.template && ! Component.render) {if (__DEV__) {
        startMeasure(instance, `compile`)}// Only the global version with the compiled version will have this step
      // (break point hit here step by step to see)
      Component.render = compile(Component.template, {  // Compile template to generate the render function
        isCustomElement: instance.appContext.config.isCustomElement,
        delimiters: Component.delimiters
      })
      if (__DEV__) {
        endMeasure(instance, `compile`)}}/ /...
Copy the code








About the dev-compiler command (see compilation optimizations for VUe3)

npm run dev
npm run dev-compiler
Copy the code

Open the localhost: 5000

Go to the Packages/template-Explorer directory

You can see the template compiled

1. Static node prompt

The following is a code generated string

const _Vue = Vue const { createVNode: Const _hoisted_1 = /*#__PURE__*/_createVNode("p", null, "1", -1 /* HOISTED */) const _hoisted_2 = /*#__PURE__*/_createVNode("p", null, "2", -1 /* HOISTED */) const _hoisted_3 = /*#__PURE__*/_createVNode("p", null, "3", -1 /* HOISTED */) const _hoisted_4 = /*#__PURE__*/_createVNode("p", null, "4", -1 /* HOISTED */) return function render(_ctx, _cache) { with (_ctx) { const { toDisplayString: _toDisplayString, createVNode: _createVNode, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock } = _Vue return (_openBlock(), _createBlock(_Fragment, null, [ _createVNode("p", null, _toDisplayString(counter), 1 /* TEXT */), _hoisted_1, _hoisted_2, _hoisted_3, _hoisted_4 ], 64 /* STABLE_FRAGMENT */)) } }Copy the code

2. Patch marking and dynamic property recording

The template

<div :title='hello'>Hello World! </div> <p>LALA~~</p>Copy the code
import { createVNode as _createVNode, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from "vue" const _hoisted_1 = /*#__PURE__*/_createVNode("p", null, "LALA~~", -1 /* HOISTED */) export function render(_ctx, _cache, $props, $setup, $data, $options) { return (_openBlock(), _createBlock(_Fragment, null, [ _createVNode("div", { title: _ctx.hello}, "hello World!", 8 /* PROPSCopy the code

If patchFlag is 8, only properties need to be updated to 0B1000

If patchFlag is 1, only dynamic text 0B0001 is available

PatchFlag 9 indicates that there are attributes and dynamic text that need to be updated and compared with patch 0B10011

CacheHandlers cache events to avoid duplication

Replace the template for

<div :title='hello'>Hello World! </div> <p @click="onclick">LALA~~</p>Copy the code
import { createVNode as _createVNode, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from "vue" export function render(_ctx, _cache, $props, $setup, $data, $options) { return (_openBlock(), _createBlock(_Fragment, null, [ _createVNode("div", { title: _ctx.hello }, "Hello World!", 8 /* PROPS */, ["title"]), _createVNode("p", { onClick: _cache [1] | | (_cache [1] = (... the args) = > (_ctx. Onclick args (...))) / / event will save to _cache [1]}, "LALA ~ ~"), 64 /* STABLE_FRAGMENT */)) }Copy the code

4, Code blocks have dynamic nodes that are saved (with arrays) for comparison when traversing

Create a block

_createBlock(_Fragment, null, [ _createVNode("div", { title: _ctx.hello }, "Hello World!", 8 /* PROPS */, ["title"]), _createVNode("p", { onClick: _cache [1] | | (_cache [1] = (... the args) = > (_ctx. Onclick args (...))) / / event will save to _cache [1]}, "LALA ~ ~"), 64 /* STABLE_FRAGMENT */)) }Copy the code

// Save dynamic nodes in the virtual node vNode property dynamicChildren (array)








Vue3 Asynchronous update policy

Starting Point:

The component update method is an effect update function

const setupRenderEffect: SetupRenderEffectFn = ( instance, initialVNode, container, anchor, parentSuspense, isSVG, Optimized) => {// create reactive effect for rendering instance.update = effect(function componentEffect() { / /... }, __DEV__ ? createDevEffectOptions(instance) : prodEffectOptions) }Copy the code
const prodEffectOptions = {
  scheduler: queueJob,  // This is where it happens
  // #1801, #2043 component render effects should allow recursive updates
  allowRecurse: true
}
Copy the code

1. Effect method

export function effect<T = any> (fn: () => T, options: ReactiveEffectOptions = EMPTY_OBJ) :ReactiveEffect<T> {
  if (isEffect(fn)) {
    fn = fn.raw
  }
  const effect = createReactiveEffect(fn, options)
  if(! options.lazy) { effect() }return effect
}
Copy the code

The impact of scheduler in Options is not found

function createReactiveEffect<T = any> (fn: () => T, options: ReactiveEffectOptions) :ReactiveEffect<T> {
  const effect = function reactiveEffect() :unknown {
    if(! effect.active) {return options.scheduler ? undefined : fn()
    }
    if(! effectStack.includes(effect)) { cleanup(effect)try {
        enableTracking()
        effectStack.push(effect)
        activeEffect = effect
        return fn()
      } finally {
        effectStack.pop()
        resetTracking()
        activeEffect = effectStack[effectStack.length - 1]}}}asReactiveEffect effect.id = uid++ effect.allowRecurse = !! options.allowRecurse effect._isEffect =true
  effect.active = true
  effect.raw = fn
  effect.deps = []
  effect.options = options // Put options here
  return effect
}
Copy the code

About trigger trigger updates

The trigger is found to use scheduler

  const run = (effect: ReactiveEffect) = > {
    if (__DEV__ && effect.options.onTrigger) {
      effect.options.onTrigger({
        effect,
        target,
        key,
        type,
        newValue,
        oldValue,
        oldTarget
      })
    }
    if (effect.options.scheduler) { // Found here
      effect.options.scheduler(effect)
    } else {
      effect()
    }
  }

  effects.forEach(run)
Copy the code

2. About executing the update task

Location: Packages run-time core SRC scheduler.ts

export function queueJob(job: SchedulerJob) {
  // the dedupe search uses the startIndex argument of Array.includes()
  // by default the search index includes the current job that is being run
  // so it cannot recursively trigger itself again.
  // if the job is a watch() callback, the search will start with a +1 index to
  // allow it recursively trigger itself - it is the user's responsibility to
  // ensure it doesn't end up in an infinite loop.
  if((! queue.length || ! queue.includes( job, isFlushing && job.allowRecurse ? flushIndex +1: flushIndex )) && job ! == currentPreFlushParentJob ) { queue.push(job)// Push effect to queue
    queueFlush()
  }
}

function queueFlush() { FlushJobs specifies the effect function stored in queue
  if(! isFlushing && ! isFlushPending) { isFlushPending =true
    currentFlushPromise = resolvedPromise.then(flushJobs)
  }
}
Copy the code








Vue3 patch process

1. First of all, vNode changes: Children can be arrays or text

DynamicChildren and dynamicProps record dynamic nodes and attribute comparison to reduce traversal operations

3, patchFlag dynamic content directory location: packages\shared\ SRC \ PatchFlags.ts

4. ShapeFlag Component shape (whether component or teleport)

shapeFlag

export const enum ShapeFlags {
  ELEMENT = 1,
  FUNCTIONAL_COMPONENT = 1 << 1.// 2 function components
  STATEFUL_COMPONENT = 1 << 2.// 4 Status components
  TEXT_CHILDREN = 1 << 3.// 8 text children
  ARRAY_CHILDREN = 1 << 4.// 16 array children
  SLOTS_CHILDREN = 1 << 5.// 32 slots for children
  TELEPORT = 1 << 6./ / transfer
  SUSPENSE = 1 << 7./ / suspense
  COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8.// The component that should be cached
  COMPONENT_KEPT_ALIVE = 1 << 9.// The cached component
  COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT
}

Copy the code

About patchKeyChildren Diff algorithm

packages\runtime-core\src\renderer.ts

1, pinch head 2, pinch tail 3, how directly insert the new 4, how directly delete the old 5, according to the old (way) traverse one by one findCopy the code