vue3的API和数据响应式的变化

面试题1:为什么vue3中去掉了vue构造函数?

vue2的全局构造函数带来了诸多问题:
1.调用构造函数的静态方法会对所有vue应用生效,不利于隔离不同应用
2.vue2的构造函数集成了太多功能,不利于treeshaking,vue3把这些功能使用普通函数导出,能够充分利用treeshaking优化打包体积
3.vue2没有把组件实例和vue应用两个概念区分开,在vue2中,通过newVue创建的对象,既是一个vue应用,同时又是一个特殊的vue组件。vue3中,把两个概念区别开来,通过createApp创建的对象,是一个vue应用,它内部提供的方法是针对整个应用的,而不再是一个特殊的组件。

面试题2:谈谈你对vue3数据响应式的理解

vue3不再使用object.defineProperty的方式定义完成数据响应式,而是使用Proxy。
除了Proxy本身效率比object.defineProperty更高之外,由于不必递归遍历所有属性,而是直接得到一个Proxy。所以在
vue3中,对数据的访问是动态的,当访问某个属性的时候,再动态的获取和设置,这就极大的提升了在组件初始阶段的效
率。
同时,由于Proxy可以监控到成员的新增和删除,因此,在vue3中新增成员、删除成员、索引访问等均可以触发重新渲染,
而这些在vue2中是难以做到的。

为什么vue3中去掉了vue构造函数?

Vue 3 移除传统的 Vue 构造函数并改用 createApp 工厂函数,这一设计调整主要基于以下核心原因:


1. 模块化与 Tree Shaking 支持

Vue 3 将核心功能拆分为独立模块(如 reactivityruntime-core),通过工厂函数 createApp 按需加载,避免未使用代码被打包。这种设计显著减少了生产环境体积(例如,Vue 3 核心包仅约 10 KB,而 Vue 2 约 20 KB)。
对比:Vue 2 的全局构造函数会强制引入所有功能(如 Vue.nextTickVue.set),即使未使用也无法剔除。


2. 多应用实例隔离

Vue 3 允许创建多个独立的应用实例(通过多次调用 createApp()),每个实例拥有独立的全局配置(如组件、指令、插件),避免污染全局状态。
示例

  // 创建两个独立应用实例
  const app1 = createApp(App1);
  app1.component('MyButton', Button1);  // 仅 app1 可用

  const app2 = createApp(App2);
  app2.component('MyButton', Button2);  // 仅 app2 可用

解决痛点:Vue 2 的全局构造函数导致多个应用共享同一配置,难以实现微前端或插件隔离。


3. 配置与生命周期的明确性

通过工厂函数返回的实例对象(如 app),所有 API 显式挂载到实例上(如 app.use()app.mount()),而非全局构造函数。这种设计:
避免副作用:例如,Vue 2 中 Vue.use(plugin) 会影响所有后续实例,而 Vue 3 的 app.use(plugin) 仅影响当前实例。
生命周期更清晰:实例的挂载(mount)与卸载(unmount)行为更可控,适合动态应用场景(如按需加载组件)。


4. TypeScript 类型支持优化

工厂函数返回的实例类型明确,结合 Composition API 的按需导入,提供了更完善的类型推导和 IDE 支持。
示例

  import { createApp, ref } from 'vue';  // 按需导入,类型明确
  const app = createApp({ /* ... */ });
  app.mount('#app');  // 类型检查 mount 方法的参数合法性

5. 响应式系统的解耦

Vue 3 的响应式模块(reactivity)独立于核心框架,可通过工厂函数灵活集成。这种解耦:
支持非 DOM 环境:例如,在服务端渲染(SSR)或 NativeScript 中仅使用响应式模块。
简化测试:开发者可单独测试响应式逻辑,无需依赖完整 Vue 实例。


总结:Vue 3 设计哲学的升级

移除构造函数是 Vue 3 整体架构升级的一部分,核心目标包括:

  1. 轻量化:通过 Tree Shaking 减少代码体积。
  2. 隔离性:支持多实例独立配置。
  3. 灵活性:模块化设计适应多样化场景。
  4. 可维护性:明确的 API 边界提升代码可读性。

