渲染器

渲染器的作用是将虚拟DOM渲染为特定平台上的真实元素

虚拟DOM与真实DOM的结构一样,是由一个个虚拟节点VNode组成的树形结构。

渲染器把虚拟DOM节点渲染为真实DOM节点的过程叫作挂载

VNode

VNode的类型

常规的VNode包括TextCommentStaticFragment这几种类型。

1// 所有VNode类型
2export type VNodeTypes =
3  | string
4  | VNode
5  | Component
6  | typeof Text
7  | typeof Static
8  | typeof Comment
9  | typeof Fragment
10  | typeof TeleportImpl
11  | typeof SuspenseImpl
12
13export const Text = Symbol(__DEV__ ? 'Text' : undefined)  // 纯文本
14export const Comment = Symbol(__DEV__ ? 'Comment' : undefined) // 注释类
15export const Static = Symbol(__DEV__ ? 'Static' : undefined) // 静态html
16export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as {
17  __isFragment: true
18  new (): {
19    $props: VNodeProps
20  }
21}  // fragment html片段,根节点是多个节点

VNode的内容:

1const vnode: VNode = {
2  __v_isVNode: true,
3  [ReactiveFlags.SKIP]: true,
4  type,
5  props,
6  key: props && normalizeKey(props),
7  ref: props && normalizeRef(props),
8  scopeId: currentScopeId,
9  children: null,
10  component: null,
11  suspense: null,
12  ssContent: null,
13  ssFallback: null,
14  dirs: null,
15  transition: null,
16  el: null,
17  anchor: null,
18  target: null,
19  targetAnchor: null,
20  staticCount: 0,
21  shapeFlag,  // 用于
22  patchFlag,
23  dynamicProps,
24  dynamicChildren: null,
25  appContext: null
26}

createVNode位于@vue/runtime-core/vnode.ts,在生产环境调用了第317行的_createVNode,其代码如下:

1function _createVNode(
2  type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT,
3  props: (Data & VNodeProps) | null = null,
4  children: unknown = null,
5  patchFlag: number = 0,
6  dynamicProps: string[] | null = null,
7  isBlockNode = false
8): VNode {
9  if (!type || type === NULL_DYNAMIC_COMPONENT) {
10    type = Comment
11  }
12
13  if (isVNode(type)) {
14    // createVNode receiving an existing vnode. This happens in cases like
15    // <component :is="vnode"/>
16    // #2078 make sure to merge refs during the clone instead of overwriting it
17    const cloned = cloneVNode(type, props, true /* mergeRef: true */)
18    if (children) {
19      normalizeChildren(cloned, children)
20    }
21    return cloned
22  }
23
24  // class component normalization.
25  if (isClassComponent(type)) {
26    type = type.__vccOpts
27  }
28
29  // class & style normalization.
30  if (props) {
31    // for reactive or proxy objects, we need to clone it to enable mutation.
32    if (isProxy(props) || InternalObjectKey in props) {
33      props = extend({}, props)
34    }
35    let { class: klass, style } = props
36    if (klass && !isString(klass)) {
37      props.class = normalizeClass(klass)
38    }
39    if (isObject(style)) {
40      // reactive state objects need to be cloned since they are likely to be
41      // mutated
42      if (isProxy(style) && !isArray(style)) {
43        style = extend({}, style)
44      }
45      props.style = normalizeStyle(style)
46    }
47  }
48
49  // encode the vnode type information into a bitmap
50  const shapeFlag = isString(type)
51    ? ShapeFlags.ELEMENT
52    : __FEATURE_SUSPENSE__ && isSuspense(type)
53      ? ShapeFlags.SUSPENSE
54      : isTeleport(type)
55        ? ShapeFlags.TELEPORT
56        : isObject(type)
57          ? ShapeFlags.STATEFUL_COMPONENT
58          : isFunction(type)
59            ? ShapeFlags.FUNCTIONAL_COMPONENT
60            : 0
61
62  const vnode: VNode = {
63    // ... 省略VNode内容
64  }
65
66  normalizeChildren(vnode, children)
67
68  // normalize suspense children
69  if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
70    const { content, fallback } = normalizeSuspenseChildren(vnode)
71    vnode.ssContent = content
72    vnode.ssFallback = fallback
73  }
74
75  if (
76    shouldTrack > 0 &&
77    // avoid a block node from tracking itself
78    !isBlockNode &&
79    // has current parent block
80    currentBlock &&
81    // presence of a patch flag indicates this node needs patching on updates.
82    // component nodes also should always be patched, because even if the
83    // component doesn't need to update, it needs to persist the instance on to
84    // the next vnode so that it can be properly unmounted later.
85    (patchFlag > 0 || shapeFlag & ShapeFlags.COMPONENT) &&
86    // the EVENTS flag is only for hydration and if it is the only flag, the
87    // vnode should not be considered dynamic due to handler caching.
88    patchFlag !== PatchFlags.HYDRATE_EVENTS
89  ) {
90    currentBlock.push(vnode)
91  }
92
93  return vnode
94}

