vue源码总结


Vue3源码学习总结

2022-11-17开始,2023-01-15大概结束,坚持下来了

github

响应式数据

怎么实现响应式数据

reactive、readonly、ref、effect和computed

响应式数据就是劫持数据的读写,get的时候收集依赖,set的时候触发依赖,这个依赖可以理解为它的上级上下文。

TODO: 对reactive、readonly、ref 写下实现

响应式数据触发视图更新

依赖收集/触发:全局的变量存储依赖

effect: 接受一个函数然后执行这个函数

跟effect结合:effect接受一个函数(fn),这个函数会被实例化成一个Effect对象,执行Effect对象的run(执行fn),一个全局的变量activeEffect的值就是当前Effect对象,如果fn里面有响应式数据,响应式数据读取的时候会触发track的过程,会将activeEffect加入到,响应式数据的依赖中,当响应式数据改变的时候,会将相应的依赖取出来,然后执行。

虚拟节点跟真实dom

虚拟节点是对真实dom的一个描述,创建虚拟节点的时候,会根据这个虚拟节点的属性创建

 const vnode =  {
        type,
        props,
        children,
        key: props?.key ?? null,
        shapeFlag: getShapeFlag(type),
        el: null,
        component: null,
    }

虚拟节点跟实例

虚拟节点只是描述了真实dom,具体的组件操作方法需要挂载到组件的实例上。

 const instance =  {
        vnode,
        props: {},
        setupState: {},
        type: vnode.type,
        emit: () => {},
        provides: parent ? Object.create(parent.provides) : {}, // 原型链指向构造函数,怎么指向
        parent,
        slots: null,
        isMounted: false,
        subTree: null,
        // effect的返回值
        update: null,
        // 下一个需要更新的节点
        nextVNode: null
    }

实现更新

props更新

children更新

diff算法

实现

/** 
    老的虚拟节点对应真实的dom已经copy给新的虚拟节点的真实dom了
*/
const c1 = old.children
const c2 = old.children
let i = 0
let e1 = c1.length - 1
let e2 = c2.length - 1

// 进行左端的对比
while( i <= e1 && i <= e2 ) {
  if (isSame(c1[i], c2[i])) {
    //向下层递归处理
  } else {
    break
  }
  i++
}

// 进行右端对比
while( i <= e1 && i <= e2 ) {
  if (isSame(c1[e1], c2[e2])) {
    //向下层递归处理
  } else {
    break
  }
  e1--
  e2--
}

// 对不同情况进行处理,比较 i e1 和 e2的大小关系
if (i > e2 && i < e1) {
  //新的全部遍历完了,老的还没遍历完,直接删除老的
} else if ( i > e1 & i <= e2) {
  //老的全部遍历完了,新的还没遍历完,创建新的节点(考虑锚点)
} else {
  // 老的新的都没遍历完,要对混乱的部分进行处理,总的策略是在老的中找到尽可能可以复用的部分
  1: 找到新节点在老节点中的位置
  2: 根据1的结果,找出最长递增序列(相对位置保持不变的)
  3: 需要考虑的情况
      3.1: 老节点不存在新节点,就需要新建
    3.2: 新节点混乱部分的下标跟递增序列的值中不匹配,说明需要移动
    3.3: 匹配说明相对位置没有变化,跳过

}

优化点

边界条件的优化

​ 在处理递增序列之前,如果全部是递增的就不需要进行递增序列的处理,利用之前已有的结果,判断有没有必要处理下一步。

学到了什么

1: 实现一个功能的时候,现全部实现,后续在拆分重构,没必要一开始就想的很好

2: 发现vue的实现也不是很复杂,但是各各部分之间的结合比较巧妙,比如说effect跟响应式数据的结合、虚拟节点跟实例之间的关系(存在对对方的相互引用),还有就是写代码的过程中抽离重复逻辑

3: 设计思想方面的话,模块指责划分,比如说runtime-coreruntime-domruntime-core只负责虚拟节点的创建更新的流程,如果想要创建dom元素的话,runtime-dom提供对应的api注入到runtime-core里面,类似于依赖注入。


文章作者: 木叶勇
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 木叶勇 !
  目录