1、Vue3和Vue2的区别

使用了Composition API,提高了代码逻辑的可复用性。
引入了Fragment,允许组件有多个根节点。
更好的TypeScript支持。
使用了更小的包体积更高效的运行时性能
提供了Suspense组件,用于处理异步组件的加载状态。
Teleport组件允许将子组件渲染到DOM中的任何位置

2、vue2和vue3在虚拟DOM的处理上有什么不同

vue2的虚拟DOM瓶颈:

  1. 全量 Diff:每次更新需对比新旧虚拟 DOM 树的所有节点,即使某些节点是静态的。
  2. 静态节点重复创建:模板中的静态内容在每次渲染时都会重新生成虚拟节点。
  3. 事件监听冗余:事件处理函数在每次更新时可能被重新绑定
  4. 内存占用高:虚拟节点对象包含完整属性,占用内存较大。

vue3虚拟DOM的核心优化

1、编译时优化:静态提升、patch Flags(补丁标记)、Block Tree(区块树)、缓存事件处理函数

2、Diff算法优化:同层同序对比(与Vue2一致)、最长递增子序列(LIS)优化

3、组合式API协同优化:更细的响应式追踪(proxy)、复用的代码逻辑(Hook、Setup)

生产环境优化

Tree Sharking支持:vue3允许打包工具移除未使用的代码

总结

Vue 3 的虚拟 DOM 重构通过以下设计实现性能飞跃:

  1. 编译时静态分析:通过静态提升、Patch Flags、Block Tree 减少运行时计算量。
  2. 智能 Diff 算法:结合 LIS 和动态标记,最小化 DOM 操作。
  3. 响应式系统协同:精准追踪依赖,避免无效渲染。

3、Vue3中的Teleport有什么作用

Teleport是Vue 3中引入的一个新特性,它允许你将组件的DOM结构渲染到DOM树中的任何位置,而不仅仅是组件的根元素内部。这使得你可以更灵活地控制组件的渲染位置,实现一些复杂的布局和交互效果。例如,你可以将模态框(Modal)的DOM结构渲染到元素的末尾,以确保其始终位于页面的最顶层

官方文档

4、Vue 3中的Suspense是什么

Suspense是Vue 3中引入的另一个新特性,用于处理异步组件的加载过程。通过在Suspense组件中包裹异步组件,并提供一个fallback选项,你可以在异步组件加载过程中展示一个占位符或加载指示器,直到异步组件加载完成并成功渲染。这使得你可以更优雅地处理异步组件的加载和错误情况,提升用户体验

  • 通过 #default 插槽显示异步组件的内容。
  • 通过 #fallback 插槽显示加载中的备用内容。

相关文档

5、Vue3中的provide和inject是如何工作的

组件中提供数据,并在子组件中注入这些数据,从而实现了组件之间的数据传递。简单来说就是父组件向子组件传值的一个方式。

6、Vue3中Fragment是什么

Fragment 是一种容器,它允许我们在 Vue 组件中返回多个根节点。通常情况下,Vue 组件只能有一个根元素,这在某些情况下可能会导致额外的 HTML 被渲染,或者需要使用额外的包裹元素。这种设计在 Vue 2 中是一个限制。而在 Vue 3 中,Fragment 功能被引入,允许开发者能够在一个组件中返回多个并列的元素,而不需要额外的 DOM 节点来作为容器。

7、ref和reactive区别

数据类型的处理

  • **ref**:

    • 可以包装任意类型的值(基本类型、对象、数组等)。
    • 基本类型(如 string, number, boolean)会被自动包裹成响应式对象。
    • 对象或数组会被深层转换为响应式。
    javascript
    const count = ref(0); // 基本类型
    const user = ref({ name: "Alice", age: 30 }); // 对象
  • **reactive**:

    • 仅接受对象类型Object, Array, Map, Set 等)。
    • 直接返回对象的响应式代理(Proxy)。
    javascript
    const state = reactive({ count: 0, user: { name: "Alice" } });

访问方式

  • **ref**:

    • 需要通过 .value 访问或修改值(在 <template> 中自动解包,无需 .value)。
    javascript
    const count = ref(0);
    console.log(count.value); // 0
    count.value = 1;
  • **reactive**:

    • 直接访问和修改属性。
    javascript
    const state = reactive({ count: 0 });
    console.log(state.count); // 0
    state.count = 1;

深层响应式

  • **ref**:

    • 如果包裹的是对象或数组,会通过 reactive 自动递归转换为深层响应式。
    javascript
    const nested = ref({ a: { b: 1 } });
    nested.value.a.b = 2; // 响应式更新
  • **reactive**:

    • 默认递归代理所有嵌套对象,所有层级都是响应式的。
    javascript
    const state = reactive({ a: { b: 1 } });
    state.a.b = 2; // 响应式更新

适用场景

  • **ref**:

    • 适合基本类型(如数字、字符串)。
    • 需要明确引用一个响应式变量时(例如在组合式函数中返回)。
    • 需要灵活传递响应式数据(通过 .value 保持引用)。
    javascript
    // 组合式函数示例
    function useCounter() {
      const count = ref(0);
      const increment = () => count.value++;
      return { count, increment };
    }
  • **reactive**:

    • 适合复杂对象或状态树(如表单数据、全局状态)。
    • 需要直接操作属性,避免频繁使用 .value
    javascript
    // 复杂状态管理
    const formState = reactive({
      username: "",
      password: "",
      errors: {},
    });

