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

简介: vue3在更新element的时候,除了需要分情况讨论更新children外,还需要来看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的属性有没有变化;那么同样的道理,对于组件的更新,也是需要来更新属性,插槽等


流程


dbf476b26ae487a5ae8534d6cbd2f24b.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.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给更新掉即可

相关文章
|
2月前
|
JavaScript
Vue中如何实现兄弟组件之间的通信
在Vue中,兄弟组件可通过父组件中转、事件总线、Vuex/Pinia或provide/inject实现通信。小型项目推荐父组件中转或事件总线,大型项目建议使用Pinia等状态管理工具,确保数据流清晰可控,避免内存泄漏。
292 2
|
1月前
|
JavaScript 前端开发 安全
Vue 3
Vue 3以组合式API、Proxy响应式系统和全面TypeScript支持,重构前端开发范式。性能优化与生态协同并进,兼顾易用性与工程化,引领Web开发迈向高效、可维护的新纪元。(238字)
484 139
|
1月前
|
缓存 JavaScript 算法
Vue 3性能优化
Vue 3 通过 Proxy 和编译优化提升性能,但仍需遵循最佳实践。合理使用 v-if、key、computed,避免深度监听,利用懒加载与虚拟列表,结合打包优化,方可充分发挥其性能优势。(239字)
201 1
|
5月前
|
人工智能 JavaScript 算法
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
761 0
|
2月前
|
开发工具 iOS开发 MacOS
基于Vite7.1+Vue3+Pinia3+ArcoDesign网页版webos后台模板
最新版研发vite7+vue3.5+pinia3+arco-design仿macos/windows风格网页版OS系统Vite-Vue3-WebOS。
360 11
|
1月前
|
JavaScript 安全
vue3使用ts传参教程
Vue 3结合TypeScript实现组件传参,提升类型安全与开发效率。涵盖Props、Emits、v-model双向绑定及useAttrs透传属性,建议明确声明类型,保障代码质量。
239 0
|
3月前
|
缓存 前端开发 大数据
虚拟列表在Vue3中的具体应用场景有哪些?
虚拟列表在 Vue3 中通过仅渲染可视区域内容,显著提升大数据列表性能,适用于 ERP 表格、聊天界面、社交媒体、阅读器、日历及树形结构等场景,结合 `vue-virtual-scroller` 等工具可实现高效滚动与交互体验。
420 1
|
3月前
|
缓存 JavaScript UED
除了循环引用,Vue3还有哪些常见的性能优化技巧?
除了循环引用,Vue3还有哪些常见的性能优化技巧?
234 0
|
4月前
|
JavaScript
vue3循环引用自已实现
当渲染大量数据列表时,使用虚拟列表只渲染可视区域的内容,显著减少 DOM 节点数量。
135 0
|
JavaScript
Vue的非父子组件之间传值
全局事件总线 一种组件间通信的方式,适用于任意组件间通信
209 0