1.Vue architecture changes

compile-dom —> compile-core

reactivity

runtime-dom —> runtime-core

2,About the package

Vue is packaged with rollup

Run the rollup command with execa library

3. Packaging format:

Global –> umD –> iIFE auto-tuning function

CJS –> NodeJS environment

Es —-> ES6 environment

3,Source code initialization process

0, use (on why it is made this way, different from VUe2)

1. Avoid instance pollution

2, tree shaking optimization (previously many methods mount instances)

3. Better understanding semantically


    createApp({
      data() {
        return {
          counter: 1
        }
      }
    })
    .use(store)
    .use(router)
    .mount('#app')
Copy the code

The createApp method is in the Runtime-DOM module

export const createApp = ((. args) = > {
  // Get the renderer first
  CreateApp is actually called by the renderer
  constapp = ensureRenderer().createApp(... args)const { mount } = app
  
  app.mount = (containerOrSelector: Element | string): any= > {
    const container = normalizeContainer(containerOrSelector)
    if(! container)return
    / /... Compiling templates
    const proxy = mount(container)
    container.removeAttribute('v-cloak')
    container.setAttribute('data-v-app'.' ')
    return proxy
  }

  return app
}) as CreateAppFunction<Element>
Copy the code

2. Renderers

constapp = ensureRenderer().createApp(... args)// This method returns the renderer
ensureRenderer() 
/ / ensureRenderer () = > return the renderer | | createRenderer () / / singleton pattern
// If the renderer is created as a global variable, it has a value

/ / 2, createRenderer ({patchProp,... NodeOps}) create renderer (property comparison and replacement, DOM manipulation methods)
createRenderer() 
// return baseCreateRenderer ()

/ / 3.baseCreateRenderer({patchProp, ... nodeOps})// Directory location: packages run-time core SRC renderer.ts

  // baseCreateRenderer return result
  // return ==> {
  // render(vnode, container
  // Water injection is used for server rendering
  // createApp: createAppAPI(render, hydrate) returns a method
  / /}


// 4, createAppAPI(Render, hydrate) return app instance
createAppAPI(render, hydrate)
// Instances include {use, component, mixins, directive, mount, unmount}
// packages\runtime-core\src\apiCreateApp.ts

Copy the code

3, About mount

Location: Vue-next \ Packages \ Runtime-core \ SRC \ apicreateapp.ts (line 227)

We mainly did the following things

Const vNode = createVNode(rootComponent as ConcreteComponent, RootProps) // Get vnode vnode.appContext = context // HMR root reload hot update if (__DEV__) {context.reload = () => { render(cloneVNode(vnode), rootContainer) } } if (isHydrate && hydrate) { hydrate(vnode as VNode<Node, Element>, RootContainer, as any) / / server rendering} else {/ / the browser rendering -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - render (vnode, RootContainer)} isMounted = true app._container = rootContainer // Save the root nodeCopy the code

4, render method

The process (container._vnode) is different depending on whether there is a DOM