渲染器的基本结构

Vue3中渲染器被抽象成了一个对象,其TypeScript类型定义位于@vue/runtime-core/renderer.ts中:

1export interface Renderer<HostElement = RendererElement> {
2  render: RootRenderFunction<HostElement>
3  createApp: CreateAppFunction<HostElement>
4}

主要就是一个拥有rendercreateApp方法的对象。

Vue3中可以通过createRenderer自定义渲染器,浏览器平台的DOM渲染器就是自定义渲染器的一个实现。

1export function createRenderer<
2  HostNode = RendererNode,
3  HostElement = RendererElement
4>(options: RendererOptions<HostNode, HostElement>) {
5  return baseCreateRenderer<HostNode, HostElement>(options)
6}

createRenderer函数将自定义渲染器的依赖抽象成了RendererOptions,其TypeScript类型定义如下:

1export interface RendererOptions<
2  HostNode = RendererNode,
3  HostElement = RendererElement
4> {
5  patchProp(
6    el: HostElement,
7    key: string,
8    prevValue: any,
9    nextValue: any,
10    isSVG?: boolean,
11    prevChildren?: VNode<HostNode, HostElement>[],
12    parentComponent?: ComponentInternalInstance | null,
13    parentSuspense?: SuspenseBoundary | null,
14    unmountChildren?: UnmountChildrenFn
15  ): void
16  insert(el: HostNode, parent: HostElement, anchor?: HostNode | null): void
17  remove(el: HostNode): void
18  createElement(
19    type: string,
20    isSVG?: boolean,
21    isCustomizedBuiltIn?: string,
22    vnodeProps?: (VNodeProps & { [key: string]: any }) | null
23  ): HostElement
24  createText(text: string): HostNode
25  createComment(text: string): HostNode
26  setText(node: HostNode, text: string): void
27  setElementText(node: HostElement, text: string): void
28  parentNode(node: HostNode): HostElement | null
29  nextSibling(node: HostNode): HostNode | null
30  querySelector?(selector: string): HostElement | null
31  setScopeId?(el: HostElement, id: string): void
32  cloneNode?(node: HostNode): HostNode
33  insertStaticContent?(
34    content: string,
35    parent: HostElement,
36    anchor: HostNode | null,
37    isSVG: boolean,
38    start?: HostNode | null,
39    end?: HostNode | null
40  ): [HostNode, HostNode]
41}

mount

appmount方法中的rendercreateAppAPI作为参数传入,来源于@vue/runtime-core/renderer.ts中的baseCreateRenderer方法定义:

1const render: RootRenderFunction = (vnode, container) => {
2  if (vnode == null) {
3    if (container._vnode) {
4      unmount(container._vnode, null, null, true)
5    }
6  } else {
7    patch(container._vnode || null, vnode, container)
8  }
9  flushPostFlushCbs()  // TODO 待解析
10  container._vnode = vnode
11}