这一变化使 Vue 3 更适应现代前端工程化需求(如微前端、SSR、TypeScript),同时保持了开发体验的简洁性。


谈谈你对vue3数据响应式的理解

Vue 3 的数据响应式原理是其核心机制之一,通过 Proxy 代理Reflect 反射的协同实现了更高效、更灵活的数据追踪。以下是其核心原理及技术细节:


一、Proxy 代理取代 Object.defineProperty

Vue 3 使用 Proxy 代理整个对象,而非像 Vue 2 那样递归劫持每个属性。
优势对比:

特性Vue 2 (Object.defineProperty)Vue 3 (Proxy)
动态属性监听需手动调用 Vue.set/Vue.delete自动支持新增/删除属性
数组监听需重写数组方法(如 push直接监听索引变化和 length 修改
性能初始化时递归劫持,性能损耗大惰性代理,按需触发

实现示例

const obj = { count: 0 };
const proxy = new Proxy(obj, {
  get(target, key) {
    track(target, key); // 依赖收集
    return Reflect.get(target, key);
  },
  set(target, key, value) {
    Reflect.set(target, key, value);
    trigger(target, key); // 触发更新
    return true;
  }
});

通过 Proxy 的 getset 拦截器,Vue 3 能自动追踪所有属性的读写操作。


二、响应式 API:reactiveref

  1. reactive(obj)
    • 代理对象或数组,返回一个 Proxy 实例,直接操作属性即可触发响应式。
    • 示例:
    const state = reactive({ count: 0 });
    state.count++; // 触发更新
  1. ref(value)
    • 包装基本类型(如字符串、数字)或对象,通过 .value 访问值。
    • 底层通过 reactive 实现,自动解包嵌套响应式对象。
    • 示例:
     const count = ref(0);
     count.value++; // 触发更新
    

三、依赖收集与更新触发

  1. 依赖收集(Track)
    • 在 get 拦截器中,通过 track(target, key) 记录当前访问该属性的副作用(如组件渲染函数)。
    • 依赖关系存储在 WeakMap 结构中(targetMap),键为对象,值为属性与依赖的映射。

  2. 触发更新(Trigger)
    • 在 setdeleteProperty 拦截器中,通过 trigger(target, key) 通知所有关联的副作用重新执行。
    • 更新通过 异步队列 批量处理,减少重复渲染(类似 nextTick 机制)。


四、性能优化策略

  1. 编译优化
    静态提升(Hoist Static):将模板中的静态节点提取为常量,避免重复创建。
    预字符串化:将连续静态内容合并为字符串,减少虚拟 DOM 节点数量。

  2. 虚拟 DOM 优化
    Patch Flag:标记动态节点的属性类型(如 TEXTCLASS),仅对比变化部分。
    Block Tree:将动态内容划分为独立区块,跳过静态子树比对。

  3. Tree Shaking 支持
    • 模块化设计允许构建工具剔除未使用的代码(如未引入的 Composition API 函数)。


五、与 Composition API 的协同

通过 Composition API,响应式系统能更灵活地组织代码:

  1. 逻辑复用:将响应式数据与副作用封装为自定义 Hook(如 useFetch)。
  2. 精准控制副作用:使用 watchwatchEffect 监听数据变化,实现细粒度更新。
   const count = ref(0);
   watch(count, (newVal) => console.log('count changed:', newVal));

六、与 Vue 2 的对比总结

对比项Vue 2Vue 3
响应式实现Object.defineProperty 递归劫持Proxy 代理整个对象
动态属性支持需手动处理自动监听
数组处理需重写方法直接监听索引和 length
性能初始化性能较差惰性代理,按需触发,性能更优

总结

Vue 3 的响应式系统通过 Proxy 代理动态依赖收集编译优化,解决了 Vue 2 的局限,显著提升了性能和灵活性。其核心思想是 “按需追踪”,仅在数据被访问时建立依赖关系,而非初始化时全量劫持,这使得 Vue 3 在处理大型应用和复杂数据结构时表现更优。

评论