前言

Vue 3 的核心部分可以分为三个主要模块:Compiler、Reactivity 和 Runtime。响应式的处理逻辑在 Reactivity 部分。

Compiler(编译器):Template => 渲染函数

将 Vue 的模板(Template)转换成 JavaScript 渲染函数。

在 Vue 3 中,编译器通过 @vue/compiler-sfc 提供的工具将 .vue 文件中的模板、脚本和样式拆分开,并编译为 JavaScript 代码。模板会被转换成渲染函数(render function),这个渲染函数会在 Runtime 中执行来生成虚拟 DOM。

Runtime(运行时)

负责执行渲染函数、更新虚拟 DOM、协调更新视图、处理生命周期钩子等。运行时并不涉及模板的编译,它负责 Vue 应用的整体生命周期和视图更新。

虚拟 DOM:渲染函数生成的虚拟 DOM 会通过 diff 算法与现有虚拟 DOM 对比,然后更新到真实 DOM。
组件生命周期:通过运行时机制控制组件的生命周期钩子(如 mounted, updated 等)。
事件处理:事件绑定、事件触发等操作也由 runtime 负责。

Reactivity(响应式系统)

实现 Vue 3 的响应式数据机制,即在数据发生变化时自动更新视图。Vue 3 引入了 Proxy 作为响应式的核心实现方式,替代了 Vue 2 中的 Object.defineProperty

关键点:

  • reactive:将对象变成响应式对象。
  • ref:将基本数据类型(如字符串、数字)包装成响应式对象。
  • effect:自动追踪依赖,当数据变化时自动更新视图。
  • computed:用于创建依赖其他响应式数据的计算属性。

Vue2对于响应式的处理

Vue2 使用 Object.defineProperty,缺点如下:

  • 针对对象的某个属性,劫持整个对象则需要遍历,性能不如Proxy,具体可看本人另一篇文章:为什么Proxy性能优于Object.defineProperty
  • 无法劫持对象 新增删除属性,需要单独重写原型方法

Vue3对于响应式的处理

Vue3:通过 Proxy和Reflect 对整个对象进行劫持,主要代码如下:

get(target,key,receiver){
  const result = Reflect.get(target,key,receiver)
  track(traget,key) // 追踪
  return isObject(result) ? reactive(result):result
},
set(target,key,value,receiver){
  const oldValue = target[key]
  const result = Reflect.set(target,key,value,receiver)
  if(oldValue !== value){
    trigger(target,key) // 更新
  }
  return result
}

track 响应式数据依赖追踪

在这里插入图片描述

trigger 触发更新

在这里插入图片描述

使用 Reflect 是为了解决 this 带来的问题,通过如下示例说明:

const obj = {
  a: 1,
  b: 2,
  get c() {
    console.log(this)
    return this.a + this.b
  },
}

const handler = new Proxy(obj, {
  get(target, key, receiver) {
    console.log("get", key)
    return target[key]
  },
})

handler.c 

在这里插入图片描述

这个示例中,明明访问器成员c也依赖了a和b,但是却无法正常劫持,这是因为 Proxy 只拦截 直接的属性访问,而不会拦截通过 访问器 方法(getter)调用的属性。

所以需要通过 Reflect.get 使用对象的原始方法 [[get]],确保访问器方法依赖属性的

const obj = {
  a: 1,
  b: 2,
  get c() {
    console.log(this)
    return this.a + this.b
  },
}

const handler = new Proxy(obj, {
  get(target, key, receiver) {
    console.log("get", key)
    return Reflect.get(target, key, receiver)
  },
})

handler.c

在这里插入图片描述

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