render方法主要有两个逻辑分支,如果vnode不为null时,为新建或者更新逻辑,调用patch方法;如果vnodenull,,并且container._vnode不为null,即vnode从有值变为null,调用unmount方法。

patch

patch函数是整个渲染器的核心入口,承载了最重要的渲染逻辑

patch的类型

不同类型的元素或组件,有不同的patch策略

1export const enum PatchFlags {
2  // 表明元素具有动态文本
3  TEXT = 1,
4  // 表明元素具有动态class绑定
5  CLASS = 1 << 1, // 2
6  // 表明元素具有动态样式style,编译器会将静态对象字符串编译为静态对象,并做变量提升优化
7  // 例如:
8  // style="color: red" 或 :style="{ color: 'red' }"
9  // 将被编译为:
10  // const style = { color: 'red' }
11  // render() { return e('div', { style }) }
12  STYLE = 1 << 2, // 4
13  // 表明元素或者组件具有class/style以外的动态属性props
14  // Can also be on a component that has any dynamic props (includes
15  // class/style). when this flag is present, the vnode also has a dynamicProps
16  // array that contains the keys of the props that may change so the runtime
17  // can diff them faster (without having to worry about removed props)
18  PROPS = 1 << 3, // 8
19  // Indicates an element with props with dynamic keys. When keys change, a full
20  // diff is always needed to remove the old key. This flag is mutually
21  // exclusive with CLASS, STYLE and PROPS.
22  FULL_PROPS = 1 << 4,
23
24  // Indicates an element with event listeners (which need to be attached
25  // during hydration)
26  HYDRATE_EVENTS = 1 << 5,
27
28  // Indicates a fragment whose children order doesn't change.
29  STABLE_FRAGMENT = 1 << 6,
30
31  // Indicates a fragment with keyed or partially keyed children
32  KEYED_FRAGMENT = 1 << 7,
33
34  // Indicates a fragment with unkeyed children.
35  UNKEYED_FRAGMENT = 1 << 8,
36
37  // Indicates an element that only needs non-props patching, e.g. ref or
38  // directives (onVnodeXXX hooks). since every patched vnode checks for refs
39  // and onVnodeXXX hooks, it simply marks the vnode so that a parent block
40  // will track it.
41  NEED_PATCH = 1 << 9,
42
43  // Indicates a component with dynamic slots (e.g. slot that references a v-for
44  // iterated value, or dynamic slot names).
45  // Components with this flag are always force updated.
46  DYNAMIC_SLOTS = 1 << 10,
47
48  // SPECIAL FLAGS -------------------------------------------------------------
49
50  // Special flags are negative integers. They are never matched against using
51  // bitwise operators (bitwise matching should only happen in branches where
52  // patchFlag > 0), and are mutually exclusive. When checking for a special
53  // flag, simply check patchFlag === FLAG.
54
55  // Indicates a hoisted static vnode. This is a hint for hydration to skip
56  // the entire sub tree since static content never needs to be updated.
57  HOISTED = -1,
58
59  // A special flag that indicates that the diffing algorithm should bail out
60  // of optimized mode. For example, on block fragments created by renderSlot()
61  // when encountering non-compiler generated slots (i.e. manually written
62  // render functions, which should always be fully diffed)
63  // OR manually cloneVNodes
64  BAIL = -2
65}

patch方法位于同文件的452行:

