# Vue.js 设计与实现

# 权衡的艺术

声明试 命令式的区别

为什么需要虚拟 DOM

运行时 纯编译 运行时+纯编译

# 设计框架的核心要素

# Vue.js 3 的设计思路

# 3.2 渲染器

# 手写渲染器

// <div click="alert(123)">123</div>

const VNode = {
  tag: 'div',
  props{
    onClick: () => alert('123')
  },
  children:'123'
}

function render(VNode,container){

}

# 3.3 组件的本质:一组 DOM 元素的封装

# 3.4 模板的工作原理

# 编译器

编译器的作用其实就是将模板编译为渲染函数

  • h 函数返回虚拟 dom

# 渲染器/编译器

编译器和渲染器之间是存在信息交流的,它们互相配合使得性能进一步提升,而它们之间交流的媒介就是虚拟 DOM 对象。

# 4.响应式系统

# proxy (opens new window)

const p = new Proxy(target, handler)
# handler 对象的方法
  • get(target, property, receiver) 属性的获取
  • set(target, property, value, receiver) 属性的设置
# 响应式

读取收集副作用 设置触发副作用

没有在副作用函数与被操作的目标字段之间建立明确的联系

  • 使用 WakeMap 作为外层 bucket
  • 属性内部还是使用 Set 存好每一个 属性对应的副作用函数

在代理函数 hander 里的读取时,调用 track 函数 收集 对象属性对应的副作用 在代理函数 hander 里的设置时,调用 trigger 函数 触发对象属性对应的副作用

# 调度执行

可调度性是响应系统非常重要的特性。首先我们需要明确什么是可调度性。所谓可调度,指的是当 trigger 动作触发副作用函数重新执行时,有能力决定副作用函数执行的时机、次数以及方式。

  • 作用:Vue.js 中连续多次修改响应式数据但只会触发一次更新,实际上 Vue.js 内部实现了一个更加完善的调度器
# 计算属性 computed 与 lazy

最核心的改动是使用 lazy 选项创建了一个懒执行的 effect

function computed(getter) {
  let value
  let dirty = true

  const effectFn = effect(getter, {
    lazy: true,
    scheduler() {
      if (!dirty) {
        dirty = true
        // 当计算属性依赖的响应式数据变化时,手动调用 trigger 函数触发响应
        trigger(obj, 'value')
      }
    },
  })

  const obj = {
    get value() {
      if (dirty) {
        value = effectFn()
        dirty = false
      }
      // 当读取 value 时,手动调用 track 函数进行追踪
      track(obj, 'value')
      return value
    },
  }

  return obj
}
# watch 的实现原理

watch 的实现本质上就是利用了 effect 以及 options.scheduler 选项,

function watch(source, cb) {
  let getter
  if (typeof source === 'function') {
    getter = source
  } else {
    getter = () => traverse(source)
  }
  // 定义旧值与新值
  let oldValue, newValue
  // 使用 effect 注册副作用函数时,开启 lazy 选项,并把返回值存储到 effectFn 中以便后续手动调用
  const effectFn = effect(() => getter(), {
    lazy: true,
    scheduler() {
      // 在 scheduler 中重新执行副作用函数,得到的是新值
      newValue = effectFn()
      // 将旧值和新值作为回调函数的参数
      cb(newValue, oldValue)
      // 更新旧值,不然下一次会得到错误的旧值
      oldValue = newValue
    },
  })
  // 手动调用副作用函数,拿到的值就是旧值
  oldValue = effectFn()
}

# 非原始值的响应式方案

# Proxy 只能够代理对象的基本语义很重要

# 基本语义

对象的基本操作:{name:123} obj.name fn()存在

# reflect 能够在代理函数的时候,确保 this 指向的正确性

# diff

# 简单 diff

当新旧 vnode 的子节点都是一组节点时,为了以最小的性能开销完成更新操作,需要比较两组子节点,用于比较的算法就叫作 Diff 算法。

<ul>
  <li>a</li>
  <li>b</li>
  <li>c</li>
  <li>d</li>
</ul

# 服务器渲染

# CSR / SSR /同构渲染

# 激活

# 同构的代码

# 生命周期

# 跨平台 API

# 判断引入依赖

# 注意全局变量,引起的状态污染

# ClientOnly 组件