Vue源码学习(四)——setupRenderEffect

上文说到mountComponent的主要核心就是 createComponentInstance,setupComponent,setupRenderEffect 这三个函数的调用,而setupRenderEffect 则是组件依赖收集,更新的关键,而setupRenderEffect 依靠的就是

本文主要讨论setupRenderEffect 如何使用componentUpdateFn以及reativeEffect 

目录

1.setupRenderEffect

1.1 ReactiveEffect (响应式对象)

1.2 componentUpdateFn


1.setupRenderEffect

setupRenderEffect会先根据componentUpdateFn创建一个reativeEffect 实例,并赋值给instance.effect, effect ,然后声明函数effect.run,并赋值给update以及instance.update,最后调用update函数

  //调用render,进行组件的依赖收集
  const setupRenderEffect: SetupRenderEffectFn = (
    instance,
    initialVNode,
    container,
    anchor,
    parentSuspense,
    isSVG,
    optimized
  ) => {
    const componentUpdateFn = () => {
      if (!instance.isMounted) {
        // 此时activeEffect为当前的effect
        // 组件初始化的时候会执行这里
        // 为什么要在这里调用 render 函数呢
        // 是因为在 effect 内调用 render 才能触发依赖收集
        // 等到后面响应式的值变更后会再次触发这个函数

        let vnodeHook: VNodeHook | null | undefined
        const { el, props } = initialVNode
        const { bm, m, parent } = instance
        const isAsyncWrapperVNode = isAsyncWrapper(initialVNode)

        toggleRecurse(instance, false)
        // beforeMount hook
        if (bm) {
          invokeArrayFns(bm)
        }
        // onVnodeBeforeMount
        if (
          !isAsyncWrapperVNode &&
          (vnodeHook = props && props.onVnodeBeforeMount)
        ) {
          invokeVNodeHook(vnodeHook, parent, initialVNode)
        }
        if (
          __COMPAT__ &&
          isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
        ) {
          instance.emit('hook:beforeMount')
        }
        toggleRecurse(instance, true)

        if (el && hydrateNode) {
          // vnode has adopted host node - perform hydration instead of mount.
          const hydrateSubTree = () => {
            if (__DEV__) {
              startMeasure(instance, `render`)
            }
            //根渲染节点赋值到subTree---里面执行render函数(进行依赖的收集)
            instance.subTree = renderComponentRoot(instance)

            if (__DEV__) {
              endMeasure(instance, `render`)
            }
            if (__DEV__) {
              startMeasure(instance, `hydrate`)
            }
            hydrateNode!(
              el as Node,
              instance.subTree,
              instance,
              parentSuspense,
              null
            )
            if (__DEV__) {
              endMeasure(instance, `hydrate`)
            }
          }

          if (isAsyncWrapperVNode) {
            ; (initialVNode.type as ComponentOptions).__asyncLoader!().then(
              // note: we are moving the render call into an async callback,
              // which means it won't track dependencies - but it's ok because
              // a server-rendered async wrapper is already in resolved state
              // and it will never need to change.
              () => !instance.isUnmounted && hydrateSubTree()
            )
          } else {
            hydrateSubTree()
          }
        } else {
          if (__DEV__) {
            startMeasure(instance, `render`)
          }

          //根渲染节点赋值到subTree---里面执行render函数
          //1.执行render函数,创建虚拟子节点
          //2.进行数据的依赖收集(setup中的数据进行了proxy代理)
          //3.在render中通过this访问instance.proxy,instance.proxy对数据访问进行不同proxy处理
          const subTree = (instance.subTree = renderComponentRoot(instance))

          if (__DEV__) {
            endMeasure(instance, `render`)
          }
          if (__DEV__) {
            startMeasure(instance, `patch`)
          }
          // 基于 subTree 再次调用 patch
          // 基于 render 返回的 vnode ,再次进行渲染
          // 递归的开箱(对于render的虚拟节点进行patch)
          patch(
            null,
            subTree,
            container,
            anchor,
            instance,
            parentSuspense,
            isSVG
          )
          if (__DEV__) {
            endMeasure(instance, `patch`)
          }

          // 把 root element 赋值给 组件的vnode.el ,为后续调用 $el 的时候获取值
          // 是为了将组件和组件内部的根节点联系起来,以便正确地进行组件的初始化和挂载。
          initialVNode.el = subTree.el
        }
        // mounted hook
        if (m) {  //mount放在post类型cheduler进行异步处理
          queuePostRenderEffect(m, parentSuspense)
        }
        // onVnodeMounted
        if (
          !isAsyncWrapperVNode &&
          (vnodeHook = props && props.onVnodeMounted)
        ) {
          const scopedInitialVNode = initialVNode
          queuePostRenderEffect(
            () => invokeVNodeHook(vnodeHook!, parent, scopedInitialVNode),
            parentSuspense
          )
        }
        if (
          __COMPAT__ &&
          isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
        ) {
          queuePostRenderEffect(
            () => instance.emit('hook:mounted'),
            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
        if (
          initialVNode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE ||
          (parent &&
            isAsyncWrapper(parent.vnode) &&
            parent.vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE)
        ) {
          instance.a && queuePostRenderEffect(instance.a, parentSuspense)
          if (
            __COMPAT__ &&
            isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
          ) {
            queuePostRenderEffect(
              () => instance.emit('hook:activated'),
              parentSuspense
            )
          }
        }
        instance.isMounted = true

        if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
          devtoolsComponentAdded(instance)
        }

        // #2458: deference mount-only object parameters to prevent memleaks
        initialVNode = container = anchor = null as any
      } 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 (__DEV__) {
          pushWarningContext(next || instance.vnode)
        }

        // Disallow component effect recursion during pre-lifecycle hooks.
        toggleRecurse(instance, false)
        // 如果有 next 的话, 说明需要更新组件的数据(props,slots 等)
        // 先更新组件的数据,然后更新完成后,在继续对比当前组件的子元素
        if (next) {
          //父组件更新调用processComponet触发子组件更新
          //新组件 next 的 el 属性指向老组件 vnode 的 el 属性
          // 让新组件直接使用旧组件的 el 对象

          //instance.next = n2----updateComponent函数
          //说明next只是虚拟节点,next此时并没有el属性
          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)
        }
        if (
          __COMPAT__ &&
          isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
        ) {
          instance.emit('hook:beforeUpdate')
        }
        toggleRecurse(instance, true)

        // render
        if (__DEV__) {
          startMeasure(instance, `render`)
        }
        // 调用render函数--重新进行依赖收集和节点创建并赋值给nextTree
        const nextTree = renderComponentRoot(instance)
        if (__DEV__) {
          endMeasure(instance, `render`)
        }
        const prevTree = instance.subTree
        instance.subTree = nextTree

        if (__DEV__) {
          startMeasure(instance, `patch`)
        }
        //patch prevTree和nextTree
        patch(
          prevTree,
          nextTree,
          // parent may have changed if it's in a teleport
          hostParentNode(prevTree.el!)!,
          // anchor may have changed if it's in a fragment
          getNextHostNode(prevTree),
          instance,
          parentSuspense,
          isSVG
        )
        if (__DEV__) {
          endMeasure(instance, `patch`)
        }
        next.el = nextTree.el
        if (originNext === null) {
          // self-triggered update. In case of HOC, update parent component
          // vnode el. HOC is indicated by parent instance's subTree pointing
          // to child component's vnode
          updateHOCHostEl(instance, nextTree.el)
        }
        // updated hook
        if (u) { //updated在post类型scheduler进行异步处理
          queuePostRenderEffect(u, parentSuspense)
        }
        // onVnodeUpdated
        if ((vnodeHook = next.props && next.props.onVnodeUpdated)) {
          queuePostRenderEffect(
            () => invokeVNodeHook(vnodeHook!, parent, next!, vnode),
            parentSuspense
          )
        }
        if (
          __COMPAT__ &&
          isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
        ) {
          queuePostRenderEffect(
            () => instance.emit('hook:updated'),
            parentSuspense
          )
        }

        if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
          devtoolsComponentUpdated(instance)
        }

        if (__DEV__) {
          popWarningContext()
        }
      }
    }

    // 在effect内部调用componentUpdateFn---进行依赖收集

    // create reactive effect for rendering
    // 第三个参数instance.scope----ReactiveEffect需要一个 scope  参数来收集所有的 effect
    const effect = (instance.effect = new ReactiveEffect(
      componentUpdateFn,
      () => queueJob(update), //传入一个scheduler调度器--用于组件更新
      instance.scope // track it in component's effect scope
    ))
    //将() => effect.run()同时赋值到instance.updte,update
    //通过使用instance.updte,update触发effect.run
    const update: SchedulerJob = (instance.update = () => effect.run())
    update.id = instance.uid
    // allowRecurse
    // #1801, #2043 component render effects should allow recursive updates
    toggleRecurse(instance, true)

    if (__DEV__) {
      effect.onTrack = instance.rtc
        ? e => invokeArrayFns(instance.rtc!, e)
        : void 0
      effect.onTrigger = instance.rtg
        ? e => invokeArrayFns(instance.rtg!, e)
        : void 0
      update.ownerInstance = instance
    }
    // 调用update
    update()
  }