1const patch: PatchFn = (
2  n1, // prev vnode 
3  n2, // current vnode 
4  container,
5  anchor = null,
6  parentComponent = null,
7  parentSuspense = null,
8  isSVG = false,
9  optimized = false
10) => {
11  // patching & not same type, unmount old tree
12  if (n1 && !isSameVNodeType(n1, n2)) {
13    anchor = getNextHostNode(n1)
14    unmount(n1, parentComponent, parentSuspense, true)
15    n1 = null
16  }
17
18  if (n2.patchFlag === PatchFlags.BAIL) {
19    optimized = false
20    n2.dynamicChildren = null
21  }
22
23  const { type, ref, shapeFlag } = n2
24  switch (type) {
25    case Text:
26      processText(n1, n2, container, anchor) // 纯文本
27      break
28    case Comment:
29      processCommentNode(n1, n2, container, anchor)
30      break
31    case Static:
32      if (n1 == null) {
33        mountStaticNode(n2, container, anchor, isSVG)
34      } else if (__DEV__) {
35        patchStaticNode(n1, n2, container, isSVG)
36      }
37      break
38    case Fragment:
39      processFragment(
40        n1,
41        n2,
42        container,
43        anchor,
44        parentComponent,
45        parentSuspense,
46        isSVG,
47        optimized
48      )
49      break
50    default:
51      if (shapeFlag & ShapeFlags.ELEMENT) { // VNode 是普通标签
52        processElement(
53          n1,
54          n2,
55          container,
56          anchor,
57          parentComponent,
58          parentSuspense,
59          isSVG,
60          optimized
61        )
62      } else if (shapeFlag & ShapeFlags.COMPONENT) { // VNode 是组件
63        processComponent(
64          n1,
65          n2,
66          container,
67          anchor,
68          parentComponent,
69          parentSuspense,
70          isSVG,
71          optimized
72        )
73      } else if (shapeFlag & ShapeFlags.TELEPORT) {   
74        ;(type as typeof TeleportImpl).process(
75          n1 as TeleportVNode,
76          n2 as TeleportVNode,
77          container,
78          anchor,
79          parentComponent,
80          parentSuspense,
81          isSVG,
82          optimized,
83          internals
84        )
85      } else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
86        ;(type as typeof SuspenseImpl).process(
87          n1,
88          n2,
89          container,
90          anchor,
91          parentComponent,
92          parentSuspense,
93          isSVG,
94          optimized,
95          internals
96        )
97      }
98  }
99
100  // set ref
101  if (ref != null && parentComponent) {
102    setRef(ref, n1 && n1.ref, parentComponent, parentSuspense, n2)
103  }
104}

patch方法经过swtich语句调用了processComponent方法,其代码位于同文件的第1201行:

1const processComponent = (
2  n1: VNode | null,
3  n2: VNode,
4  container: RendererElement,
5  anchor: RendererNode | null,
6  parentComponent: ComponentInternalInstance | null,
7  parentSuspense: SuspenseBoundary | null,
8  isSVG: boolean,
9  optimized: boolean
10) => {
11  if (n1 == null) {
12    if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
13      (parentComponent!.ctx as KeepAliveContext).activate(
14        n2,
15        container,
16        anchor,
17        isSVG,
18        optimized
19      )
20    } else {
21      mountComponent(
22        n2,
23        container,
24        anchor,
25        parentComponent,
26        parentSuspense,
27        isSVG,
28        optimized
29      )
30    }
31  } else {
32    updateComponent(n1, n2, optimized)
33  }
34}

其主要有两个逻辑分支:旧的VNode``n1不为null时,调用更新组件的updateComponent方法;当n1null时,又有两个子分支:新的VNode``n1keep-alive的组件时,调用其ctxactivate方法,否则调用挂载组件的mountComponent方法。

我们先看挂载组件的mountComponent方法,其位于processComponent的下方:

