vue3源码分析——实现组件更新

Posted twinkle||cll

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue3源码分析——实现组件更新相关的知识,希望对你有一定的参考价值。

引言

<<往期回顾>>

  1. vue3源码分析——实现组件通信provide,inject
  2. vue3源码分析——实现createRenderer,增加runtime-test
  3. vue3源码分析——实现element属性更新,child更新
  4. vue3源码分析——手写diff算法

前面的两期主要是实现element的更新,vue的更新除了element的更新外,还有component的更新哦,本期就带大家一起来看看,本期所有的源码请查看

正文

vue3在更新element的时候,除了需要分情况讨论更新children外,还需要来看vue3的属性有没有变化;那么同样的道理,对于组件的更新,也是需要来更新属性,插槽

流程

看到这个流程,是不是感觉比element的更新简单许多(❁´◡`❁)

测试用例

根据上面的流程图,可以写出这样的测试用例

 test('test comp update by Child', () => 
    let click
    const Child = 
      name: 'Child',
      setup(props,  emit ) 
        click = () => 
          emit('click')
        
        return 
          click
        
      ,
      render() 
        return h('div', , this.$props.a)
      
    
    const app = createApp(
      name: 'App',
      setup() 
        const a = ref(1);
        const changeA = () => 
          a.value++
        
        return 
          a,
          changeA
        
      ,
      render() 
        return h('div',  class: 'container' , [h('p', , this.a), h(Child,  a: this.a, onClick: this.changeA )])
      
    )
    const appDoc = document.querySelector('#app')
    app.mount(appDoc)
    // 默认开始挂载严重
    const containerDom = document.querySelector('.container')
    expect(containerDom?.innerhtml).toBe('<p>1</p><div>1</div>')
    // 调用click
    click()
    expect(containerDom?.innerHTML).toBe('<p>2</p><div>2</div>')

分析

根据上面的流程图,可以分析出下面的需求

  1. updateComponent里面需要实现啥;
  2. updatePreRender函数里面又是需要做啥;
  3. 怎么调用render函数呢?

问题解决:

updateComponent

updateComponent方法的调用肯定是在processComponent中的旧vnode存在的时候来调用,里面需要进行新老节点的对比,判断是否需要进行更新.更新则调用updateComponentPreRender去更新vnode的props,slots等.这里会还需要把新vnode给保存在实例当中方便后续的使用,最后还需要在当前的vnode当中保存当前组件的实例,方便后续交换新老vnode的时候调用.

updatePreRender

这个函数就是只要处理更新的逻辑即可

调用render

render的调用是在 setupRenderEffect中调用的,是不是可以重复利用下这个功能呢? 当然可以,effect函数是默认返回一个runner的,可以手动调用runner来执行effect里面的方法. 那么可以在当前的实例当中保存一个update方法,用于需要调用render的时候来进行调用即可.

编码

// 在创建vnode当中,添加component属性
export function createVNode(type, props?, children?) 
  const vnode = 
    ...省略其他属性
    // 当前组件的实例
    component: null,
    

// 在instance中添加 next属性和update方法,方便后续使用

export function createComponentInstance(vnode, parent) 
  const instance = 
    // ... 省略其他属性 
    // 更新后组件的vnode
    next: null,
    // 当前组件的更新函数,调用后,自动执行render函数
    update: null,
  
  vnode.component = instance


// 绑定insance.update,在setupRenderEffect中调用effect的时候绑定

// 实现updateComponent,并且在processComponent满足n1的时候来进行调用
function updateComponent(n1, n2) 
    // 更新组件
    const instance = (n2.component = n1.component)
    // 判断是否需要更新
    if (需要更新(n1, n2)) 
      instance.next = n2;
      instance.update();
     else 
    // 不需要更新则赋值
      n2.el = n1.el;
      instance.vnode = n2
    
  
 
 // 在setupRenderEffect中对更新部分进行改造,存在next的时候来调用updateComponentPreRender
 
  function updateComponentPreRender(instance, nextVNode) 
  // 把当前实例赋值给更新的vnode
    nextVNode.component = instance;
    // 更新当前实例的vode
    instance.vnode = nextVNode
    // 置空newVnode
    instance.next = null;
    // 更新属性
    instance.props = nextVNode.props;
    // 更新插槽
    instance.slots = nextVNode.slots;
  
 

总结

本期主要实现了vue3的组件更新,在组件更新中,主要的流程是 updateComponent--> updateComponentPreRender --> render, 在这三个函数中交换新老vnode的属性,给把当前的vnode给更新掉即可

以上是关于vue3源码分析——实现组件更新的主要内容,如果未能解决你的问题,请参考以下文章

vue3源码分析——实现组件更新

vue3源码分析——解密nextTick的实现

vue3源码分析——解密nextTick的实现

vue3源码分析——解密nextTick的实现

vue3源码分析——实现element属性更新,child更新

vue3源码分析——实现element属性更新,child更新