const render: RootRenderFunction = (vnode, container) => { if (vnode == null) { if (container._vnode) { unmount(container._vnode, Null, null, true)}} else {/ there/here / / parameter 1 / update/parameter 1 does not exist walk mount patch (container. _vnode | | null, vnode. Container)} flushPostFlushCbs() container._vnode = vnode // hang to dom}Copy the code

5. Patch compares virtual DOM updates, including initial rendering

Directory location: Packages \ Runtime-core \ SRC \renderer.ts

const patch: PatchFn = (
    n1, // Old virtual node
    n2, // New virtual node
    container,
    anchor = null,
    parentComponent = null,
    parentSuspense = null,
    isSVG = false,
    optimized = false
  ) = > {
    // patching & not the same type, unmount old tree Is not of the same root node type
    if(n1 && ! isSameVNodeType(n1, n2)) { anchor = getNextHostNode(n1) unmount(n1, parentComponent, parentSuspense,true)
      n1 = null
    }

    // Get the new node type
    const { type, ref, shapeFlag } = n2
    switch (type) {
      case Text: / / text
        processText(n1, n2, container, anchor)
        break
      case Comment: / / comment
        processCommentNode(n1, n2, container, anchor)
        break
      case Static: / / static
        if (n1 == null) {
          mountStaticNode(n2, container, anchor, isSVG)
        } else if (__DEV__) {
          patchStaticNode(n1, n2, container, isSVG)
        }
        break
      case Fragment: // Non-single root node
        processFragment(...)
        break
      default:
        if (shapeFlag & ShapeFlags.ELEMENT) {
          processElement(...)
        } else if (shapeFlag & ShapeFlags.COMPONENT) {
          // Initialization goes here
          processComponent(...)
        } else if(shapeFlag & ShapeFlags.TELEPORT) { ; (typeas typeof TeleportImpl).process(...) // Handle the portal
        } else if(__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) { ; (typeas typeofSuspenseImpl).process(...) }}// set ref sets ef
    if(ref ! =null && parentComponent) {
      setRef(ref, n1 && n1.ref, parentComponent, parentSuspense, n2)
    }
  }
Copy the code

Initial render steps:

Initialize the Component (processComponent) process (not fragment)

ProcessComponent => mountComponent Mounts the component for the first time

The process of patch components

const processComponent = ( n1: VNode | null, n2: VNode, container: RendererElement, anchor: RendererNode | null, parentComponent: ComponentInternalInstance | null, parentSuspense: SuspenseBoundary | null, isSVG: boolean, optimized: Boolean) => {if (n1 == null) {if (n2.shapeflag & ShapeFlags.COMPONENT_KEPT_ALIVE) {keepalive activation (parentComponent! .ctx as KeepAliveContext).activate()} else {// Initialize mountComponent()}} else {// Update updateComponent(n1, n2, optimized) } }Copy the code

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

7, the setupComponent (instance)

export function setupComponent( instance: Whether ComponentInternalInstance, isSSR = false / / SSR rendering) = {isInSSRComponentSetup isSSR const {props, children, shapeFlag } = instance.vnode const isStateful = shapeFlag & ShapeFlags.STATEFUL_COMPONENT initProps(instance, props, isStateful, isSSR) initSlots(instance, children) const setupResult = isStateful ? SetupStatefulComponent (instance, isSSR) : undefined isInSSRComponentSetup = false return setupResult}Copy the code
function setupStatefulComponent(instance: ComponentInternalInstance, isSSR: boolean) {
  const Component = instance.type as ComponentOptions

  // 0. create render proxy property access cache
  instance.accessCache = Object.create(null)
  // 1. create public instance / render proxy
  // also mark it raw so it's never observed
  // Get the context
  instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers) // Data responsive

  // 2. call setup() handles the setup function
  const { setup } = Component / / handle the setup
  if (setup) {
    const setupContext = (instance.setupContext =
      setup.length > 1 ? createSetupContext(instance) : null)

    currentInstance = instance
    pauseTracking()
    const setupResult = callWithErrorHandling(
      setup,
      instance,
      ErrorCodes.SETUP_FUNCTION,
      [__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext]
    )
    resetTracking()
    currentInstance = null

    if (isPromise(setupResult)) {
      if (isSSR) {
        // return the promise so server-renderer can wait on it
        return setupResult.then((resolvedResult: unknown) = > {
          handleSetupResult(instance, resolvedResult, isSSR)
        })
      } else if (__FEATURE_SUSPENSE__) {
        // async setup returned Promise.
        // bail here and wait for re-entry.
        instance.asyncDep = setupResult
      } else if (__DEV__) {
        warn(
          `setup() returned a Promise, but the version of Vue you are using ` +
            `does not support it yet.`)}}else {
      handleSetupResult(instance, setupResult, isSSR)
    }
  } else {
    // Setup is not set here
    finishComponentSetup(instance, isSSR)
  }
}
Copy the code

Both setup and data exist, and the setup value takes precedence

8, finishComponentSetup(instance, isSSR) this is still to be studied

Directory: packages \ runtime – core \ SRC \ component ts

9, setupRenderEffect installation side effects

The initial render and update components are separated

 const setupRenderEffect: SetupRenderEffectFn = (instance, initialVNode, container, anchor, parentSuspense, isSVG, optimized) = > {
    // Create reactive effect for rendering effect
    instance.update = effect(function componentEffect() {
      if(! instance.isMounted) {// Unmounted condition
        let vnodeHook: VNodeHook | null | undefined
        const { el, props } = initialVNode
        const { bm, m, parent } = instance

        // beforeMount hook life cycle
        if (bm) {
          invokeArrayFns(bm)
        }
        // onVnodeBeforeMount
        if ((vnodeHook = props && props.onVnodeBeforeMount)) {
          invokeVNodeHook(vnodeHook, parent, initialVNode)
        }

        // 1. Obtain the vNode of the current component
        const subTree = (instance.subTree = renderComponentRoot(instance))

        if (el && hydrateNode) {
           // SSR server rendering
          // vnode has adopted host node - perform hydration instead of mount.
          hydrateNode(
            initialVNode.el as Node,
            subTree,
            instance,
            parentSuspense
          )

        } else {
          // Run patch. If multiple nodes run fragments, run patch
          patch(
            null,
            subTree,
            container,
            anchor,
            instance,
            parentSuspense,
            isSVG
          )

          initialVNode.el = subTree.el
        }
        // mounted hook
        if (m) {
          queuePostRenderEffect(m, parentSuspense)
        }
        // onVnodeMounted
        if ((vnodeHook = props && props.onVnodeMounted)) {
          queuePostRenderEffect(() = >{ invokeVNodeHook(vnodeHook! , parent, initialVNode) }, parentSuspense) }// activated hook for keep-alive roots.
        // #1742 activated hook must be accessed after first render
        // since the hook may be injected by a child keep-alive
        const { a } = instance
        if (
          a &&
          initialVNode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
        ) {
          queuePostRenderEffect(a, parentSuspense)
        }
        instance.isMounted = true
      } else {
        // updateComponent
        // This is triggered by mutation of component's own state (next: null)
        // OR parent calling processComponent (next: VNode)
        let { next, bu, u, parent, vnode } = instance
        let originNext = next
        let vnodeHook: VNodeHook | null | undefined

        if (next) {
          next.el = vnode.el
          updateComponentPreRender(instance, next, optimized)
        } else {
          next = vnode
        }

        // beforeUpdate hook
        if (bu) {
          invokeArrayFns(bu)
        }
        // onVnodeBeforeUpdate
        if ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) {
          invokeVNodeHook(vnodeHook, parent, next, vnode)
        }


        const nextTree = renderComponentRoot(instance)
        if (__DEV__) {
          endMeasure(instance, `render`)}const prevTree = instance.subTree
        instance.subTree = nextTree

        patch(
          prevTree,
          nextTree,
          // parent may have changed if it's in a teleporthostParentNode(prevTree.el!) ! .// anchor may have changed if it's in a fragment
          getNextHostNode(prevTree),
          instance,
          parentSuspense,
          isSVG
        )
        / /...
  }
Copy the code