vue3源码学习

142 阅读6分钟

一、框架设计基本概念

1、编程范式之命令式编程

关注过程 的一种编程范式,他描述了完成一个功能的 详细逻辑与步骤

2、编程范式之声明式编程

不关注过程,只关注结果 的范式

3、命令式 VS 声明式

声明式的框架本质是由命令式的代码去实现的

  • 性能: 命令式 > 声明式

  • 可维护性:命令式 < 声明式

可维护性指: 阅读、修改、删除、增加

4、企业应用开发与设计原则

  • 项目成本: 命令式 < 声明式

由开发周期决定

  • 开发体验: 命令式 < 声明式

5、框架的设计过程是一个不断在可维护性和性能取舍的过程

vue设计原则:在保证可维护性的基础上,尽可能减少性能的损耗

6、.vue中的html是真实的html吗

不是,这些标签上的指令,浏览器不能识别,vue做了处理

  1. 编译时:compiler
  2. 运行时:runtime
  3. 运行时 + 编译时:runtime + compiler

7、运行时

利用 render 把 vnode 渲染成真实 dom 节点

8、编译时

把 template 中的 html 的节点 编译成 render 函数

9、运行时 + 编译时

  • dom 渲染是如何进行的
    • 初次渲染(挂载)
    • 更新渲染(打补丁)
      • 全删,全加
      • 对比新旧差异,再删、加(选这个)
  • 纯运行时:只能使用复杂的js对象
  • 纯编译时:分析差异的过程在编译时进行,损失灵活性,例svelte
  • 运行时 + 编译时:保持灵活性的基础上,尽量进行性能优化,达到平衡

10、副作用

对数据进行 setter 或 getter 操作时,产生的一系列后果,可能会有多个副作用

11、vue3框架设计概述

  1. 响应性:reactivity

    通过 reactive 传入一个被代理对象(target)得到代理对象(proxyTarget),当代理对象产生setter或getter行为,都会产生副作用

  2. 编译器:complier 把template转换成render函数

  3. 运行时:runtime 通过render函数渲染成真实dom

12、为什么说vue3对ts支持比较好

vue 内部声明了很多ts类型

二、Vue 3源码结构 - 搭建框架雏形

1、源码结构

bash
复制代码
├── packages              
│   ├── compiler-core     # 与平台无关的编译器实现的核心函数包
│   ├── compiler-dom      # 浏览器相关的编译器上层内容
│   ├── compiler-sfc      # 单文件组件的编译器
│   ├── compiler-ssr      # 服务端渲染相关的编译器实现
│   ├── global.d.ts       # ts 相关一些声明文件
│   ├── reactivity        # 响应式核心包
│   ├── runtime-core      # 与平台无关的渲染器相关的核心包
│   ├── runtime-dom       # 浏览器相关的渲染器部分
│   ├── runtime-test      # 渲染器测试相关代码
│   ├── server-renderer   # 服务端渲染相关的包
│   ├── sfc-playground    # 单文件组件演练场 
│   ├── shared            # 工具库相关
│   ├── size-check        # 检测代码体积相关
│   ├── template-explorer # 演示模板编译成渲染函数相关的包
│   └── vue               # 包含编译时和运行时的发布包

1、源码debugger

  1. 下载 vue.js/core 代码
  2. 修改打包命令,开启soutceMap "build": "node scripts/build.js -s"
  3. 注释git相关代码,否则build失败

全局搜索 const commit = execa.sync('git', ['rev-parse', 'HEAD']).stdout.slice(0, 7) 注释,并注释引用commit变量的地方 4. 执行npm run build,使用/packages/vue/dist/vue.global.js, 5. 通过 live server 启动 6. 在控制台源代码debugger代码

2、如何阅读源代码

  1. 摒弃边缘情况,仅阅读核心逻辑
  2. 跟随一条主线

三、响应系统-响应系统的核心设计原则

影响视图变化的数据称为响应数据