1.1 ReactiveEffect (响应式对象)

ReactiveEffect是一个类,最关键的就是 run方法,因为setupRenderEffect 调用的就是run方法,

run方法最主要的将activeEffect赋值给自己,然后return this.fn(), 就是执行fn函数也就是传过来的componentUpdateFn,

对于ReactiveEffect,我们先暂时过一下,先看componentUpdateFn

export class ReactiveEffect<T = any> {
  active = true
  //dep 数组,在响应式对象收集依赖时也会将对应的依赖项添加到这个数组中
  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
  ) {
    recordEffectScope(this, scope)
  }

  run() {
    // 当active=false,执行函数但不收集依赖
    if (!this.active) {
      return this.fn()
    }
    let parent: ReactiveEffect | undefined = activeEffect
    let lastShouldTrack = shouldTrack

    //循环遍历parent的父级元素
    //直到找到了与当前实例this相同的父级元素,或者直到parent为null为止
    //避免在组件的父子关系中出现循环引用的情况
    while (parent) {
      if (parent === this) {
        return
      }
      parent = parent.parent
    }
    try {
      //将上一个activeEffect先保存起来
      this.parent = activeEffect
      // 将自己设置为当前活跃的 ReactiveEffect
      activeEffect = this
      shouldTrack = true
      //标记effect层级,执行一层effect,++effectTrackDepth
      trackOpBit = 1 << ++effectTrackDepth
      // 超过 maxMarkerBits 则 trackOpBit 的计算会超过最大整形的位数,降级为 cleanupEffect 
      if (effectTrackDepth <= maxMarkerBits) {
        //给收集到的dpes数组打标记
        initDepMarkers(this)
      } else {
        //重置deps数组依赖,需要重新收集
        cleanupEffect(this)
      }
      // 执行fn
      return this.fn()
    } finally {
      if (effectTrackDepth <= maxMarkerBits) {
        // 执行完effect,看一下需要删除那些依赖添加哪些依赖
        finalizeDepMarkers(this)
      }
      //标记effect层级,执行完--effectTrackDepth
      trackOpBit = 1 << --effectTrackDepth

      activeEffect = this.parent
      shouldTrack = lastShouldTrack
      this.parent = undefined

      if (this.deferStop) {
        this.stop()
      }
    }
  }

  stop() {
    // stopped while running itself - defer the cleanup
    if (activeEffect === this) {
      this.deferStop = true
    } else if (this.active) {
      cleanupEffect(this)
      if (this.onStop) {
        this.onStop()
      }
      this.active = false
    }
  }
}