1const mountComponent: MountComponentFn = (
2  initialVNode, // 初始的VNode
3  container,  // 挂载的容器
4  anchor,
5  parentComponent,
6  parentSuspense,
7  isSVG,
8  optimized
9) => {
10  const instance: ComponentInternalInstance = (initialVNode.component = createComponentInstance(
11    initialVNode,
12    parentComponent,
13    parentSuspense
14  ))
15
16  // inject renderer internals for keepAlive
17  if (isKeepAlive(initialVNode)) {
18    (instance.ctx as KeepAliveContext).renderer = internals
19  }
20
21  setupComponent(instance)
22
23  // setup() is async. This component relies on async logic to be resolved
24  // before proceeding
25  if (__FEATURE_SUSPENSE__ && instance.asyncDep) {
26    parentSuspense && parentSuspense.registerDep(instance, setupRenderEffect)
27
28    // Give it a placeholder if this is not hydration
29    // TODO handle self-defined fallback
30    if (!initialVNode.el) {
31      const placeholder = (instance.subTree = createVNode(Comment))
32      processCommentNode(null, placeholder, container!, anchor)
33    }
34    return
35  }
36
37  setupRenderEffect(
38    instance,
39    initialVNode,
40    container,
41    anchor,
42    parentSuspense,
43    isSVG,
44    optimized
45  )
46}

create

首先调用createComponentInstance方法生成组件的实例,先看下组件实例的构成:

1const instance: ComponentInternalInstance = {
2  uid: uid++,
3  vnode,
4  type,
5  parent,
6  appContext,
7  root: null!, // to be immediately set
8  next: null,
9  subTree: null!, // will be set synchronously right after creation
10  update: null!, // will be set synchronously right after creation
11  render: null,
12  proxy: null,
13  exposed: null,
14  withProxy: null,
15  effects: null,
16  provides: parent ? parent.provides : Object.create(appContext.provides),
17  accessCache: null!,
18  renderCache: [],
19
20  // local resovled assets
21  components: null,
22  directives: null,
23
24  // resolved props and emits options
25  propsOptions: normalizePropsOptions(type, appContext),
26  emitsOptions: normalizeEmitsOptions(type, appContext),
27
28  // emit
29  emit: null as any, // to be set immediately
30  emitted: null,
31
32  // state
33  ctx: EMPTY_OBJ,
34  data: EMPTY_OBJ,
35  props: EMPTY_OBJ,
36  attrs: EMPTY_OBJ,
37  slots: EMPTY_OBJ,
38  refs: EMPTY_OBJ,
39  setupState: EMPTY_OBJ,
40  setupContext: null,
41
42  // suspense related
43  suspense,
44  suspenseId: suspense ? suspense.pendingId : 0,
45  asyncDep: null,
46  asyncResolved: false,
47
48  // lifecycle hooks
49  // not using enums here because it results in computed properties
50  isMounted: false,
51  isUnmounted: false,
52  isDeactivated: false,
53  bc: null,
54  c: null,
55  bm: null,
56  m: null,
57  bu: null,
58  u: null,
59  um: null,
60  bum: null,
61  da: null,
62  a: null,
63  rtg: null,
64  rtc: null,
65  ec: null
66}

createComponentInstance方法的代码位于@vue/runtime-core/component.ts的第401行:

1export function createComponentInstance(
2  vnode: VNode,
3  parent: ComponentInternalInstance | null,
4  suspense: SuspenseBoundary | null
5) {
6  const type = vnode.type as ConcreteComponent
7  // inherit parent app context - or - if root, adopt from root vnode
8  const appContext =
9    (parent ? parent.appContext : vnode.appContext) || emptyAppContext
10
11  const instance: ComponentInternalInstance = {
12    // 省略componentInstance内容
13  }
14
15  if (__DEV__) { // TODO
16    instance.ctx = createRenderContext(instance)
17  } else {
18    instance.ctx = { _: instance }
19  }
20
21  instance.root = parent ? parent.root : instance
22  instance.emit = emit.bind(null, instance)
23
24  return instance
25}

setup

setup主要包括setupComponentsetupRenderEffect两个过程。

setupComponent方法代码位于@vue/runtime-core/component.ts的第516行:

1export let isInSSRComponentSetup = false // 表明组件是否服务端渲染的变量
2
3export function setupComponent(
4  instance: ComponentInternalInstance,
5  isSSR = false
6) {
7  isInSSRComponentSetup = isSSR
8
9  const { props, children, shapeFlag } = instance.vnode
10  const isStateful = shapeFlag & ShapeFlags.STATEFUL_COMPONENT
11  initProps(instance, props, isStateful, isSSR)
12  initSlots(instance, children)
13
14  const setupResult = isStateful
15    ? setupStatefulComponent(instance, isSSR)
16    : undefined
17  isInSSRComponentSetup = false
18  return setupResult
19}