1、vue 2 的响应性核心 API:Object.defineProperty

只能监听指定对象上指定属性的getter行为和setter行为,因此有缺陷——不能监听数组和对象的变化,不能监听新增的属性的变化

  1. 为指定对象的指定属性设置属性描述符
  2. 当想要修改对象的指定属性时,可以使用原对象进行修改
  3. 通过属性描述符,只有被监听的指定属性,才可以触发getter和setter

2、vue3的响应性核心 API:proxy

  1. 传入被代理对象,得到一个代理对象,同时拥有被代理对象中所有的属性
  2. 当想要修改对象的指定属性时,我们应该使用代理对象进行修改
  3. 代理对象的任何一个属性都可以触发handler的getter和setter

3、proxy的最佳拍档reflect

Reflect.get(target, propertyKey, receiver

如果target对象中指定了getterreceiver则为getter调用时的this值。 当我们期望代理对象的 getter 和setter时,不应该使用 target[key], 因为当被代理对象的属性有 get set标记时,this指向的是被代理对象,应该使用 reflect,借助他的get set方法,将this执行代理对象

四、响应系统 - 初见 reactivity 模块

创建 proxy; 收集 effect 的依赖; 触发收集的依赖

1、reactive

  • 1.触发了 createReactiveObject 函数,里面构建了 proxy ,才可以监听被代理对象的getter行为和setter行为,这个监听通过baseHandlers里完成

2、effect

  • 1.生成 ReactiveEffect 实例,
  • 2.类ReactiveEffect 接收一个 fn,fn必须暴露getter行为,才能完成依赖收集
  • 2.类ReactiveEffect 除了有fn,在 run 对全局变量 activeEffect 赋值,触发 fn 方法,从而激活getter行为,触发track,构建了一个targetMap,完成当前指定对象,指定属性,依赖收集的工作,也就是建立 targetMap 和 activeEffect 之间的联系
    • dep.add(activeEffect)
    • activeEffect.deps.push(dep)

3、触发依赖

  • 1.触发setter行为,触发trigger,通过targetMap,tartget和key拿到effect,调用fn,完成依赖触发

4、特点

  • 只支持对象和数组(引用数据类型)
  • 重新分配一个新对象、传入函数会丢失响应性
  • 解构时会丢失响应性,需使用toRefs

五、响应式系统 - ref

1、复杂数据类型

  1. 触发 createRef 函数,返回 RefImpl 类型的实例

  2. 该实例中,根据__v_isShallow传入数据类型分开处理

    1. 复杂数据类型:转化为 reactive 返回的 proxy 实例
    2. 简单类型:不做处理
  3. 当触发getter行为,执行trackRefValue,当activeEffect为真时,执行trackEffects完成依赖收集,返回this._value

  4. 触发setter行为时,实际上触发了一次 get value,触发了 reactive setter行为

2、简单数据类型

  1. 响应性依赖于 get value,set value
  2. 区别在于,set value时,触发triggrRefValue,执行triggerEffects

特点

  • 支持基本数据类型+引用数据类型
  • 需要使用 .value 访问属性
  • 重新分配一个新对象、传入函数不会丢失响应性
  • 解构对象时会丢失响应性,需使用toRefs

六、响应式系统 - computed

  1. 计算属性的实例,本质上是 ComputedRefImpl 的实例
  2. ComputedRefImpl 中通过 dirty 变量来控制 run 的执行和 triggerRefValue 的触发
  3. 想要访问计算属性的值必须通过 .value,因为他内部 和 ref 一样是通过 get value 实现的
  4. 每次 .value 都会触发 trackRefValue 即 依赖收集
  5. 在依赖触发时,先触发 computed的 effect 再触发 非computed的effect

过程: 依赖值的setter触发了computed effect 的scheduler,执行了triggerRefValue,触发了 render effect,触发了计算属性值的 getter ,计算出最新的 计算属性值 ,完成更新渲染

七、响应式系统 - watch

1.scheduler 调度器