1.2 componentUpdateFn

componentUpdateFn 是根据isMounted进行不同的逻辑处理,如果isMounted为true则进行更新处理,isMounted为false则进行初始化处理 (先说初始化

初始化处理主要 1.先检查有无beforeMount,有则触发  2. 调用renderComponentRoot函数并把1返回值赋值给instance.subTree和subTree (renderComponentRoot的主要作用就是调用render方法,并且通过call将this绑定在instance.proxy)----注意:因为render函数会使用reactive,ref的值,因此会触发track,所以此时就是在进行依赖收集   ,最后会将render函数的值进行返回--(例如 h('div',{},this.msg))

3.此时subTree 类似 h('div',{},'Hello Vue'),为了防止内部还要组件,因此要基于subTree递归调用patch函数

因此 componentUpdateFn 内部会执行一个render函数,所以我们可以知道每一个组件其实就是一个reativeEffect

但是reative,ref内部是这么样,为什么就可以进行依赖收集,我们还不得而知,下一篇我们需要知道reative,ref内部如何进行操做 ?????

  const componentUpdateFn = () => {
      if (!instance.isMounted) {
        // 此时activeEffect为当前的effect
        // 组件初始化的时候会执行这里
        // 为什么要在这里调用 render 函数呢
        // 是因为在 effect 内调用 render 才能触发依赖收集
        // 等到后面响应式的值变更后会再次触发这个函数

        let vnodeHook: VNodeHook | null | undefined
        const { el, props } = initialVNode
        const { bm, m, parent } = instance
        const isAsyncWrapperVNode = isAsyncWrapper(initialVNode)

        toggleRecurse(instance, false)
        // beforeMount hook
        if (bm) {
          invokeArrayFns(bm)
        }
        // onVnodeBeforeMount
        if (
          !isAsyncWrapperVNode &&
          (vnodeHook = props && props.onVnodeBeforeMount)
        ) {
          invokeVNodeHook(vnodeHook, parent, initialVNode)
        }
        if (
          __COMPAT__ &&
          isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
        ) {
          instance.emit('hook:beforeMount')
        }
        toggleRecurse(instance, true)

        if (el && hydrateNode) {
          // vnode has adopted host node - perform hydration instead of mount.
          const hydrateSubTree = () => {
            if (__DEV__) {
              startMeasure(instance, `render`)
            }
            //根渲染节点赋值到subTree---里面执行render函数(进行依赖的收集)
            instance.subTree = renderComponentRoot(instance)

            if (__DEV__) {
              endMeasure(instance, `render`)
            }
            if (__DEV__) {
              startMeasure(instance, `hydrate`)
            }
            hydrateNode!(
              el as Node,
              instance.subTree,
              instance,
              parentSuspense,
              null
            )
            if (__DEV__) {
              endMeasure(instance, `hydrate`)
            }
          }

          if (isAsyncWrapperVNode) {
            ; (initialVNode.type as ComponentOptions).__asyncLoader!().then(
              // note: we are moving the render call into an async callback,
              // which means it won't track dependencies - but it's ok because
              // a server-rendered async wrapper is already in resolved state
              // and it will never need to change.
              () => !instance.isUnmounted && hydrateSubTree()
            )
          } else {
            hydrateSubTree()
          }
        } else {
          if (__DEV__) {
            startMeasure(instance, `render`)
          }

          //根渲染节点赋值到subTree---里面执行render函数
          //1.执行render函数,创建虚拟子节点
          //2.进行数据的依赖收集(setup中的数据进行了proxy代理)
          //3.在render中通过this访问instance.proxy,instance.proxy对数据访问进行不同proxy处理
          const subTree = (instance.subTree = renderComponentRoot(instance))

          if (__DEV__) {
            endMeasure(instance, `render`)
          }
          if (__DEV__) {
            startMeasure(instance, `patch`)
          }
          // 基于 subTree 再次调用 patch
          // 基于 render 返回的 vnode ,再次进行渲染
          // 递归的开箱(对于render的虚拟节点进行patch)
          patch(
            null,
            subTree,
            container,
            anchor,
            instance,
            parentSuspense,
            isSVG
          )
          if (__DEV__) {
            endMeasure(instance, `patch`)
          }

          // 把 root element 赋值给 组件的vnode.el ,为后续调用 $el 的时候获取值
          // 是为了将组件和组件内部的根节点联系起来,以便正确地进行组件的初始化和挂载。
          initialVNode.el = subTree.el
        }
        // mounted hook
        if (m) {  //mount放在post类型cheduler进行异步处理
          queuePostRenderEffect(m, parentSuspense)
        }
        // onVnodeMounted
        if (
          !isAsyncWrapperVNode &&
          (vnodeHook = props && props.onVnodeMounted)
        ) {
          const scopedInitialVNode = initialVNode
          queuePostRenderEffect(
            () => invokeVNodeHook(vnodeHook!, parent, scopedInitialVNode),
            parentSuspense
          )
        }
        if (
          __COMPAT__ &&
          isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
        ) {
          queuePostRenderEffect(
            () => instance.emit('hook:mounted'),
            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
        if (
          initialVNode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE ||
          (parent &&
            isAsyncWrapper(parent.vnode) &&
            parent.vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE)
        ) {
          instance.a && queuePostRenderEffect(instance.a, parentSuspense)
          if (
            __COMPAT__ &&
            isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
          ) {
            queuePostRenderEffect(
              () => instance.emit('hook:activated'),
              parentSuspense
            )
          }
        }
        instance.isMounted = true

        if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
          devtoolsComponentAdded(instance)
        }

        // #2458: deference mount-only object parameters to prevent memleaks
        initialVNode = container = anchor = null as any
      } 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 (__DEV__) {
          pushWarningContext(next || instance.vnode)
        }

        // Disallow component effect recursion during pre-lifecycle hooks.
        toggleRecurse(instance, false)
        // 如果有 next 的话, 说明需要更新组件的数据(props,slots 等)
        // 先更新组件的数据,然后更新完成后,在继续对比当前组件的子元素
        if (next) {
          //父组件更新调用processComponet触发子组件更新
          //新组件 next 的 el 属性指向老组件 vnode 的 el 属性
          // 让新组件直接使用旧组件的 el 对象

          //instance.next = n2----updateComponent函数
          //说明next只是虚拟节点,next此时并没有el属性
          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)
        }
        if (
          __COMPAT__ &&
          isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
        ) {
          instance.emit('hook:beforeUpdate')
        }
        toggleRecurse(instance, true)

        // render
        if (__DEV__) {
          startMeasure(instance, `render`)
        }
        // 调用render函数--重新进行依赖收集和节点创建并赋值给nextTree
        const nextTree = renderComponentRoot(instance)
        if (__DEV__) {
          endMeasure(instance, `render`)
        }
        const prevTree = instance.subTree
        instance.subTree = nextTree

        if (__DEV__) {
          startMeasure(instance, `patch`)
        }
        //patch prevTree和nextTree
        patch(
          prevTree,
          nextTree,
          // parent may have changed if it's in a teleport
          hostParentNode(prevTree.el!)!,
          // anchor may have changed if it's in a fragment
          getNextHostNode(prevTree),
          instance,
          parentSuspense,
          isSVG
        )
        if (__DEV__) {
          endMeasure(instance, `patch`)
        }
        next.el = nextTree.el
        if (originNext === null) {
          // self-triggered update. In case of HOC, update parent component
          // vnode el. HOC is indicated by parent instance's subTree pointing
          // to child component's vnode
          updateHOCHostEl(instance, nextTree.el)
        }
        // updated hook
        if (u) { //updated在post类型scheduler进行异步处理
          queuePostRenderEffect(u, parentSuspense)
        }
        // onVnodeUpdated
        if ((vnodeHook = next.props && next.props.onVnodeUpdated)) {
          queuePostRenderEffect(
            () => invokeVNodeHook(vnodeHook!, parent, next!, vnode),
            parentSuspense
          )
        }
        if (
          __COMPAT__ &&
          isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
        ) {
          queuePostRenderEffect(
            () => instance.emit('hook:updated'),
            parentSuspense
          )
        }

        if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
          devtoolsComponentUpdated(instance)
        }

        if (__DEV__) {
          popWarningContext()
        }
      }
    }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值