内部主要做了初始化属性initProps和初始化插槽initSlots这两件事儿,并且带状态isStateful的组件,会执行setupStatefulComponent方法获取setupResult。最后将标识组件是否在SSR环境下执行setup的标识置为false,并返回setupResult

initProps方法位于@vue/runtime-core/componentProps.ts的第114行:

1export function initProps(
2  instance: ComponentInternalInstance,
3  rawProps: Data | null,
4  isStateful: number, // result of bitwise flag comparison
5  isSSR = false
6) {
7  const props: Data = {}
8  const attrs: Data = {}
9  def(attrs, InternalObjectKey, 1)
10  setFullProps(instance, rawProps, props, attrs)
11  // validation
12  if (__DEV__) {
13    validateProps(props, instance)
14  }
15
16  if (isStateful) {
17    // stateful
18    instance.props = isSSR ? props : shallowReactive(props)
19  } else {
20    if (!instance.type.props) {
21      // functional w/ optional props, props === attrs
22      instance.props = attrs
23    } else {
24      // functional w/ declared props
25      instance.props = props
26    }
27  }
28  instance.attrs = attrs
29}

setFullProps位于同文件:

1function setFullProps(
2  instance: ComponentInternalInstance,
3  rawProps: Data | null,
4  props: Data,
5  attrs: Data
6) {
7  const [options, needCastKeys] = instance.propsOptions
8  if (rawProps) {
9    for (const key in rawProps) {
10      const value = rawProps[key]
11      // key, ref are reserved and never passed down
12      if (isReservedProp(key)) {
13        continue
14      }
15      // prop option names are camelized during normalization, so to support
16      // kebab -> camel conversion here we need to camelize the key.
17      let camelKey
18      if (options && hasOwn(options, (camelKey = camelize(key)))) {
19        props[camelKey] = value
20      } else if (!isEmitListener(instance.emitsOptions, key)) {
21        // Any non-declared (either as a prop or an emitted event) props are put
22        // into a separate `attrs` object for spreading. Make sure to preserve
23        // original key casing
24        attrs[key] = value
25      }
26    }
27  }
28
29  if (needCastKeys) {
30    const rawCurrentProps = toRaw(props)
31    for (let i = 0; i < needCastKeys.length; i++) {
32      const key = needCastKeys[i]
33      props[key] = resolvePropValue(
34        options!,
35        rawCurrentProps,
36        key,
37        rawCurrentProps[key],
38        instance
39      )
40    }
41  }
42}

shallowReactiveVue3暴露的响应式核心API之一,用于创建一个不会深度嵌套的响应式proxy。调用了Reactivity部分的核心代码createReactiveObject,通过new Proxy的方式来响应data的变化。

update

setupRenderEffect主要设置了实例的update方法,此处调用了reactivity中的effect方法,在定义之后会立即执行,出发DOM渲染:

1const setupRenderEffect: SetupRenderEffectFn = (
2  instance,
3  initialVNode,
4  container,
5  anchor,
6  parentSuspense,
7  isSVG,
8  optimized
9) => {
10  // create reactive effect for rendering
11  instance.update = effect(function componentEffect() {
12    if (!instance.isMounted) {
13      let vnodeHook: VNodeHook | null | undefined
14      const { el, props } = initialVNode
15      const { bm, m, parent } = instance
16
17      // beforeMount hook
18      if (bm) {
19        invokeArrayFns(bm)
20      }
21      // onVnodeBeforeMount
22      if ((vnodeHook = props && props.onVnodeBeforeMount)) {
23        invokeVNodeHook(vnodeHook, parent, initialVNode)
24      }
25
26      
27      const subTree = (instance.subTree = renderComponentRoot(instance))
28      if (__DEV__) {
29        endMeasure(instance, `render`)
30      }
31
32      if (el && hydrateNode) {
33        if (__DEV__) {
34          startMeasure(instance, `hydrate`)
35        }
36        // vnode has adopted host node - perform hydration instead of mount.
37        hydrateNode(
38          initialVNode.el as Node,
39          subTree,
40          instance,
41          parentSuspense
42        )
43        if (__DEV__) {
44          endMeasure(instance, `hydrate`)
45        }
46      } else {
47        if (__DEV__) {
48          startMeasure(instance, `patch`)
49        }
50        patch(
51          null,
52          subTree,
53          container,
54          anchor,
55          instance,
56          parentSuspense,
57          isSVG
58        )
59        if (__DEV__) {
60          endMeasure(instance, `patch`)
61        }
62        initialVNode.el = subTree.el
63      }
64      // mounted hook
65      if (m) {
66        queuePostRenderEffect(m, parentSuspense)
67      }
68      // onVnodeMounted
69      if ((vnodeHook = props && props.onVnodeMounted)) {
70        queuePostRenderEffect(() => {
71          invokeVNodeHook(vnodeHook!, parent, initialVNode)
72        }, parentSuspense)
73      }
74      // activated hook for keep-alive roots.
75      // #1742 activated hook must be accessed after first render
76      // since the hook may be injected by a child keep-alive
77      const { a } = instance
78      if (
79        a &&
80        initialVNode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
81      ) {
82        queuePostRenderEffect(a, parentSuspense)
83      }
84      instance.isMounted = true
85    } else {
86      // updateComponent
87      // This is triggered by mutation of component's own state (next: null)
88      // OR parent calling processComponent (next: VNode)
89      let { next, bu, u, parent, vnode } = instance
90      let originNext = next
91      let vnodeHook: VNodeHook | null | undefined
92      if (__DEV__) {
93        pushWarningContext(next || instance.vnode)
94      }
95
96      if (next) {
97        updateComponentPreRender(instance, next, optimized)
98      } else {
99        next = vnode
100      }
101      next.el = vnode.el
102
103      // beforeUpdate hook
104      if (bu) {
105        invokeArrayFns(bu)
106      }
107      // onVnodeBeforeUpdate
108      if ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) {
109        invokeVNodeHook(vnodeHook, parent, next, vnode)
110      }
111
112      
113      const nextTree = renderComponentRoot(instance)
114      if (__DEV__) {
115        endMeasure(instance, `render`)
116      }
117      const prevTree = instance.subTree
118      instance.subTree = nextTree
119
120      // reset refs
121      // only needed if previous patch had refs
122      if (instance.refs !== EMPTY_OBJ) {
123        instance.refs = {}
124      }
125      if (__DEV__) {
126        startMeasure(instance, `patch`)
127      }
128      patch(
129        prevTree,
130        nextTree,
131        // parent may have changed if it's in a teleport
132        hostParentNode(prevTree.el!)!,
133        // anchor may have changed if it's in a fragment
134        getNextHostNode(prevTree),
135        instance,
136        parentSuspense,
137        isSVG
138      )
139      
140      next.el = nextTree.el
141      if (originNext === null) {
142        // self-triggered update. In case of HOC, update parent component
143        // vnode el. HOC is indicated by parent instance's subTree pointing
144        // to child component's vnode
145        updateHOCHostEl(instance, nextTree.el)
146      }
147      // updated hook
148      if (u) {
149        queuePostRenderEffect(u, parentSuspense)
150      }
151      // onVnodeUpdated
152      if ((vnodeHook = next.props && next.props.onVnodeUpdated)) {
153        queuePostRenderEffect(() => {
154          invokeVNodeHook(vnodeHook!, parent, next!, vnode)
155        }, parentSuspense)
156      }
157    }
158  }, prodEffectOptions)
159}

Diff算法