解构与响应式丢失

  • **ref**:

    • 解构时会保留响应性,因为 .value 的引用始终不变。
    javascript
    const { value: count } = ref(0); // 仍响应式
  • **reactive**:

    • 直接解构会丢失响应性!需配合 toRefstoRef 保持响应式。
    javascript
    const state = reactive({ count: 0 });
    const { count } = state; // ❌ 解构后失去响应性
    const { count } = toRefs(state); // ✅ 保持响应性

底层实现

  • **ref**:

    • 通过一个包装对象(RefImpl)实现,内部用 getter/setter 拦截 .value 操作。
    • 如果值是对象,则调用 reactive 进行深层响应式转换。
  • **reactive**:

    • 基于 ES6 的 Proxy 实现,代理整个对象的读写操作。

总结对比表

特性**ref****reactive**
接受的类型任意类型仅对象类型(Object, Array 等)
访问方式.value(模板中自动解包)直接访问属性
深层响应式自动深层转换自动深层代理
适用场景基本类型、需要灵活引用的数据复杂对象、状态树
解构响应式保留响应式toRefs 辅助
底层实现getter/setter + reactiveProxy

如何选择?

  • 使用 ref 当:

    • 需要包装基本类型。
    • 需要明确引用一个变量(例如在组合式函数中返回)。
    • 需要灵活传递响应式数据(通过 .value)。
  • 使用 reactive 当:

    • 管理复杂对象或嵌套状态。
    • 希望直接操作属性,避免 .value

8、watch和watchEffect区别

watch是显式声明依赖,需要指定具体的监听目标

watchEffect是自动收集依赖,回调函数中的响应式变量会被自动追踪

watch:惰性执行(可以设置immediate:true)、可获取旧值、支持深度监听对象\数组(deep:true)、控制监听时机(flush:pre|post|sync)

watchEffect:立即执行、无旧值、自动深度监听

对比总结

特性**watch****watchEffect**
依赖声明显式指定依赖自动收集回调内的依赖
执行时机默认惰性(可配置immediate立即执行
旧值访问(newVal, oldVal)
深度监听deep: true自动深度监听
适用场景精确监听、对比新旧值、条件性逻辑多依赖副作用、简化代码、立即响应
副作用清理通过onCleanup通过onInvalidate

如何选择

  • 用watch

    • 需要明确知道哪个数据变化触发了回调
    • 需要旧值参与逻辑
    • 需要条件性监听延迟执行
  • 用watchEffect

    • 逻辑依赖多个响应式变量、且关系紧密
    • 希望简化代码、减少手动声明依赖
    • 需要立即执行并响应后续变化(如初始化加载)

9、Vue3如何优化性能

减少响应式依赖

  • 避免过度响应式化:对无需响应式的数据使用shallowRefshallowReactive(浅层相应)文档
  • 冻结大数据:对只读的数据(如配置表)使用object.freeze()markRaw阻止响应式转换

优化渲染性能

  • v-once: 渲染静态内容一次,避免后续更新
  • v-memo:缓存模板片段,仅在依赖项变化时重新渲染
  • 合理使用key,提供唯一且稳定的key,帮助虚拟DOM高效复用节点

组件优化

  • 异步组件(async Components)、使用defineAsyncComponent
  • keepAlive缓存、缓存组件实例、避免重复渲染

防抖节流

10、Vue3如何处理组件的异步加载

使用defineAsyncComponent定义异步组件

import { defineAsyncComponent } from 'vue';

// 基本用法
const AsyncComponent = defineAsyncComponent(() =>
  import('./components/AsyncComponent.vue')
);

// 在组件中注册
export default {
  components: {
    AsyncComponent
  }
}

defineAsyncComponent 支持配置加载中、错误、超时状态组件

const AsyncComponent = defineAsyncComponent({
  loader: () => import('./components/AsyncComponent.vue'), // 动态导入
  loadingComponent: LoadingSpinner, // 加载中的占位组件
  errorComponent: ErrorDisplay,      // 加载错误的提示组件
  delay: 200,                       // 延迟显示 loading 的时间(避免闪烁)
  timeout: 3000                     // 超时时间(超时后显示 errorComponent)
});

11、Vue2和Vue3响应式的区别以及为什么3比2好

vue2的底层技术是基于ES5的Object.defineProperty、vue3的底层技术是基于ES6的Proxy

vue2响应式的瓶颈

  • 无法监听动态属性

    • 对象属性:通过vue.set或vue.delete手动触发更新
    • 数组索引:直接通过下标修改数组不会触发更新
  • 性能瓶颈

    • 初始化递归遍历:大象对象或深层嵌套数据初始化耗时
    • 全量依赖收集:每次数据变化需要遍历所有依赖,效率低

vue3对响应式的核心优化

  • 数据监听

    • 动态属性:无需set\delete,直接响应新增或者删除的属性
    • 数组索引修改:vue2无法监听到通过下标修改数据
  • 高性能

    • 惰性代理:仅在对象访问的时候创建代理、减少初始化开销
    • 精准更新:基于Proxy的依赖追踪,避免vue2中全量对比的开销

12、Vue3生命周期有哪些变化

13、Vue3有哪些事件修饰符

14、customRef是什么东西

最后修改:2025 年 02 月 28 日
如果觉得我的文章对你有用,请随意赞赏