• 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的属性有没有变化;那么同样的道理,对于组件的更新,也是需要来更新属性,插槽

    流程

    image.png

    看到这个流程,是不是感觉比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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    分析

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

    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;
      }
     
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    总结

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

  • 相关阅读:
    细分图中的可到达节点 : 常规最短路运用题
    【Kotlin学习】Kotlin的类型系统——基本数据类型和其他基本类型、集合与数组
    Oracle数据库之日期查询
    【TSP问题】基于Hopfield神经网络求解旅行商问题附Matlab代码
    分布式事务-Seata-详细图文讲解
    【智慧医疗】Springboot+Vue+Element-UI前后端分离的医疗管理平台
    oracle 如何查死锁
    继承和方法重写
    前端实现复制文字和图片,原来这么简单!
    系统架构设计师-数据库系统(2)
  • 原文地址:https://blog.csdn.net/qq_41499782/article/details/125562240