vue3 learning source code notes (newbie entry series) ------ component update process

illustrate

Since there is too much content related to responsiveness, we decided to continue to analyze the subsequent update process of the component after mounting the component above, without analyzing how the component is analyzed.

example

Use the vitest plug-in debug to run this use case and slowly understand it with the following core code.

 it('should support runtime template compilation', async () => {
    
    
    const container = document.createElement('div')
    container.classList.add('app')
    const foo = {
    
    name:'kd'}
    let temp ;
    // 子组件
    const child = defineComponent({
    
    
      template: `
         <div><p>{
     
     {age}}</p></div>
      `,
      props:{
    
    
        age:{
    
    
          type: Number,
          default:20
        }
      }
  })
   let num = 1000
    const App = {
    
    
      components:{
    
    child},
      beforeMount() {
    
    
        console.log('beforeMount');
        
      },
      data() {
    
    
        return {
    
    
        }
      },
      setup() {
    
    
        const count = ref(1)

        const age = ref('20')


        onMounted(()=>{
    
    
          count.value = 5
          age.value = '2'
        })

        onUpdated(()=>{
    
    
          num++
        })
        // const obj = reactive({name:'kd'})
        // return {obj,time}
        return ()=>{
    
    
          return  h('div',[count.value,h(child,{
    
    age:age.value})])
        }
      }
    }
    createApp(App).mount(container)
    await nextTick()
    // time.value = 2000
    // await nextTick()
    expect(foo).equals(temp)
     expect(container.innerHTML).toBe(`0`)
  })

processComponent

Do you still remember updateComponentFn in the processComponent initialization side effect function in the patch?
When the count age responsive data in onMounted changes, the effect in the App component instance will be triggered (that is, the app component is created during the initial mounting)

// packages/runtime-core/src/renderer.ts
const setupRenderEffect: SetupRenderEffectFn = (
    instance,
    initialVNode,
    container,
    anchor,
    parentSuspense,
    isSVG,
    optimized
  ) => {
    
    
 const componentUpdateFn = ()=>{
    
    ...}
const effect = (instance.effect = new ReactiveEffect({
    
    componentUpdateFn,
      () => queueJob(update),
      instance.scope}))
  const update: SchedulerJob = (instance.update = () => effect.run())
    update.id = instance.uid
    //... 省略部分逻辑
    update()
}

The effect is that the responsive data update will trigger the call and go to the component update part in componentUpdateFn.

componentUpdateFn


 const componentUpdateFn = ()=>{
    
    
    if (!instance.isMounted) {
    
    ...}
    else {
    
    
       // 组件更新
        // updateComponent
        // This is triggered by mutation of component's own state (next: null) 由组件自身状态的突变触发时(next: null)
        // OR parent calling processComponent (next: VNode) 父组件 调用一般就是 有新的属性 props slots 改变 有新的vnode 
        let {
    
     next, bu, u, parent, vnode } = instance
        // 如果有 next 的话说明需要更新组件的数组(props, slot 等)
        let originNext = next
        // ... 省略
       if (next) {
    
    
          next.el = vnode.el
          // 更新组件vnode实例信息 props slots 等
          updateComponentPreRender(instance, next, optimized)
        } else {
    
    
          //没有代表 不需要更新 自身
          next = vnode
        }
   }
// render
        if (__DEV__) {
    
    
          startMeasure(instance, `render`)
        }
        // 新的vnode 
        const nextTree = renderComponentRoot(instance)
        if (__DEV__) {
    
    
          endMeasure(instance, `render`)
        }
        // 旧的vnode
        const prevTree = instance.subTree
        // 新的vnode 给下次渲染更新使用
        instance.subTree = nextTree

        if (__DEV__) {
    
    
          startMeasure(instance, `patch`)
        }
        // diff更新 
        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
    
}

The instance at this time is the app. Since the rendering is triggered by internal data, its own props slots have not changed, so next is null at this time (I will explain later when updateComponentPreRender is executed). After going to the patch below, the child component will be updated. At this time
, Will enter processComponent and will go to updateComponent method

updateComponent


const updateComponent = (n1: VNode, n2: VNode, optimized: boolean) => {
    
    
    const instance = (n2.component = n1.component)!
    // 先去判断组件自身是否需要被更新 
    if (shouldUpdateComponent(n1, n2, optimized)) {
    
    
      if (
        __FEATURE_SUSPENSE__ &&
        instance.asyncDep &&
        !instance.asyncResolved
      ) {
    
    
        // async & still pending - just update props and slots
        // since the component's reactive effect for render isn't set-up yet
        if (__DEV__) {
    
    
          pushWarningContext(n2)
        }
        updateComponentPreRender(instance, n2, optimized)
        if (__DEV__) {
    
    
          popWarningContext()
        }
        return
      } else {
    
    
        // normal update 将 需要
        instance.next = n2
        // in case the child component is also queued, remove it to avoid
        // double updating the same child component in the same flush.
        // 先执行 invalidataJob 避免子组件(指的是app 的 子组件child)由于自身数据变化导致的重复更新 去除queue 中 子组件的更新 任务(就是子组件child自身的 update)
        invalidateJob(instance.update)
        // instance.update is the reactive effect.
        // 主动触发child组件的更新
        instance.update()
      }
    } else {
    
    
      // no update needed. just copy over properties 不需要更新就把之前节点的元素 赋值给 新节点 在赋值到组件的vnode上
      n2.el = n1.el
      instance.vnode = n2
    }
  }

At this time, the instance next attribute of the child component instance will be copied into a new vnode. After manually triggering the component update, it will go to the componentUpdateFn generated by the child instance instance initialization. At this time, the next logic will be used to update the props slots and other attributes of the child component.

Let’s take a look at updateComponentPreRender again

updateComponentPreRender

const updateComponentPreRender = (
    instance: ComponentInternalInstance,
    nextVNode: VNode,
    optimized: boolean
  ) => {
    
    
    // 新组件 vnode 的 component 属性指向组件实例
    nextVNode.component = instance
    // 旧组件vnode 的 props属性
    const prevProps = instance.vnode.props
    //组件实例的vnode属性 也指向新的组件vnode
    instance.vnode = nextVNode
    // 清空next 属性 为下一次重新渲染做准备
    instance.next = null
    // 更新 props
    updateProps(instance, nextVNode.props, prevProps, optimized)
    // 更新 slots
    updateSlots(instance, nextVNode.children, optimized)

    pauseTracking()
    // props update may have triggered pre-flush watchers.
    // flush them before the render update.
    flushPreFlushCbs()
    resetTracking()
  }

After the child updates its own attributes, it executes renderComponentRoot to generate a new vnode according to the new component attributes. Then go to patch => processElement => and then diff update... I will not go into the comparison
rules of ordinary elements.

Insert image description here

Insert image description here

Summarize

The essence of processComponent processing component vnode is to first determine whether the sub-component needs to be updated.
If necessary, recurse the side-effect rendering function of the sub-component to update, otherwise just update some vnode attributes, and let the sub-component instance retain a reference to the component (self) vnode, which is used to re-render the component (self) due to changes in the sub-component's own data. When you can get the latest component (self) vnode

Guess you like

Origin blog.csdn.net/weixin_45485922/article/details/132626197