vue面试宝典之二

发布于:2025-03-07 ⋅ 阅读:(101) ⋅ 点赞:(0)

39.vue2和vue3中源码是如何解析模版的

new vue()的时候实例化了类之后根据传进去的option进行模版的类型div还是text还是啥进行匹配,同时拿到节点的值进行绑定,比如正则匹配{{}}将匹配到的变量拿去跟option中的data查找到具体的值在绑定在对应的html上
对于组件树则依照以下递归生成抽象语法树,然后再进行diff计算进行更新

1. 解析阶段(Parse)

Vue2

  • 实现方式:使用递归下降解析器,通过正则表达式逐层解析模板字符串。

  • 流程

    1. 分词:利用正则匹配标签名、属性、文本内容。
    2. 构建AST节点:对每个元素创建 AST 节点,处理指令(如 v-if)、插值({{}})。
    3. 层级嵌套:通过栈结构维护父级关系,解析子节点时递归调用。
  • 关键方法parseHTML 处理字符串,parseStartTag 解析开始标签。

Vue3

  • 优化点:基于状态机的解析器,减少正则依赖,提升性能。

  • 流程

    1. 逐字符扫描:直接遍历模板字符串,显著减少回溯消耗。
    2. 动态标记:使用位运算记录节点类型(如元素、插值、纯文本),加快后续处理。
    3. 更多语法支持:直接处理 <Teleport><Suspense> 等新特性。
  • 关键改进:解析速度提升约 40%,内存占用更少。

2. 优化阶段(Optimize)

Vue2

  • 静态标记:遍历所有 AST 节点,标记静态子树(无响应式依赖的节点)。
  • 结果static: true 标志,Diff 时跳过这些节点。
  • 限制:仅跳过整个子树,嵌套动态节点时优化效果有限。

Vue3

  • Block Tree 优化

    • Block节点:包裹动态内容,仅追踪内部动态子节点(如带 v-forv-if 的节点)。
    • PatchFlag 标记:在生成代码时为动态节点添加标志(如 TEXTCLASSPROPS),精准定位变化点。
  • 静态提升(Hoisting)

    • 将纯静态节点(如 `
      Hello
      提高至渲染函数外部,避免重复创建。
    • 效果:减少 VNode 创建开销,内存占用优化约 20%。
3. 代码生成(Generate)

Vue2

  • 生成代码:将 AST 转化为 createElement 调用嵌套结构的字符串。

  • 示例代码

    _c('div', { attrs: { id: 'app' } }, [_v("Hello " + _s(name))])
    
  • 问题:全量生成 VNode,包含静态节点。

Vue3

  • 按需生成

    • Block 树:动态节点按需生成更新逻辑,静态内容直接复用。
    • PatchFlag 嵌入生成:基于动态标记生成针对性更新代码。
  • 示例代码

    createBlock('div', { id: 'app' }, [
      createVNode('span', null, _toDisplayString(name), PatchFlags.TEXT)
    ])
    
  • 优化结果:渲染函数体积更小,运行更快。

4. 性能差异
  • 编译速度:Vue3 模板编译比 Vue2 快约 2 倍(基准测试数据)。
  • 运行时性能:初次渲染快 30%~50%,更新性能提升 1.5~2 倍。
  • 体积:Vue3 编译器生成的代码体积减少约 40%。
5. 新特性支持
  • Vue3 独有

    • 支持 <script setup> 编译时语法糖。
    • 处理 Fragments(多根节点)、Teleport 等结构的 AST 转换。
    • 更精细的编译时警告(如未使用变量提示)。

总结

  • Vue2:基于正则和递归下降解析,生成较通用的渲染函数,静态优化有限。
  • Vue3:采用逐字符扫描和 Block Tree,结合 PatchFlag,实现精准更新。编译时优化程度更高,直接提升运行时性能。

40.vue中生命周期源码如何实现

通过在vue类中写生命周期方法,在对应该执行的地方进行调用,当组件被实例化后按需求调用

41. Vue v-model 双向数据绑定的原理(面试版)

v-model 本质上是 数据绑定 (value) + 事件监听 (@input@update:xxx) ,实现 数据和视图的双向同步。其工作原理在 Vue 2 和 Vue 3 略有不同:

  1. Vue 2 原理:

    • 依赖 Object.defineProperty() 进行数据劫持,监听 dataget/set
    • v-model 在表单元素上解析为 :value="data" + @input="data = $event.target.value"。劫持到v-model这个属性时对该属性绑定的元素进行试图更行,更新什么呢 就需要通过Object.defineProperty()对数据的改变进行劫持然后去更新
    • Watcher 监听数据变化,触发 set 时更新视图,实现 Model → View
    • 视图变化(用户输入)时,触发 input 事件,修改 data,实现 View → Model
  2. Vue 3 原理:

    • 采用 Proxy 代理 data,监听 get/set,提升性能,支持深层响应式。
    • v-model 解析为 :prop="data" + @update:prop="data = $event",允许绑定多个 v-model 变量。
    • Vue 3 引入 defineModel() 语法糖,简化组件 v-model 绑定,提高开发体验。

核心区别:

  • Vue 2 依赖 Object.defineProperty(),Vue 3 采用 Proxy,性能更高。
  • Vue 3 允许 v-model 绑定多个变量,事件机制从 @input 改为 @update:xxx
  • Vue 3 提供 defineModel() 语法糖,简化组件 v-model 绑定逻辑。

总结一句话:
Vue v-model 通过 数据绑定 + 事件监听 实现双向数据同步,Vue 2 依赖 Object.defineProperty() + input 事件,Vue 3 采用 Proxy + update:xxx 事件,提供更强大的数据绑定能力。

42.vue的diff算法

Vue 的 diff 算法用来高效更新 DOM,通过对新旧虚拟 DOM 树的差异对比,最小化真实 DOM 操作。以下是其核心机制和优化策略:
patch函数进行对比,h函数生成虚拟dom


1. 层级比较策略

  • 同层级比较:只在同一父节点下递归比较子节点,跨层级的节点直接销毁重建。
  • 时间复杂度优化:将 O(n³) 的树对比复杂度降为 O(n),适合大部分场景。

2. 双端对比算法

通过 两端四个指针(旧头尾、新头尾) 逐步缩小范围,优先处理常见顺序变化(如头尾插入):

  1. 头头对比:若旧头和新头为同一节点,直接复用,指针后移。
  2. 尾尾对比:若旧尾和新尾为同一节点,复用后指针前移。
  3. 旧头-新尾对比:若相同,将旧头节点移到旧尾之后,指针移动。
  4. 旧尾-新头对比:若相同,将旧尾节点移到旧头之前,指针移动。

若上述均不匹配,则通过 key 映射到旧节点数组查找


3. Key 的作用

  • 避免就地复用:通过唯一 key 标识节点身份,确保列表变更时(如排序、增删),Vue 能精准匹配节点,避免状态错乱。若无 key,Vue 根据位置变化复用节点,可能导致渲染问题(例如表单输入的错位)。

4. 剩余节点处理

  • 新旧节点列表遍历完成后:

    • 旧列表剩余:删除多余 DOM。
    • 新列表剩余:新增节点插入到正确位置。

5. 静态节点优化

  • 编译阶段标记静态节点(无动态绑定),跳过其 diff 过程,减少对比次数。

6. 组件复用机制

  • 相同类型的组件会通过 key 复用实例,触发更新而非销毁重建,保留内部状态(如输入框内容)。

性能影响与最佳实践

  • 合理使用 Key:在动态列表中,必填唯一且稳定的 key
  • 避免跨层级结构变化:如 v-ifv-for 混用不当可能导致组件重建。
  • 利用 Keep-Alive:缓存不活跃组件实例减少重复渲染。

示例场景

比如新旧子节点为 [A, B, C, D][A, D, B, C]

  1. 头头对比(A=A)复用,指针后移。
  2. 新头 D 与旧头 B 不匹配,转双端对比:旧尾 D 与新头 D 匹配,将 D 移到 B 前。
  3. 继续处理剩余节点,最终仅移动 D 一次而非重建。

通过上述策略,Vue 的 diff 算法以较低成本完成高效更新,成为响应式系统的核心优化手段。

43 vue2 和 vue3的diff算法区别

Vue 3 的 diff 算法在 Vue 2 的双端对比基础上,通过编译阶段的优化更细粒度的动态追踪,显著减少了对比范围,整体性能更高。以下是核心区别:


1. 编译优化降低 Diff 范围

  • 静态提升(Hoist Static)
    将纯静态的节点/属性提升到渲染函数外,生成一次后复用,不再参与每次 Diff。

  • 区块树(Block Tree)

    • 以「动态节点」为根的树结构(Block)将模板划分为动态区块,更新时只需遍历动态内容,静态部分直接跳过。
    • 层级更扁平,减少「跨层级移动」的误判。

2. Patch Flags 精准对比属性

在编译阶段对动态节点标记 PatchFlag(如 TEXTCLASSPROPS),Diff 时直接根据标记仅对比变化的属性。例如:

// 编译生成的代码片段(示意)
createVNode("div", {
  class: _normalizeClass({ active: isActive }),
  style: _normalizeStyle({ color: userColor })
}, null, 12 /* CLASS | STYLE */)

此处 12 表示需要检查 classstyle,其他属性忽略。


3. 缓存优化减少对比次数

  • 事件缓存(Cache Handler)
    若事件绑定为内联函数(如 @click="handleClick"),Vue 3 自动缓存函数,避免触发子组件无意义更新。
  • 动态节点缓存
    同一模板中的动态内容(如 v-if/v-else)复用已创建的节点,减少销毁重建。

4. 双端对比的优化调整

Vue 3 保留了双端对比,但结合动态标记优化流程:

  1. 头尾对比效率更高,因 PatchFlag 可快速跳过相同节点。
  2. 仅文本变化等场景直接更新,无需递归子节点。
  3. 对有 key 的列表,优先复用相同 key 节点,跨层级移动更精确。

5. 响应式优化减少触发 Diff

  • Vue 3 使用 Proxy 替代 Object.defineProperty,避免了全量递归响应式初始化。
  • 依赖追踪更细粒度,非必要的数据变化不会触发组件重新渲染。

性能对比示例

假设一个列表更新场景:

  • Vue 2:需遍历所有子节点,通过双端对比找出差异。
  • Vue 3:通过 Block Tree 仅对比动态部分,结合 PatchFlag 快速定位文本或属性变化,路径更短。

总结区别

特性 Vue 2 Vue 3
静态内容处理 全量对比 提升并跳过,不参与 Diff
动态对比粒度 节点级别 属性级别(通过 PatchFlag)
列表更新 双端对比为主,可能误判跨层级移动 Block 树优化,减少多余操作
事件处理器 每次触发更新 自动缓存,避免子组件更新
响应式影响 Diff 触发全量对比 更精准的依赖收集,减少不必要更新

最佳实践

  • 合理使用 key:清晰标识动态列表项,避免复用问题。
  • 避免深层嵌套:利用 Vue 3 的 Block Tree 特性减少对比深度。
  • 减少不必要的动态绑定:静态内容尽量保持简洁,以触发静态提升。

通过编译时优化和运行时策略的结合,Vue 3 的 Diff 算法在大多数场景下的性能明显优于 Vue 2,尤其适用于大型动态应用。

44.vue和react 的虚拟dom有和区别,以及各自的diff算法的区别

Vue 与 React 的虚拟 DOM 及 Diff 算法对比


一、虚拟 DOM 的核心区别
特性 Vue React
生成方式 基于模板编译,编译时优化明显(如静态提升、区块标记) 基于 JSX 生成常规虚拟 DOM 树,依赖运行时优化
节点标记 通过 PatchFlag 标记动态内容(如属性/文本变化),精确追踪变更 无内置动态标记机制,需全量对比节点
静态优化 静态节点提升为常量,直接复用,无需 Diff 在最新版本中引入 react/compiler-runtime(实验性)尝试静态优化
响应式机制影响 配合依赖追踪,精准判定组件更新范围 依赖状态变更手动触发组件重渲染,可能需 memoPureComponent 优化

二、Diff 算法策略对比
1. Vue 的 Diff 算法
  • 双端对比 + 动态标记

    • 首尾四指针对比,处理常见顺序变动(如头部插入、尾部删除)。
    • 编译时对动态内容标记 PatchFlag,降低对比粒度(如仅检查 classstyle)。
  • Block Tree 优化

    • 将动态内容划分为 Block(区块),更新时仅遍历动态区块,跳过静态内容。
    • 列表对比时复用带 key 的节点,减少跨层级移动。
  • 优势:适合模板驱动的场景,编译优化显著减少无效对比,高效处理局部更新。

2. React 的 Diff 算法
  • 传统 Diff 策略(React 16 前):

    • 逐层递归,同层级按索引对比。若元素类型不同,直接卸载重建。
    • 对列表使用 key 优化,基于 key 匹配旧节点,减少位置变动时的性能损耗。
  • Fiber 架构优化(React 16+):

    • 将 Diff 过程拆分为可中断的链表遍历,支持优先级调度和异步渲染。
    • 修复了深度优先递归可能导致的主线程阻塞问题。
  • 优势:更适合需要精细化控制渲染过程的应用(如复杂动画交互),但在动态列表中间插入时性能成本较高。


三、Key 的作用与差异
框架 无 Key 时的行为 有 Key 时的优化策略
Vue 根据节点类型及顺序尝试就地复用,可能导致状态错乱(如输入框错位) 精准匹配相同 Key 的节点,跨层级移动时复用 DOM 并保持状态
React 同类节点按索引直接复用,可能误判更新 通过 Key 标识同一节点,避免不必要的销毁重建

四、性能场景对比
  • 列表中间插入

    • Vue:双端对比 + Block Tree 快速定位插入点,仅移动相邻节点。
    • React:默认按索引对比,可能导致大量节点向后位移,性能损耗较大(需依赖 Key 优化)。
  • 静态内容更新

    • Vue:跳过静态区块的对比(编译时优化)。
    • React:需手动用 memo 跳过或依赖实验性编译优化。

五、总结与选型建议
框架 适用场景 优化方向
Vue 模板驱动、局部更新频繁的应用(如CMS、表单工具) 依赖内置编译优化,适合快速开发,无需手动优化
React 复杂交互、需精细调度渲染的应用(如大型SPA、动画) 需结合 memouseCallback 或 Fiber 调度优化

示例代码对比

// React 列表渲染(依赖 key 优化)
{items.map((item) => (
  <div key={item.id}>{item.text}</div>
))}

// Vue 列表渲染(key + v-for)
<div v-for="item in items" :key="item.id">{{ item.text }}</div>

核心差异
Vue 的虚拟 DOM 和 Diff 通过编译阶段提前优化(如静态提升、区块标记),运行时仅处理动态部分;
React 则依赖运行时策略 + 组件级优化,通过 Fiber 架构实现可中断的异步渲染,适合复杂调度场景。

45.v-for 的key 有什么作用

v-forkey 有什么作用?(面试版)

1. key 的作用

key 的主要作用是唯一标识列表中的每个节点,帮助 Vue 在进行 DOM Diff 时更高效地更新视图

2. key 的作用机制
  • 优化 Diff 过程:Vue 通过 key 识别元素是否复用或需要重新渲染,避免不必要的 DOM 操作,提高性能。
  • 保持组件状态:如果不使用 key,Vue 可能会错误复用组件,导致状态错乱。
  • 避免渲染异常:当 key 不唯一或缺失时,可能导致列表更新时数据错位。
3. key 的 Diff 影响

Vue 采用双端 Diff 算法,如果 key 唯一且稳定:

  • 只修改必要的节点,不重新创建整个列表,优化渲染性能。
  • 如果 key 不唯一或缺失,Vue 可能使用错误的复用策略,导致错误更新频繁销毁重建
4. 示例:key 影响列表更新

错误示例(缺少 key

<li v-for="item in list">{{ item.text }}</li>

Vue 可能错误复用旧节点,导致渲染错乱。

正确示例(使用 key

<li v-for="item in list" :key="item.id">{{ item.text }}</li>

Vue 识别 id 变化,仅更新必要部分,保持性能最佳。

5. key 的最佳实践
  • key 应该是唯一且稳定的 ID(如数据库 id)。

  • 避免使用索引 index 作为 key,因为索引变化会导致错误复用,影响渲染:

    <li v-for="(item, index) in list" :key="index">{{ item.text }}</li>
    

    ⚠️ 如果列表有新增/删除,索引会变动,导致 Vue 误判,影响性能或导致错误 UI 变化

6. 结论

key 的作用是优化 Diff 算法,提高列表更新效率,避免错误复用,确保渲染正确!
面试时可以这样总结:

keyv-for 中用于唯一标识节点,帮助 Vue 更高效地进行 Diff 运算,避免不必要的 DOM 变更,提高渲染性能,同时确保组件状态正确复用。”

46.vue中的修饰符有哪些

Vue 中的修饰符(面试版)

Vue 提供 指令修饰符(Modifiers) ,用于 增强事件、指令和表单绑定的功能,以更简洁的方式处理逻辑。


1. 事件修饰符

作用:防止默认行为、阻止事件冒泡、事件绑定控制等。

修饰符 作用
.stop 阻止事件冒泡 (event.stopPropagation())
.prevent 阻止默认行为 (event.preventDefault())
.capture 使用事件捕获模式 (addEventListener{ capture: true })
.self 仅触发绑定事件的元素本身(不冒泡)
.once 事件只触发一次
.passive 提高滚动性能 ({ passive: true },不能与 .prevent 一起用)

示例

<button @click.stop="handleClick">阻止冒泡</button>
<form @submit.prevent="submitForm">阻止默认提交</form>
<div @click.capture="log">使用捕获模式</div>

2. 按键修饰符

作用:只在特定按键触发事件。

修饰符 作用
.enter Enter 键
.tab Tab 键
.delete Delete/Backspace 键
.esc Escape 键
.space 空格键
.up .down .left .right 方向键
.exact 只在指定按键组合触发

示例

<input @keyup.enter="search">
<input @keyup.delete="deleteItem">
<input @keydown.ctrl.enter="submit">
<button @click.ctrl.exact="onlyCtrlClick">仅 Ctrl 生效</button>

3. 鼠标修饰符

作用:限制鼠标事件。

修饰符 作用
.left 仅限左键点击
.right 仅限右键点击
.middle 仅限鼠标中键

示例

<button @click.right="showMenu">右键点击</button>

4. 表单修饰符

作用:优化表单输入体验。

修饰符 作用
.lazy 失去焦点时(change 事件)更新数据
.number 自动转换输入值为 Number 类型
.trim 自动去除输入首尾空格

示例

<input v-model.lazy="name"> <!-- 失去焦点才更新 -->
<input v-model.number="age"> <!-- 自动转数字 -->
<input v-model.trim="email"> <!-- 自动去空格 -->

5. v-bind 修饰符

作用:控制属性的动态绑定行为。

修饰符 作用
.prop 绑定 DOM 原生属性 (el.propName = value)
.camel 适用于 SVG,将 kebab-casecamelCase

示例

<div v-bind:innerHTML.prop="rawHtml"></div>
<svg v-bind:viewBox.camel="viewBoxValue"></svg>

6. v-on 修饰符

作用:监听原生事件。

修饰符 作用
.native 监听原生事件(用于 v-on 组件事件)

示例

<custom-button @click.native="handleClick"></custom-button>

7. v-model 修饰符(Vue 2 & Vue 3)

Vue 2

<input v-model.lazy="message">
<input v-model.number="age">
<input v-model.trim="email">

Vue 3

修饰符 作用
.lazy 失去焦点后更新数据
.number 自动转换为 Number
.trim 自动去空格
.capitalize Vue 3 自定义修饰符示例(手动实现)

Vue 3 允许自定义 v-model 修饰符

<Child v-model.capitalize="text" />

子组件:

<template>
  <input :value="modelValue" @input="$emit('update:modelValue', $event.target.value.toUpperCase())">
</template>
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>

总结(面试时的回答)

Vue 修饰符主要用于优化事件监听、表单绑定和属性动态绑定,提升代码可读性和性能。常见的修饰符包括:

  1. 事件修饰符.stop.prevent.once 等)优化事件行为,避免额外 DOM 操作。
  2. 按键修饰符.enter.esc 等)提高键盘事件的精准控制。
  3. 鼠标修饰符.left.right 等)限制鼠标事件类型。
  4. 表单修饰符.lazy.number.trim)优化 v-model 绑定数据的方式。
  5. v-bind 修饰符.prop.camel)优化动态属性的绑定方式。
  6. v-on 修饰符.native)用于监听组件的原生事件。

面试官可能会问:

“为什么 v-for 遍历时要加 key?”

你可以补充:

v-forkey 不是修饰符,但它用于优化 Vue 的 Diff 算法,减少不必要的 DOM 变更,提高渲染性能,避免状态错乱。” 😊

47.如何解决刷新后二次加载路由?什么是刷新后二次加载路由

什么是“刷新后二次加载路由”?

刷新后二次加载路由,指的是在 Vue 项目中,用户刷新页面后,路由会重新解析并加载,但由于某些原因(如异步路由、权限控制、动态添加路由等),导致路由数据丢失或需要二次加载,甚至页面 404 或白屏的问题。

这种情况通常发生在:

  1. 动态路由未持久化(如基于权限的 addRoute() 添加路由)。
  2. Vuex/Pinia 状态丢失(状态管理库默认存储在内存中,刷新后丢失)。
  3. 路由模式问题history 模式可能导致找不到路由)。
  4. 后端路由权限控制(需要重新请求路由数据)。

如何解决刷新后二次加载路由?

根据问题的成因,常见的解决方案如下:


1. 持久化动态路由

问题

如果路由是动态添加的(如基于用户权限 router.addRoute()),刷新后 Vue Router 会重置,导致 动态路由丢失

解决方案

  • 刷新时,从本地存储获取权限信息,重新添加路由
  • 确保在路由守卫(beforeEach)中进行动态加载
示例:动态路由持久化
// src/router/index.ts
import router from './index';
import { getUserRoutes } from '@/api/user'; // 模拟后端返回的动态路由数据
import { useUserStore } from '@/store/user';

export async function setupDynamicRoutes() {
  const userStore = useUserStore();
  const savedRoutes = localStorage.getItem('userRoutes');

  // 如果 Vuex/Pinia 中没有路由,尝试从本地存储恢复
  if (!userStore.routes.length && savedRoutes) {
    userStore.setRoutes(JSON.parse(savedRoutes));
  }

  // 遍历动态路由并添加
  userStore.routes.forEach(route => {
    router.addRoute(route);
  });
}

// 组件挂载时调用
setupDynamicRoutes();
原理
  1. 刷新时,从本地存储获取权限数据
  2. beforeEach 守卫或 setupDynamicRoutes 里动态添加路由
  3. 确保动态路由不会因刷新而丢失

2. 持久化 Vuex/Pinia 数据

问题

Vuex/Pinia 状态默认存储在内存中,刷新后会丢失数据,导致依赖 Vuex/Pinia 存储的权限、用户信息、动态路由丢失

解决方案

使用 localStorage / sessionStorage / pinia-plugin-persistedstate 持久化数据。

示例:Pinia 持久化
import { defineStore } from 'pinia';

export const useUserStore = defineStore('user', {
  state: () => ({
    routes: []
  }),
  actions: {
    setRoutes(newRoutes) {
      this.routes = newRoutes;
      localStorage.setItem('userRoutes', JSON.stringify(newRoutes));
    }
  },
  persist: true // 持久化存储
});
原理
  1. 持久化 routes 数据,避免刷新后丢失
  2. 配合动态路由,在 setupDynamicRoutes 里重新加载

3. 确保后端权限数据提前加载

问题

如果权限路由是从后端获取的,但 Vue Router 先初始化,可能会导致页面加载时找不到匹配路由,出现 404

解决方案

  • 路由初始化前,先获取用户权限和路由信息
  • 使用 beforeEach 守卫拦截并等待权限加载完成
示例
router.beforeEach(async (to, from, next) => {
  const userStore = useUserStore();

  if (!userStore.routes.length) {
    // 获取用户权限 & 路由信息
    await userStore.fetchUserRoutes();
    setupDynamicRoutes(); // 重新添加动态路由
    next({ ...to, replace: true }); // 重新导航,确保新路由生效
  } else {
    next();
  }
});
原理
  • 刷新后,如果 userStore.routes 为空,重新请求后端路由数据
  • 动态注册后,next({ ...to, replace: true }) 重新跳转,避免 404
  • 确保路由加载完毕后,才进入页面

4. history 模式导致的 404 问题

问题

如果 Vue Router 使用 history 模式,直接在浏览器输入 URL 可能导致 服务器找不到该路由,返回 404

解决方案

  • 后端需要配置 fallback 规则,所有请求都返回 index.html
  • Vue Router 可使用 hash 模式,避免服务器找不到路径
Nginx 配置示例
location / {
  try_files $uri /index.html;
}
Vue Router 变更为 hash 模式
const router = createRouter({
  history: createWebHashHistory(), // 改用 hash 模式
  routes
});

5. 路由懒加载,避免二次加载

问题

如果 Vue 采用懒加载路由,但没有正确处理异步路由,可能导致 刷新后部分路由二次加载,影响用户体验。

解决方案

  • 确保 import() 路由时,配合动态注册
  • 避免 beforeEach 里重复执行 addRoute
示例
const routes = [
  {
    path: '/',
    component: () => import('@/views/Home.vue') // 懒加载
  },
  {
    path: '/dashboard',
    component: () => import('@/views/Dashboard.vue')
  }
];

const router = createRouter({
  history: createWebHistory(),
  routes
});

避免二次加载:

router.beforeEach(async (to, from, next) => {
  if (!router.hasRoute(to.name)) {
    await setupDynamicRoutes(); // 确保只添加一次
    next({ ...to, replace: true });
  } else {
    next();
  }
});

总结(面试版回答)

刷新后二次加载路由,通常是由于动态路由未持久化、Vuex/Pinia 状态丢失、后端路由未提前加载或 history 模式 404 导致的。
解决方案包括:

  1. 持久化动态路由,刷新时重新 addRoute()
  2. 持久化 Vuex/Pinia 数据,避免状态丢失
  3. 确保后端权限数据提前加载,在 beforeEach 里拦截处理
  4. 避免 history 模式 404,通过 Nginx 配置 try_files
  5. 正确使用路由懒加载,避免二次 addRoute()

48.vuex刷新数据会丢失吗?怎么解决?

Vuex 刷新数据会丢失吗?为什么?

❌ 是的,Vuex 数据会在页面刷新后丢失

原因:Vuex 的数据存储在 内存(RAM) 中,而 页面刷新会导致整个 Vue 实例重新初始化,Vuex 也会被重置,导致数据丢失。

🔥 解决方案(面试高频回答)

解决 Vuex 刷新数据丢失的问题,主要有 3 种方式


✅ 方案 1:使用 localStorage / sessionStorage 持久化

原理:在 Vuex mutation 中存储数据到 localStorage/sessionStorage,刷新后 从本地存储恢复 Vuex 状态

示例

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    userInfo: JSON.parse(localStorage.getItem('userInfo')) || {} // 先从本地存储恢复数据
  },
  mutations: {
    setUser(state, user) {
      state.userInfo = user;
      localStorage.setItem('userInfo', JSON.stringify(user)); // 存储到 localStorage
    }
  }
});

📌 方案解析

优点:简单、兼容性好,适用于小型项目
缺点:只能存储基本数据类型,无法存储复杂对象(如 Vue 组件实例)


✅ 方案 2:使用 vuex-persistedstate

原理vuex-persistedstate 是一个插件,自动同步 Vuex 状态到 localStorage/sessionStorage,无需手动操作。

安装

npm install vuex-persistedstate

使用

import Vue from 'vue';
import Vuex from 'vuex';
import createPersistedState from 'vuex-persistedstate';

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    token: '',
    userInfo: {}
  },
  mutations: {
    setToken(state, token) {
      state.token = token;
    },
    setUserInfo(state, userInfo) {
      state.userInfo = userInfo;
    }
  },
  plugins: [createPersistedState()] // 添加插件
});

📌 方案解析

优点

  • 无需手动操作 localStorage,自动持久化
  • 支持多个 state 存储
  • 可配置存储方式(sessionStoragelocalStorage

缺点

  • 可能会影响性能(持久化大数据对象时)
  • 存储到 localStorage 的数据是明文,敏感数据(如 Token)需要加密

✅ 方案 3:使用 Pinia(Vue 3 推荐)

Vue 3 官方推荐使用 Pinia 作为 Vuex 的替代方案,它 默认支持持久化存储

安装

npm install pinia

使用

import { defineStore } from 'pinia';
import { createPinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';

// 创建 Pinia 实例
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);

// 创建 store
export const useUserStore = defineStore('user', {
  state: () => ({
    token: '',
    userInfo: {}
  }),
  actions: {
    setToken(token) {
      this.token = token;
    },
    setUserInfo(userInfo) {
      this.userInfo = userInfo;
    }
  },
  persist: true // 直接开启持久化
});

Vue 3 项目中,Pinia 是更推荐的状态管理方案!


🔍 总结(面试最佳回答)

Vuex 刷新后数据会丢失,因为它存储在内存中,页面刷新会重新初始化 Vuex 状态。
解决方案包括: 1️⃣ 使用 localStorage/sessionStorage 持久化 Vuex 数据 2️⃣ 使用 vuex-persistedstate 插件,自动存储和恢复 Vuex 状态 3️⃣ 在 Vue 3 中,使用 Pinia(官方推荐),并开启 persist 持久化

💡 面试官追问:哪种方案更推荐?

小型项目可用 localStorage,大型项目推荐 vuex-persistedstate,Vue 3 直接用 Pinia,更轻量、性能更优。 🚀

49.computed和watch的区别?

💡 computed 和 watch 的区别(面试高频考点)

在 Vue 中,computedwatch 都可以用于监听数据变化,但它们的应用场景和工作原理不同。


✅ 1. 核心区别

对比点 computed 计算属性 watch 侦听器
触发方式 依赖的值变化时自动更新 手动监听某个值的变化
是否有缓存 有缓存,依赖值不变时不会重新计算 无缓存,值变化时总会执行
适用场景 值是基于其他值计算而来(如数据派生、格式化) 执行异步操作、副作用操作(如 API 请求、手动控制逻辑)
返回值 必须返回一个值(getter 形式) 可以执行方法,不一定有返回值

✅ 2. computed(计算属性)—— 适用于数据派生

特点:

  • 基于依赖数据自动计算,如果依赖的数据不变,则不会重复计算(有缓存)。
  • 适用于 计算值、数据格式化、依赖多个数据的计算

🌟 示例 1:computed 实现数据派生

<script setup>
import { ref, computed } from "vue";

const firstName = ref("张");
const lastName = ref("三");

// 计算属性:拼接全名
const fullName = computed(() => `${firstName.value} ${lastName.value}`);
</script>

<template>
  <p>姓名:{{ fullName }}</p>
</template>

💡 关键点

fullName 依赖于 firstNamelastName,当其中一个变化时,computed 只会重新计算一次
有缓存,如果 firstNamelastName 没变,则不会重复执行计算。


✅ 3. watch(侦听器)—— 适用于副作用

特点:

  • 监听数据变化后执行函数(无缓存)。
  • 适用于异步操作(如 API 请求)或手动控制逻辑

🌟 示例 2:watch 监听数据变化,执行异步请求

<script setup>
import { ref, watch } from "vue";

const keyword = ref("Vue");

// 监听 keyword 变化,触发 API 请求
watch(keyword, async (newVal, oldVal) => {
  console.log(`搜索关键词变化:${oldVal} → ${newVal}`);
  await fetch(`https://api.example.com/search?q=${newVal}`);
});
</script>

<template>
  <input v-model="keyword" placeholder="输入关键词" />
</template>

💡 关键点

监听 keyword 变化后,执行 API 请求(副作用)。
无缓存,每次 keyword 变化都会触发 watch 逻辑
🚀 适用于异步请求、定时器、手动控制逻辑等。


✅ 4. 什么时候用 computed,什么时候用 watch

场景 computed watch
计算派生数据(如格式化、拼接)
监听数据变化并触发 API 请求
监听多个数据变化并执行逻辑
依赖数据不变时,希望缓存结果
处理异步操作(如 setTimeoutfetch

✅ 5. watchEffect vs watch

Vue 3 还提供了 watchEffect,它是 watch 的增强版,自动收集依赖,不用手动指定监听变量。

🌟 示例 3:watchEffect 自动收集依赖

<script setup>
import { ref, watchEffect } from "vue";

const count = ref(0);

watchEffect(() => {
  console.log(`count 变化了:${count.value}`);
});
</script>

<template>
  <button @click="count++">增加</button>
</template>

💡 watchEffect 适用场景

  • 自动收集依赖,不用手动指定监听值
  • 适合处理副作用(如订阅数据、监听多个变量)

✅ 6. 面试最佳回答

computed vs watch 的区别?

  • computed 适用于计算派生数据,基于依赖变化自动更新,并且有缓存
  • watch 适用于监听数据变化,执行副作用操作(如 API 请求、定时器)无缓存
  • Vue 3 还有 watchEffect自动收集依赖,适用于全局监听副作用。

🔥 总结

  • computed 适用于计算派生数据(有缓存)。
  • watch 适用于监听数据变化并执行副作用(无缓存)。
  • Vue 3 watchEffect 适用于自动收集依赖的副作用

👉 面试官可能追问:

  1. computed 什么时候触发?

    • 依赖的数据变化时才会触发,而且有缓存,不会重复计算。
  2. computed 和 watch 的性能对比?

    • computed 性能更好,因为它有缓存,只有依赖变化时才会重新计算。
    • watch 每次值变更都会触发回调,可能导致不必要的计算。

50. vuex在什么场景会去使用?属性有哪些?

🔥 Vuex 使用场景及核心属性(面试高频考点)

Vuex 是 Vue 2 官方推荐的全局状态管理库,适用于多个组件共享状态的场景。在 Vue 3 中,Vuex 逐渐被 Pinia 取代,但仍然在很多 Vue 2 项目中广泛使用。


✅ 1. Vuex 适用场景

📌 什么时候应该使用 Vuex?

Vuex 适用于多个组件之间需要共享状态的情况,比如:

  1. 用户登录信息管理(如 tokenuserInfo
  2. 购物车数据管理(多个组件共享购物车数据)
  3. 权限控制(不同角色展示不同页面)
  4. 跨组件通信(避免 props 传递层级过深)
  5. 缓存数据,避免重复请求(如分页数据、搜索结果)

🌟 示例:Vuex 适用于全局用户状态管理

const store = new Vuex.Store({
  state: {
    userInfo: null
  },
  mutations: {
    setUser(state, user) {
      state.userInfo = user;
    }
  }
});

📌 场景:userInfo 需要在多个组件(如 Navbar、Profile、Settings)中共享时,Vuex 是最佳选择。


✅ 2. Vuex 核心属性

Vuex 由 五个核心属性 组成:stategettersmutationsactionsmodules

属性 作用
state 存储全局状态(如用户信息、购物车数据)
getters 从 state 派生数据(类似 computed)
mutations 同步修改 state(唯一能直接改 state 的方式)
actions 异步操作(如 API 请求,然后提交 mutations 修改 state
modules 模块化 Vuex,适用于大型项目

✅ 3. Vuex 详细示例

📌 state(存储全局状态)

const store = new Vuex.Store({
  state: {
    userInfo: { name: "张三", role: "admin" }
  }
});
  • 作用:集中存储全局数据,多个组件可以共享。

📌 getters(从 state 派生数据,类似 computed)

const store = new Vuex.Store({
  state: {
    cart: [{ id: 1, name: "苹果", price: 5 }, { id: 2, name: "香蕉", price: 3 }]
  },
  getters: {
    totalPrice: state => state.cart.reduce((sum, item) => sum + item.price, 0)
  }
});

使用:

<p>总价:{{ $store.getters.totalPrice }}</p>
  • 作用:对 state 进行计算,不会修改 state 本身。
  • 类似 computed,如果 state 没变,getters 也不会重新计算(有缓存)。

📌 mutations(唯一修改 state 的方式,必须同步)

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state, payload) {
      state.count += payload;
    }
  }
});

使用:

<button @click="$store.commit('increment', 5)">增加 5</button>
  • 作用:修改 state必须是同步的
  • 参数 payload:可以传递额外参数(如 5)。

📌 actions(处理异步逻辑,提交 mutations 修改 state)

const store = new Vuex.Store({
  state: {
    userInfo: null
  },
  mutations: {
    setUser(state, user) {
      state.userInfo = user;
    }
  },
  actions: {
    async fetchUser({ commit }) {
      const user = await fetch("/api/user").then(res => res.json());
      commit("setUser", user);
    }
  }
});

使用:

<button @click="$store.dispatch('fetchUser')">获取用户信息</button>
  • 作用:用于处理异步请求,然后提交 mutations 修改 state
  • 必须用 dispatch 调用(不能用 commit)。

📌 modules(模块化 Vuex,适用于大型项目)

const userModule = {
  state: { userInfo: null },
  mutations: { setUser(state, user) { state.userInfo = user; } }
};

const store = new Vuex.Store({
  modules: {
    user: userModule
  }
});

使用:

<p>用户:{{ $store.state.user.userInfo }}</p>
  • 作用:将 Vuex 按功能拆分多个模块,便于管理。

✅ 4. 面试最佳回答

Vuex 适用于多个组件共享状态的场景,例如用户信息、购物车数据、权限控制等。
Vuex 有 5 个核心属性:

  • state:存储全局数据
  • getters:从 state 计算派生数据(类似 computed)
  • mutations:唯一修改 state 的方法(必须同步)
  • actions:用于执行异步操作(如 API 请求)
  • modules:模块化 Vuex,适用于大型项目

✅ 5. Vue3 还适合用 Vuex 吗?

🚀 Vue3 推荐使用 Pinia 代替 Vuex,因为:

  • Pinia 更轻量(API 更简单)
  • 默认支持持久化存储
  • 支持 TypeScript 更友好
  • 支持更好的开发体验(Devtools)

如果是新项目,建议直接用 Pinia! 🎯


🎯 总结

问题 答案
Vuex 适用于什么场景? 多个组件共享状态,如用户信息、购物车、权限管理等。
Vuex 的核心属性有哪些? stategettersmutationsactionsmodules
mutationsactions 的区别? mutations 只能同步actions 可以异步
Vue 3 还用 Vuex 吗? 推荐用 Pinia,更轻量、性能更好。

51. vue和jquery的区别是什么?

🔥 Vue 和 jQuery 的区别(面试高频考点)

Vue 和 jQuery 是两种完全不同的前端开发方式,主要体现在 编程范式、数据驱动方式、组件化开发、性能优化等方面。面试中,掌握它们的核心区别,能让你更有竞争力!💪🚀


✅ 1. Vue vs jQuery 的核心区别

对比点 Vue(现代框架) jQuery(传统库)
编程范式 声明式(数据驱动) 命令式(操作 DOM)
核心思想 数据驱动视图,只需要修改数据,Vue 会自动更新 DOM 手动操作 DOM,通过 jQuery 选择器查找元素然后修改
组件化开发 支持(复用组件,提高开发效率) 不支持(只能拆分 HTML 片段)
数据绑定 双向数据绑定 (v-model) 无双向绑定,需要手动 $('#id').val() 获取值
事件绑定 指令方式(@click="fn" ,自动解绑 手动绑定($('button').click(fn) ,需手动解绑
性能优化 虚拟 DOM + Diff 算法,只更新变更部分 直接操作 DOM,性能较差
适用场景 SPA(单页面应用)、组件化开发、大型项目 简单的 DOM 操作、老项目维护、小型项目
状态管理 Vuex / Pinia 进行全局状态管理 无状态管理,依赖全局变量

✅ 2. Vue vs jQuery 代码对比

🌟 场景:点击按钮修改文本内容

🔥 Vue 实现(数据驱动)
<template>
  <div>
    <p>{{ message }}</p>
    <button @click="changeMessage">点击修改</button>
  </div>
</template>

<script setup>
import { ref } from 'vue';

const message = ref("Hello Vue!");
const changeMessage = () => {
  message.value = "Vue 是数据驱动的!";
};
</script>

数据驱动message 变化,Vue 自动更新视图,无需手动操作 DOM。
Vue 自动管理事件绑定和解绑,不会出现 内存泄漏


🔥 jQuery 实现(手动操作 DOM)
<p id="text">Hello jQuery!</p>
<button id="btn">点击修改</button>

<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
  $("#btn").click(function () {
    $("#text").text("jQuery 直接操作 DOM!");
  });
</script>

手动操作 DOM,需要 $('#text').text("xxx") 直接修改。
需要手动解绑事件,否则可能会导致 内存泄漏


✅ 3. Vue 的优势

  1. 组件化开发 🧩

    • Vue 支持组件拆分,提高代码复用率,而 jQuery 只能通过 HTML 片段拼接。
  2. 数据驱动

    • Vue 自动管理 DOM 更新,只需要修改数据,而 jQuery 需要手动操作 DOM。
  3. 双向数据绑定 🔄

    • Vue 通过 v-model 实现数据和 UI 双向绑定,jQuery 需要手动获取和更新值。
  4. 性能优化 🚀

    • Vue 使用虚拟 DOM + Diff 算法,只更新变更部分,jQuery 直接修改 DOM,性能较差。
  5. 响应式开发 📡

    • Vue 内置响应式系统refreactive),jQuery 只能通过事件监听变化。

✅ 4. jQuery 什么时候还会被用到?

虽然 Vue 是现代框架,但 jQuery 仍然有一些适用场景

  1. 老项目维护:许多老系统仍然使用 jQuery,Vue 迁移成本较高。
  2. 简单的 DOM 操作:如果只是少量的动态效果(如轮播图、动画),jQuery 更轻量。
  3. 插件生态:jQuery 仍然有很多插件(如 DataTables、Swiper)适用于特定需求。

💡 但如果是新项目,Vue 是更好的选择!


✅ 5. 面试最佳回答

Vue 和 jQuery 的核心区别在于:

  • Vue 是 声明式开发(数据驱动视图),而 jQuery 是 命令式开发(手动操作 DOM)。
  • Vue 支持组件化、双向数据绑定、虚拟 DOM,适用于大型项目和 SPA
  • jQuery 主要用于简单的 DOM 操作、老项目维护,但不适合复杂应用。

🎯 总结

对比点 Vue(现代框架) jQuery(传统库)
编程方式 声明式编程(数据驱动) 命令式编程(手动操作 DOM)
适用场景 SPA、组件化开发、大型项目 简单 DOM 操作、老项目维护
组件化 支持 不支持
数据绑定 双向绑定 (v-model) 手动更新
事件管理 自动绑定解绑 需手动解绑
性能优化 虚拟 DOM + Diff 算法 直接操作 DOM,性能较低

52. vuex的响应式处理。

🔥 Vuex 的响应式原理(面试高频考点)

vue中可以直接触发methods中的方法,vuex是不可以的。未来处理异步,当触发事件的时候,会通过dispatch来访问 actions中的方法,actions中的commit会触发mutations中的方法从而修改state里的值,通过getter把数据更新到视图
Vue.use(vuex),调用install方法,通过applyMixin(vue)在任意组件内执行this.$store就可以访问到store对象。 vuex的state是响应式的,借助的就是vue的data,把state存到vue实例组件的data中

Vuex 通过 Vue 的响应式系统(Vue.observable() 或 reactive()) 实现全局状态管理:

  • Vue2 使用 Vue.observable(),基于 Object.defineProperty() 实现响应式。
  • Vue3 使用 reactive(),基于 Proxy 代理整个对象,性能更优。
  • Vue 组件会自动订阅 state,当 state 变化时,Vue 会触发 render() 重新渲染组件。
  • Vuex 强制要求 state 只能通过 mutations 修改,避免 Vue 无法检测数据变更。

💡 如果是 Vue 3 新项目,推荐使用 Pinia,响应式更强大,代码更简单! 🚀

53.vue中遍历全局的方法有哪些?

在 Vue 中,遍历全局的方法主要有以下几种:


🔥 1. 遍历全局组件

📌 Vue 2

console.log(Vue.options.components); // 获取所有全局组件
Object.keys(Vue.options.components).forEach((name) => {
  console.log(name); // 组件名
});

📌 Vue 3

import { getCurrentInstance } from "vue";

export default {
  setup() {
    const instance = getCurrentInstance();
    console.log(instance.appContext.components); // Vue 3 获取全局组件
  }
};

🔥 2. 遍历全局指令

📌 Vue 2

console.log(Vue.options.directives);

📌 Vue 3

const instance = getCurrentInstance();
console.log(instance.appContext.directives); // 获取全局指令

🔥 3. 遍历全局 mixins

📌 Vue 2

console.log(Vue.options.mixins);

📌 Vue 3

const instance = getCurrentInstance();
console.log(instance.appContext.mixins);

🔥 4. 遍历 Vuex Store(全局状态)

Object.keys(this.$store.state).forEach(key => {
  console.log(key, this.$store.state[key]); // Vuex 全局状态
});

🔥 5. 遍历路由

this.$router.getRoutes().forEach(route => {
  console.log(route.name, route.path);
});

🔥 6. 遍历全局注册的插件

Vue 本身没有直接暴露获取所有插件的方法,但可以在 Vue.use() 注册插件时,自己维护一个全局数组:

const plugins = [];
const registerPlugin = (plugin) => {
  plugins.push(plugin);
  Vue.use(plugin);
};
registerPlugin(MyPlugin);
console.log(plugins); // 存储所有全局插件

✅ 总结

遍历对象 Vue 2 方式 Vue 3 方式
全局组件 Vue.options.components getCurrentInstance().appContext.components
全局指令 Vue.options.directives getCurrentInstance().appContext.directives
全局 mixins Vue.options.mixins getCurrentInstance().appContext.mixins
Vuex state Object.keys(this.$store.state) Object.keys(this.$store.state)
全局路由 this.$router.options.routes this.$router.getRoutes()
全局插件 手动存储插件 手动存储插件

💡 Vue 3 需要 getCurrentInstance().appContext 来访问全局注册项,而 Vue 2 直接用 Vue.options 🚀
或者回答forEach,map,some,filter??这个题我觉得有歧义,在下没有搞懂想问什么

53. 如何搭建脚手架?

Vue 搭建脚手架的方法

Vue 项目可以使用官方 CLI(Vue CLI)或 Vite 来搭建。下面是两种主流方式的搭建方法。


🔥 方法 1:使用 Vue CLI(适用于 Vue 2 & Vue 3)

Vue CLI 是 Vue 官方提供的脚手架工具,可以快速创建 Vue 2 或 Vue 3 项目。

📌 1. 安装 Vue CLI

如果没有安装 Vue CLI,可以用以下命令安装:

npm install -g @vue/cli
# 或者
yarn global add @vue/cli

📌 Vue CLI 4.x 及以上支持 Vue 3,Vue CLI 3.x 仅支持 Vue 2。

📌 2. 创建 Vue 项目

vue create my-project

执行后,CLI 会提供选项:

  • Default(Vue 3 + Babel + ESLint)
  • Manually select features(手动选择配置)

如果要创建 Vue 2 项目:

vue create my-project --preset default

然后手动选择 Vue 2 版本。

📌 3. 进入项目并运行

cd my-project
npm run serve
# 或
yarn serve

默认情况下,项目会在 http://localhost:8080 运行。


🔥 方法 2:使用 Vite(推荐,适用于 Vue 3)

Vite 是更快的构建工具,Vue 3 官方推荐使用 Vite 代替 Vue CLI。

📌 1. 使用 Vite 创建 Vue 3 项目

npm create vite@latest my-vue-app --template vue
# 或者
yarn create vite@latest my-vue-app --template vue
  • --template vue 代表创建 Vue 3 项目
  • 你也可以使用 --template vue-ts 创建 TypeScript 项目

📌 2. 进入项目目录

cd my-vue-app

📌 3. 安装依赖

npm install
# 或
yarn install

📌 4. 启动项目

npm run dev
# 或
yarn dev

Vite 默认在 http://localhost:5173 运行。


✅ Vue CLI vs Vite 对比

对比项 Vue CLI Vite(推荐)
适用版本 Vue 2 & Vue 3 Vue 3
打包速度 较慢 更快
开发体验 Webpack 需重启 HMR(热更新)更快
构建工具 Webpack Esbuild & Rollup
默认端口 8080 5173

🔥 总结

  • 如果是 Vue 3 项目,推荐 Vite,启动快,开发体验更好。
  • 如果是 Vue 2 项目,使用 Vue CLI 搭建。

54.封装一个可复用的组件,需要满足什么条件?

🔥 封装可复用 Vue 组件需要满足的条件

在 Vue 中,封装一个高质量、可复用的组件需要考虑设计规范、功能封装、灵活性、性能优化等方面。以下是封装组件时的关键点:


✅ 1. 组件的通用性

  • 组件应具有独立性,避免与特定业务强耦合。
  • 组件支持传参(props) ,可根据不同需求调整。
  • 支持插槽(slot) ,让用户灵活控制内部内容。

📌 示例

<template>
  <button :class="['btn', type]" @click="handleClick">
    <slot>默认按钮</slot>
  </button>
</template>

<script setup>
defineProps({
  type: { type: String, default: 'primary' } // 可传入 'primary'、'success' 等
});
const emit = defineEmits(['click']);
const handleClick = () => {
  emit('click');
};
</script>

<style scoped>
.btn {
  padding: 10px 20px;
  border-radius: 5px;
  cursor: pointer;
}
.primary {
  background-color: blue;
  color: white;
}
.success {
  background-color: green;
  color: white;
}
</style>

💡 关键点

props 提供组件自定义能力
slot 提供灵活的内容插入
emit 让组件与外部交互


✅ 2. 组件的可维护性

  • 组件应该职责单一,每个组件只完成一个功能。
  • 代码应结构清晰,逻辑、样式、模板分离。

📌 示例

<template>
  <input v-model="modelValue" :placeholder="placeholder" class="input" />
</template>

<script setup>
defineProps({
  modelValue: String,
  placeholder: { type: String, default: '请输入内容' }
});
defineEmits(['update:modelValue']);
</script>

<style scoped>
.input {
  padding: 8px;
  border: 1px solid #ccc;
  border-radius: 4px;
}
</style>

💡 关键点

使用 v-model 绑定值,符合 Vue 规范
props + emit 结合,实现双向数据绑定
避免在组件内直接修改 props,而是用 emit 触发事件


✅ 3. 组件的灵活性

  • 组件应该支持多种配置,可以通过 props 控制样式和行为。
  • 提供默认插槽作用域插槽,让外部决定内容。

📌 作用域插槽示例

<template>
  <div class="list">
    <slot v-for="(item, index) in items" :item="item" :index="index">
      <p>{{ item }}</p>
    </slot>
  </div>
</template>

<script setup>
defineProps({
  items: { type: Array, default: () => [] }
});
</script>

<style scoped>
.list {
  padding: 10px;
  border: 1px solid #ddd;
}
</style>

💡 关键点

slot 让外部决定渲染方式
作用域插槽 让外部组件可以访问 itemindex


✅ 4. 组件的性能优化

  • 避免不必要的渲染,使用 computedwatch 进行优化。
  • key 绑定 避免列表渲染错误。
  • 事件销毁,使用 onUnmounted 清理事件监听。

📌 示例

<template>
  <div>{{ computedValue }}</div>
</template>

<script setup>
import { computed, ref } from 'vue';

const props = defineProps({
  value: Number
});

const computedValue = computed(() => props.value * 2);
</script>

💡 关键点

computed 只在依赖变更时重新计算,避免不必要的渲染
清理事件监听,避免内存泄漏


✅ 5. 组件的可扩展性

  • 提供 自定义事件,方便外部监听。
  • 使用 defineExpose 公开方法,方便父组件调用。

📌 示例

<template>
  <input ref="inputRef" v-model="value" />
</template>

<script setup>
import { ref, defineExpose } from 'vue';

const value = ref('');
const inputRef = ref(null);

defineExpose({
  focus: () => inputRef.value.focus()
});
</script>

外部组件调用:

<template>
  <MyInput ref="inputComponent" />
  <button @click="focusInput">聚焦输入框</button>
</template>

<script setup>
import { ref } from 'vue';
import MyInput from './MyInput.vue';

const inputComponent = ref(null);
const focusInput = () => {
  inputComponent.value.focus();
};
</script>

💡 关键点

defineExpose 允许父组件调用子组件的方法
组件封装遵循 Vue 3 语法,符合最佳实践


✅ 6. 组件的易用性

  • 提供清晰的 API 文档,让开发者快速上手。
  • 支持国际化(i18n) ,适应不同语言需求。

🔥 结论

条件 说明
通用性 组件应支持 propsslotemit 进行自定义
可维护性 结构清晰,逻辑、样式、模板分离
灵活性 提供作用域插槽,支持多种配置
性能优化 使用 computed、事件销毁、避免不必要渲染
可扩展性 公开方法 defineExpose,支持 emit 事件
易用性 结构清晰,支持国际化

🎯 总结

在 Vue 组件封装中,要考虑通用性、灵活性、性能优化、易用性等多个方面。 组件应做到低耦合、高可复用、易扩展,才能真正成为一个优秀的 Vue 组件! 🚀

55.vue的过滤器怎么使用?

在 Vue 中,过滤器(Filters) 用于对文本进行格式化处理(如日期、货币格式化),不过需要注意 Vue 2 与 Vue 3 的差异。以下是详细使用方法:


Vue 2 中的过滤器

1. 全局注册过滤器

main.js 中定义全局过滤器:

// 日期格式化过滤器
Vue.filter('formatDate', function(value) {
  if (!value) return '';
  return new Date(value).toLocaleDateString();
});

// 货币符号过滤器
Vue.filter('currency', function(value, symbol = '¥') {
  return symbol + value.toFixed(2);
});
2. 局部注册过滤器

在组件选项中定义:

export default {
  filters: {
    capitalize: function(value) {
      if (!value) return '';
      return value.toString().charAt(0).toUpperCase() + value.slice(1);
    }
  }
}
3. 在模板中使用

通过管道符 | 调用过滤器,支持串联

<!-- 基本用法 -->
<p>{{ date | formatDate }}</p>

<!-- 传参和使用多个过滤器 -->
<p>{{ price | currency('$') | toUpperCase }}</p>
4. 在 JS 中调用

通过 $options.filters 访问:

this.$options.filters.formatDate(this.date);

Vue 3 中的变化

Vue 3 移除了过滤器(Filters) ,建议使用以下替代方案:

1. 使用方法(Methods)

直接在模板中调用方法:

<p>{{ formatDate(date) }}</p>
export default {
  methods: {
    formatDate(value) {
      return new Date(value).toLocaleDateString();
    }
  }
}
2. 使用计算属性(Computed)

适合依赖数据的格式化处理:

<p>{{ formattedDate }}</p>
export default {
  computed: {
    formattedDate() {
      return new Date(this.date).toLocaleDateString();
    }
  }
}
3. 全局属性(结合 app.config.globalProperties

定义全局方法供所有组件使用:

// main.js
const app = createApp(App);
app.config.globalProperties.$filters = {
  formatDate(value) {
    return new Date(value).toLocaleDateString();
  }
};

// 组件中使用
<p>{{ $filters.formatDate(date) }}</p>
4. Composition API

setup() 中使用工具函数:

<script setup>
import { formatDate } from '@/utils/filters';

const date = ref('2023-10-01');
</script>

<template>
  <p>{{ formatDate(date) }}</p>
</template>

过滤器迁移建议

  • Vue 2 项目升级:逐步替换过滤器为方法或计算属性。
  • 新项目(Vue 3) :直接使用方法或工具函数。

总结

版本 方案 示例
Vue 2 全局/局部过滤器 `{{ date
Vue 3 方法/计算属性/全局属性 {{ formatDate(date) }}

建议优先使用方法或工具函数,既清晰又兼容性更好,尤其是在需要复用复杂逻辑时。

56.vue中如何做强制刷新?react中如何做强制刷新?

🔥 Vue 和 React 中如何做强制刷新?

在 Vue 和 React 中,有时我们需要强制刷新组件,绕过 Vue 或 React 的优化机制,确保组件重新渲染。常见的方法包括 使用 key 重新挂载组件、修改状态、使用 forceUpdate()(React 专用)等


✅ Vue 中的强制刷新

1️⃣ 通过 key 重新挂载组件(推荐)

Vue 依赖 虚拟 DOM 进行高效更新,修改 key 可以强制销毁并重新创建组件。

<template>
  <MyComponent :key="refreshKey" />
  <button @click="forceRefresh">刷新组件</button>
</template>

<script setup>
import { ref } from "vue";
import MyComponent from "./MyComponent.vue";

const refreshKey = ref(0);
const forceRefresh = () => {
  refreshKey.value += 1; // 修改 key 触发重新渲染
};
</script>

💡 适用场景

适用于整个组件需要重新渲染的情况(如组件状态异常、生命周期重新触发)


2️⃣ 通过 v-if 重新挂载组件

使用 v-if 先卸载再挂载组件,达到强制刷新的效果。

<template>
  <MyComponent v-if="isShow" />
  <button @click="forceRefresh">刷新组件</button>
</template>

<script setup>
import { ref } from "vue";
import MyComponent from "./MyComponent.vue";

const isShow = ref(true);
const forceRefresh = () => {
  isShow.value = false;
  setTimeout(() => (isShow.value = true), 0); // 重新挂载
};
</script>

💡 适用场景

适用于子组件数据异常,无法通过 props 更新的情况


3️⃣ 直接刷新页面(彻底刷新)

<button @click="window.location.reload()">刷新整个页面</button>

💡 适用场景

适用于全局数据异常或 API 状态不一致时


✅ React 中的强制刷新

1️⃣ 通过 useState 触发重渲染(推荐)

React 组件会在 state 变化时重新渲染。使用一个 无关数据 强制触发 useState 变化。

import React, { useState } from "react";

const MyComponent = () => {
  const [refreshKey, setRefreshKey] = useState(0);

  return (
    <>
      <ChildComponent key={refreshKey} />
      <button onClick={() => setRefreshKey(refreshKey + 1)}>刷新组件</button>
    </>
  );
};

export default MyComponent;

💡 适用场景

适用于局部组件刷新,避免整个应用刷新


2️⃣ 通过 forceUpdate() 触发强制刷新(仅 class 组件)

React forceUpdate()忽略 shouldComponentUpdate,直接重新渲染

import React, { Component } from "react";

class MyComponent extends Component {
  forceRefresh = () => {
    this.forceUpdate(); // 强制刷新
  };

  render() {
    return (
      <>
        <ChildComponent />
        <button onClick={this.forceRefresh}>刷新组件</button>
      </>
    );
  }
}

💡 适用场景

适用于 Class 组件,已被 Hooks 取代,不推荐


3️⃣ 通过 window.location.reload() 刷新整个页面

<button onClick={() => window.location.reload()}>刷新页面</button>

💡 适用场景

适用于全局状态异常(如 Redux 丢失数据)


🔥 Vue vs React 强制刷新方式对比

方法 Vue React
修改 key 重新挂载 :key="refreshKey" <ChildComponent key={refreshKey} />
v-if 重新挂载 v-if="isShow" ❌(React 无 v-if
修改状态触发重渲染 state++ setState(state + 1)
forceUpdate() ❌(Vue 不支持) ✅ 仅 Class 组件
window.location.reload()

🎯 结论

  • Vue 推荐:使用 keyv-if 重新挂载组件。
  • React 推荐:使用 useState 触发重渲染,Class 组件可用 forceUpdate()
  • 全局问题window.location.reload() 适用于 Vue 和 React。

🚀 一般来说,修改 key 是最优雅的强制刷新方式!

57.vue的性能优化怎么做?

🔥 Vue 性能优化指南

Vue 作为一个响应式框架,默认已经做了许多优化,但在复杂业务场景下,我们仍然需要手动优化,确保应用运行流畅。以下是 Vue 性能优化的关键策略:


1️⃣ 计算属性 (computed) 代替方法 (methods)

  • 问题:如果在模板中直接使用 methods,每次渲染都会执行一次函数。
  • 优化:使用 computed 进行缓存,只有依赖的数据变化时才会重新计算。

优化前(性能低)

<template>
  <p>{{ computedValue() }}</p>
</template>

<script setup>
const computedValue = () => {
  console.log("计算了!"); // 每次渲染都会调用
  return Math.random();
};
</script>

优化后(性能高)

<template>
  <p>{{ computedValue }}</p>
</template>

<script setup>
import { computed } from "vue";

const computedValue = computed(() => {
  console.log("计算了!"); // 只有依赖更新时才执行
  return Math.random();
});
</script>

💡 适用场景

✅ 适用于有复杂计算的场景,减少重复计算


2️⃣ v-ifv-show 的选择

  • v-if按需销毁(性能高,但切换有成本)
  • v-show显示/隐藏(性能低,但切换快)

优化前(不必要的 v-if

<template>
  <div v-if="isShow">切换的内容</div>
</template>

⚠️ 问题:频繁切换时,v-if 反复销毁和创建组件,影响性能。

优化后(使用 v-show

<template>
  <div v-show="isShow">切换的内容</div>
</template>

💡 结论

  • 长时间不需要显示时:用 v-if(减少 DOM 占用)
  • 频繁切换显示状态时:用 v-show(减少重新渲染)

3️⃣ 使用 key 优化 v-for 遍历

  • key 让 Vue 更高效地更新 DOM,避免不必要的重新渲染

优化前(性能低,Vue 不能高效复用 DOM)

<template>
  <ul>
    <li v-for="item in list">{{ item.name }}</li>
  </ul>
</template>

优化后(性能高)

<template>
  <ul>
    <li v-for="item in list" :key="item.id">{{ item.name }}</li>
  </ul>
</template>

💡 结论

  • 避免使用数组索引作为 key
  • 最好使用唯一标识符(如 id

4️⃣ watchwatchEffect 的优化

  • watch 监听特定数据变化,提高性能。
  • 避免深度监听 deep: true,如果可能的话,监听具体属性。

优化前(深度监听会消耗性能)

watch(
  () => state,
  (newVal) => {
    console.log("数据变了", newVal);
  },
  { deep: true }
);

优化后(监听具体字段)

watch(
  () => state.value,
  (newVal) => {
    console.log("value 变了", newVal);
  }
);

💡 结论

  • 尽量避免 deep: true,只监听需要的数据
  • 避免不必要的 watch,使用 computed 代替

5️⃣ 路由懒加载(按需加载组件)

  • 问题:如果不做优化,Vue 会一次性加载所有组件,影响首屏速度。
  • 优化:使用 import() 按需加载 组件。

优化前(性能低)

import Home from '@/views/Home.vue';
import About from '@/views/About.vue';

优化后(性能高)

const Home = () => import('@/views/Home.vue');
const About = () => import('@/views/About.vue');

💡 适用场景大规模应用,减少初始加载时间


6️⃣ 长列表优化 (virtual list)

  • 问题v-for 渲染大量数据严重影响性能
  • 解决方案:使用虚拟列表(只渲染可见部分)。

优化前(直接 v-for 渲染 10000 条数据,卡顿)

<template>
  <ul>
    <li v-for="item in list" :key="item.id">{{ item.name }}</li>
  </ul>
</template>

优化后(使用 Vue 虚拟列表库 vue-virtual-scroller

<template>
  <RecycleScroller
    :items="list"
    :item-size="50"
    key-field="id"
  >
    <template v-slot="{ item }">
      <div>{{ item.name }}</div>
    </template>
  </RecycleScroller>
</template>

💡 适用场景数据量超过 1000+ 时,必须使用虚拟列表


7️⃣ 事件销毁 (onUnmounted)

  • 问题:Vue 组件销毁时,如果不清除事件监听,会造成内存泄漏
  • 优化:在 onUnmounted 里销毁事件。

优化前(内存泄漏风险)

onMounted(() => {
  window.addEventListener("resize", handleResize);
});

优化后(清除事件监听)

import { onMounted, onUnmounted } from "vue";

onMounted(() => {
  window.addEventListener("resize", handleResize);
});

onUnmounted(() => {
  window.removeEventListener("resize", handleResize);
});

💡 适用场景组件销毁后需要清除事件监听


🔥 总结

优化策略 优化方式 适用场景
计算属性优化 computed 代替 methods 复杂计算避免重复执行
条件渲染优化 v-if vs v-show v-show 适用于频繁切换
列表渲染优化 key & 虚拟列表 遍历大数据时
watch 优化 避免 deep: true 监听具体属性
路由懒加载 import() 动态加载 加快首屏速度
虚拟滚动 vue-virtual-scroller 处理超大列表
事件销毁 onUnmounted 清理 组件销毁时清理事件

🚀 Vue 默认已经很快,但合理优化可以让它更快!

  • 避免不必要的渲染
  • 只加载必要的数据
  • 清理不必要的监听
  • 使用高效的数据处理方法

58.首屏优化该如何去做?

🔥 Vue 首屏优化方案(极致提速)

首屏优化(First Screen Optimization)主要针对首屏白屏时间加载速度进行优化。我们需要减少首屏资源体积、网络请求次数、渲染时间等,确保用户可以快速看到内容。


🚀 1️⃣ 开启 Gzip/Brotli 压缩(减少资源体积)

  • 问题:Vue 生成的 dist 目录资源体积大,加载慢。
  • 优化:开启 GzipBrotli 压缩,减少前端静态资源大小。

✅ Webpack 配置 Gzip

const CompressionWebpackPlugin = require("compression-webpack-plugin");

module.exports = {
  configureWebpack: {
    plugins: [
      new CompressionWebpackPlugin({
        algorithm: "gzip",
        test: /.(js|css|html|svg)$/, // 需要压缩的文件类型
        threshold: 10240, // 只处理 10KB 以上的文件
        minRatio: 0.8, // 压缩比
      }),
    ],
  },
};

✅ 使用 Brotli(更高压缩率)

const BrotliPlugin = require('brotli-webpack-plugin');

module.exports = {
  configureWebpack: {
    plugins: [
      new BrotliPlugin({
        asset: '[path].br[query]',
        test: /.(js|css|html|svg)$/,
        threshold: 10240,
        minRatio: 0.8
      })
    ]
  }
};

💡 适用场景

适用于所有 Vue 项目,显著减少文件体积


🚀 2️⃣ 路由懒加载(减少首屏加载体积)

  • 问题:如果 import 直接引入所有页面组件,首屏加载时间过长
  • 优化:使用 路由懒加载,首屏仅加载必要组件,其他组件按需加载。

优化前(所有页面组件一次性加载)

import Home from "@/views/Home.vue";
import About from "@/views/About.vue";

const routes = [
  { path: "/", component: Home },
  { path: "/about", component: About },
];

优化后(按需加载组件)

const routes = [
  { path: "/", component: () => import("@/views/Home.vue") },
  { path: "/about", component: () => import("@/views/About.vue") },
];

💡 适用场景

适用于 Vue 单页应用(SPA),减少首屏加载时间


🚀 3️⃣ 组件按需加载(减少打包体积)

  • 问题:Vue 组件库(如 Element PlusAnt Design Vue)会全部引入,导致打包体积过大。
  • 优化:使用按需加载,仅引入用到的组件。

优化前(全量引入,导致首屏加载慢)

import ElementPlus from "element-plus";
import "element-plus/dist/index.css";

app.use(ElementPlus);

优化后(按需加载)

import { ElButton, ElTable } from "element-plus";

app.use(ElButton);
app.use(ElTable);

💡 适用场景
适用于大型 Vue 项目,显著减少 JS 体积


🚀 4️⃣ Vue Keep-Alive(缓存页面,提高二次加载速度)

  • 问题:每次切换路由时,都会重新加载页面,影响二次访问速度。
  • 优化:使用 <keep-alive> 缓存组件,避免重复渲染。

优化前

<router-view />

优化后

<keep-alive>
  <router-view />
</keep-alive>

💡 适用场景
适用于需缓存的页面,如 Tab 切换、返回上一级


🚀 5️⃣ 预渲染 SSR(减少白屏时间)

  • 问题:Vue 是单页应用(SPA),首屏可能会白屏
  • 优化:使用**预渲染(Prerendering)**或 SSR(服务端渲染) ,让页面更快可见。

预渲染配置(Prerender SPA Plugin)

const PrerenderSPAPlugin = require("prerender-spa-plugin");
const path = require("path");

module.exports = {
  configureWebpack: {
    plugins: [
      new PrerenderSPAPlugin({
        staticDir: path.join(__dirname, "dist"),
        routes: ["/", "/about"], // 预渲染的路由
      }),
    ],
  },
};

使用 Nuxt.js 进行 SSR

npx create-nuxt-app my-app

💡 适用场景
适用于 SEO 需求较高的 Vue 项目,如博客、电商站点


🚀 6️⃣ 静态资源 CDN 加速(减少服务器压力)

  • 问题:Vue 依赖的 vue.jsaxioselement-plus 等库默认是本地加载,导致首屏加载慢。
  • 优化:使用 CDN 加载常用库,减少本地打包体积。

优化前(本地打包,文件过大)

"dependencies": {
  "vue": "^3.0.0",
  "axios": "^0.21.1"
}

优化后(CDN 加载 Vue 和 Axios)

<script src="https://cdn.jsdelivr.net/npm/vue@3.2.37/dist/vue.global.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios@0.21.1/dist/axios.min.js"></script>

💡 适用场景
适用于 Vue Web 项目,减少本地打包体积


🚀 7️⃣ 延迟加载图片(Lazy Load)

  • 问题:如果页面有大量图片,加载速度会变慢。
  • 优化:使用懒加载,只有图片出现在可视区域时才加载。

优化前

<img :src="imageSrc" />

优化后(使用 v-lazy

<img v-lazy="imageSrc" />

Vue 3 懒加载

<template>
  <img v-if="isInView" :src="imageSrc" />
</template>

<script setup>
import { ref, onMounted } from "vue";

const isInView = ref(false);
onMounted(() => {
  const observer = new IntersectionObserver((entries) => {
    isInView.value = entries[0].isIntersecting;
  });
  observer.observe(document.querySelector("img"));
});
</script>

💡 适用场景
适用于图片较多的页面,减少首屏加载时间


🎯 总结:Vue 首屏优化策略

优化方式 作用 适用场景
Gzip/Brotli 压缩 减少打包体积 所有 Vue 项目
路由懒加载 只加载首屏必要组件 SPA 应用
组件按需加载 仅引入用到的组件 使用 UI 库
Keep-Alive 缓存 避免重复渲染 Tab 页面缓存
SSR / 预渲染 减少白屏时间,SEO 友好 SEO 需求高
CDN 加速 加快静态资源加载 Vue Web 项目
图片懒加载 延迟加载图片 需优化大量图片

🚀 综合使用这些优化方案,可以让 Vue 首屏加载速度提高 30%~60%! 💪

59.vue3为什么使用proxy?

Vue 3 为什么使用 Proxy?(相比 Vue 2 的 Object.defineProperty)

在 Vue 2 中,响应式系统是基于 Object.defineProperty 实现的,而在 Vue 3 中,改用 Proxy 作为响应式核心。这一改变带来了诸多优势,解决了 Vue 2 响应式的局限性。


🌟 1. Vue 2 响应式的局限性

Vue 2 使用 Object.defineProperty 劫持对象的每个属性 来实现数据响应式,但存在以下问题:

❌ (1) 不能监听新增/删除的属性

const obj = {};
Vue.set(obj, "name", "Vue3"); // 必须使用 Vue.set 才能响应式
delete obj.name; // 删除属性无法触发更新
  • 由于 Object.defineProperty 只能劫持已有属性,新增属性不会自动变成响应式。

❌ (2) 不能监听数组的变化

const arr = [];
arr[0] = "Vue3"; // 无法监听
arr.length = 0;  // 也不会触发更新
  • Vue 2 需要对数组的方法(push/pop/shift/splice)进行手动劫持,但仍无法监听数组索引的变化

❌ (3) 深层次嵌套对象需要递归遍历

const data = { a: { b: { c: 1 } } };
  • Vue 2 初始化时,必须递归遍历整个对象,每一层都用 Object.defineProperty 处理。
  • 深层嵌套对象性能差,对象越大,初始化越慢。

✅ 2. Vue 3 采用 Proxy 的优势

Vue 3 使用 Proxy 代替 Object.defineProperty,解决了上述问题。

🚀 (1) 可以监听对象新增/删除属性

const obj = reactive({});
obj.name = "Vue3";  // ✅ 直接赋值就能响应式
delete obj.name;     // ✅ 删除属性也能触发更新
  • Proxy 代理整个对象,可以动态监听属性的新增和删除

🚀 (2) 可以监听数组索引和 length 变化

const arr = reactive([]);
arr[0] = "Vue3";  // ✅ 能正确监听
arr.length = 0;   // ✅ 能触发更新
  • Proxy 直接代理整个数组,所以索引和 length 变化都能触发更新

🚀 (3) 无需递归,性能更优

const data = reactive({ a: { b: { c: 1 } } });
  • Vue 3 只代理第一层对象,深层属性只有在访问时才会变成响应式(懒代理)
  • 避免了一次性遍历整个对象,提升初始化性能

🚀 (4) 能代理 MapSetWeakMapWeakSet

const map = reactive(new Map());
map.set("key", "Vue3"); // ✅ 能正常响应式
  • Vue 2 不能劫持 MapSet,Vue 3 通过 Proxy 支持它们的响应式

🔥 3. Vue 3 Proxy 代码示例

const state = reactive({ count: 0 });

watchEffect(() => {
  console.log(state.count);
});

state.count++;  // ✅ 能触发响应式
  • Vue 3 使用 reactive 内部基于 Proxy 实现响应式
  • 所有属性(新增、删除、嵌套对象)都能正确触发更新

🎯 总结

特性 Vue 2(Object.defineProperty) Vue 3(Proxy)
监听新增/删除属性 ❌ 不能 ✅ 可以
监听数组索引变化 ❌ 不能 ✅ 可以
监听 length 变化 ❌ 不能 ✅ 可以
深层对象响应式 ❌ 需要递归遍历 ✅ 访问时才代理(性能更优)
支持 Map/Set ❌ 不支持 ✅ 完全支持
性能 🔴 初始化慢,嵌套对象影响大 🟢 性能更优

🚀 Vue 3 使用 Proxy 彻底解决了 Vue 2 的响应式局限,提升了性能和开发体验! 💪

60.vue3的性能为什么比vue2好?

🔥 Vue 3 为什么比 Vue 2 性能更好?(核心优化点)

Vue 3 进行了多个底层优化,使其比 Vue 2 运行更快、内存占用更低、渲染更高效。以下是关键的性能优化点👇:


🚀 1. 采用 Proxy 取代 Object.defineProperty(提升响应式性能)

Vue 2 使用 Object.defineProperty 劫持对象的每个属性,存在无法监听新增属性、无法监听数组索引、深层对象递归遍历导致性能低下的问题。

✅ Vue 3 使用 Proxy 解决这些问题

const state = reactive({ count: 0 });

state.count++;  // ✅ 直接修改就能触发更新(比 Vue 2 更高效)
state.newProp = "Vue3"; // ✅ 可以监听新增属性

🌟 Proxy 优势

对比项 Vue 2(Object.defineProperty) Vue 3(Proxy)
监听新增/删除属性 ❌ 不支持 ✅ 支持
监听数组索引 ❌ 不能监听 ✅ 可以监听
监听 length 变化 ❌ 不能监听 ✅ 可以监听
深层对象劫持 ❌ 需要递归遍历 ✅ 访问时才代理(性能更优)

🚀 结果

减少递归遍历,响应式更快、更高效
减少内存占用,访问时才触发代理


🚀 2. 编译优化(更高效的 Diff 算法)

❌ Vue 2 的问题

  • Vue 2 采用 “全量 Diff” 算法,即组件发生变化时,整个 Virtual DOM 需要重新比对,导致不必要的性能损耗

✅ Vue 3 采用 “Block Tree + Patch Flag”(动态标记最小更新单元)

Vue 3 在编译时优化,给节点打上Patch Flag,只更新真正变化的部分:

<template>
  <h1>{{ title }}</h1>
  <p>静态文本,不变的部分</p>
</template>

编译后的 VNode 结构:

{ 
  type: 'h1', 
  flag: 1, // ✅ 只追踪 "title" 变化
}

🌟 结果

只更新变化的部分(更少的 DOM 操作)
减少不必要的比对,提高渲染效率


🚀 3. 组件优化(减少内存占用 & 运行时开销)

✅ Vue 3 移除了 this,减少运行时开销

Vue 2 使用 this 访问数据:

export default {
  data() {
    return { count: 0 };
  },
  methods: {
    increment() {
      this.count++; // ✅ Vue 2 需要绑定 this
    }
  }
};

Vue 3 采用 setup 语法,避免 this 绑定:

export default {
  setup() {
    const count = ref(0);
    const increment = () => count.value++;
    return { count, increment };
  }
};

🌟 结果

减少 this 解析开销,执行效率更高
更简洁,代码更直观


🚀 4. Tree Shaking(按需打包,减少体积)

❌ Vue 2 的问题

  • Vue 2 是全局 API 注册,即使没用到某些功能,也会被打包进最终文件

✅ Vue 3 采用 “Tree Shaking”

Vue 3 把 API 设计成按需导入,只打包用到的部分:

import { reactive, computed } from "vue"; // ✅ 只引入需要的 API

🌟 结果

减少不必要的代码,打包体积更小
性能更好,按需加载提高运行效率


🚀 5. Fragment(减少 DOM 层级,提高渲染性能)

❌ Vue 2 的问题

  • Vue 2 每个组件必须有一个根节点,导致多余的 DOM 层级
<template>
  <div>  <!-- 这个 div 是必须的 -->
    <h1>Vue 2</h1>
    <p>增加了不必要的 DOM 层级</p>
  </div>
</template>

✅ Vue 3 支持 Fragment(组件可以返回多个根节点)

<template>
  <h1>Vue 3</h1>
  <p>没有多余的 DOM 结构</p>
</template>

🌟 结果

减少不必要的 div,减少 DOM 层级
提高渲染性能,减少 DOM 操作


🚀 6. Teleport(更快的 Modal/弹窗渲染)

❌ Vue 2 的问题

  • 组件内部的 Modal/弹窗 必须放在当前组件内,容易导致 z-index 层级混乱:
<template>
  <div>
    <MyModal /> <!-- 这个 Modal 可能被父级 div 限制,层级可能有问题 -->
  </div>
</template>

✅ Vue 3 提供 Teleport,可以让元素渲染到 body 之外

<template>
  <teleport to="body">
    <MyModal /> <!-- ✅ 直接渲染到 body 层级,避免层级问题 -->
  </teleport>
</template>

🌟 结果

提高弹窗渲染性能
避免 z-index 层级问题


🎯 总结:Vue 3 主要的性能优化点

优化点 Vue 2(Object.defineProperty) Vue 3(Proxy)
响应式优化 Object.defineProperty 不能监听新增/删除 Proxy 支持动态监听
Diff 算法 需要全量比对 Virtual DOM 通过 Patch Flag 只更新变化的部分
代码优化 依赖 this,运行时开销大 setup 减少 this 解析,提高执行效率
Tree Shaking 全局 API 注册,打包体积大 按需引入,减少打包体积
Fragment 组件必须有根节点 支持多个根节点,减少 DOM 层级
Teleport 组件内容受限于父级 可将内容渲染到 body,减少 DOM 操作

🎯 结论

🚀 Vue 3 通过 Proxy、编译优化、组件优化、Tree Shaking 等,使其在运行时、渲染性能、打包体积等方面全面超越 Vue 2 💪
响应式更快,渲染更高效,打包更轻量

61.说一下你对组件的理解

🔹 你对 Vue 组件的理解?(面试官常问)

📌 1. 什么是 Vue 组件?

Vue 组件是 Vue 应用的基本构成单元,本质上是一个封装了 HTML、CSS、JS 的独立模块,可以复用、组合、拆分,提高代码的可维护性和开发效率。


📌 2. Vue 组件的核心特性?

1️⃣ 组件化开发(模块化 & 复用性)

  • 组件可以独立封装,减少代码冗余,提高开发效率。
  • 例如:一个 Button 组件可以在多个页面使用。

2️⃣ 组件通信(Props / $emit / Provide & Inject / Vuex / Pinia)

  • 父传子(Props): props 让父组件向子组件传递数据。
  • **子传父( e m i t ): ∗ ∗ 子组件可以通过 ‘ emit):** 子组件可以通过 ` emit):子组件可以通过emit` 触发父组件的方法。
  • 跨层级通信(Provide & Inject): 适用于多层级组件传递数据。
  • 全局状态管理(Vuex / Pinia): 适用于复杂项目的全局状态管理。

3️⃣ 组件生命周期(生命周期钩子)

  • 组件有完整的生命周期,如 onMountedonUpdatedonUnmounted 等,可用于初始化、销毁、更新操作。

4️⃣ 动态组件 & 异步组件

  • 动态组件: <component :is="currentComponent" />,可实现组件切换。
  • 异步组件: defineAsyncComponent(() => import('./MyComponent.vue')),可按需加载,提升性能。

5️⃣ 插槽(Slot)

  • 通过 slot 让组件更灵活,如:
<MyCard>
  <template v-slot:header> 标题 </template>
  <template v-slot:default> 主要内容 </template>
</MyCard>

📌 3. Vue 组件的优化?

1️⃣ 合理使用 v-ifv-show

  • v-if 适用于组件销毁/创建(开销大,适用于少量切换)
  • v-show 适用于频繁切换(仅控制 display,性能更好)

2️⃣ 使用 computed 代替 methods,避免不必要的重复计算

const fullName = computed(() => firstName + " " + lastName);

3️⃣ 使用 keep-alive 缓存组件,避免重复销毁/创建

<keep-alive>
  <RouterView />
</keep-alive>

4️⃣ 使用异步组件减少首屏加载压力

const AsyncComponent = defineAsyncComponent(() =>
  import("./MyComponent.vue")
);

5️⃣ 使用 Teleport 让弹窗渲染到 body 级别

<teleport to="body">
  <MyModal />
</teleport>

📌 4. 面试官追问:组件和模块的区别?

组件(Component) 模块(Module)
定义 UI 结构、逻辑封装(如 Vue 组件 代码逻辑封装(如 ES6 Module
作用 负责 UI 渲染和交互 负责业务逻辑或工具函数
复用性 可复用 UI 组件 可复用业务逻辑
示例 <Button /> 组件 utils.js 工具函数

📌 5. 总结

Vue 组件是 Vue 开发的核心,具有 模块化、复用性、独立性、动态性 等特点。掌握组件的 通信方式、生命周期、优化技巧,可以提升 Vue 开发效率和应用性能。

62.你是如何规划项目文件的?

📌 你是如何规划 Vue 项目的文件结构?(面试官常问)

在实际开发中,合理的项目结构可以提高可维护性可扩展性,并方便团队协作。下面是常见的 Vue 3 项目文件结构规划


📂 1. 常见的 Vue 3 项目结构

├── public/                   # 静态资源(不会被 Webpack 处理)
│   ├── index.html            # HTML 入口文件
│   └── favicon.ico           # 网站图标
├── src/                      # 核心代码目录
│   ├── api/                  # API 请求封装
│   │   ├── index.ts          # 统一导出 API
│   │   ├── user.ts           # 用户相关 API
│   │   ├── auth.ts           # 登录相关 API
│   ├── assets/               # 静态资源(如图片、字体)
│   │   ├── images/           # 图片
│   │   ├── icons/            # 图标
│   │   ├── styles/           # 全局样式
│   ├── components/           # 复用组件(如按钮、弹窗)
│   │   ├── BaseButton.vue    # 按钮组件
│   │   ├── BaseModal.vue     # 弹窗组件
│   ├── layouts/              # 布局组件(如侧边栏、导航栏)
│   │   ├── DefaultLayout.vue # 默认布局
│   │   ├── AdminLayout.vue   # 后台管理布局
│   ├── router/               # 路由管理
│   │   ├── index.ts          # Vue Router 配置
│   │   ├── routes.ts         # 路由表
│   ├── store/                # Vuex/Pinia 状态管理
│   │   ├── index.ts          # 统一导出 Store
│   │   ├── user.ts           # 用户相关状态
│   │   ├── settings.ts       # 配置相关状态
│   ├── views/                # 页面组件(按功能模块拆分)
│   │   ├── Home.vue          # 首页
│   │   ├── Login.vue         # 登录页
│   │   ├── Dashboard.vue     # 仪表盘
│   │   ├── Profile.vue       # 个人中心
│   ├── utils/                # 工具函数
│   │   ├── request.ts        # 封装 Axios 请求
│   │   ├── auth.ts           # 鉴权相关工具
│   ├── directives/           # 自定义指令
│   │   ├── vPermission.ts    # 权限指令
│   │   ├── vLazyLoad.ts      # 图片懒加载
│   ├── composables/          # 组合式 API 封装(Vue 3)
│   │   ├── useAuth.ts        # 用户认证逻辑
│   │   ├── useFetch.ts       # 封装 Fetch 逻辑
│   ├── App.vue               # 根组件
│   ├── main.ts               # 入口文件
├── tests/                    # 单元测试
│   ├── components/           # 组件测试
│   ├── views/                # 页面测试
├── .env                      # 环境变量文件
├── vite.config.ts             # Vite 配置文件
├── tsconfig.json              # TypeScript 配置
├── package.json               # 依赖管理
└── README.md                  # 项目说明文档

📌 2. 详细规划与优化

1️⃣ src/api/ - API 请求封装

  • 采用 模块化拆分,避免 API 逻辑散落在组件中。
  • 封装 axios,统一管理请求 & 响应拦截,便于维护。

示例

import axios from "axios";

const api = axios.create({
  baseURL: import.meta.env.VITE_APP_BASE_URL,
  timeout: 5000,
});

// 请求拦截器
api.interceptors.request.use((config) => {
  config.headers.Authorization = `Bearer ${localStorage.getItem("token")}`;
  return config;
});

export default api;

2️⃣ src/components/ - 复用组件

  • BaseXXX.vue 代表基础组件,如 BaseButton.vueBaseTable.vue
  • AppXXX.vue 代表全局应用组件,如 AppHeader.vueAppFooter.vue
  • 业务组件(如 UserCard.vueProductItem.vue 归类到具体 modules/ 目录下。

示例

<template>
  <button class="base-button">
    <slot></slot>
  </button>
</template>

3️⃣ src/store/ - 状态管理(Vuex / Pinia)

  • Vuex(Vue 2 & Vue 3)
  • Pinia(推荐,适用于 Vue 3)

Pinia 示例

import { defineStore } from "pinia";

export const useUserStore = defineStore("user", {
  state: () => ({
    userInfo: null,
  }),
  actions: {
    setUser(data) {
      this.userInfo = data;
    },
  },
});

4️⃣ src/composables/ - 组合式 API(Vue 3)

  • 封装可复用的逻辑
  • 避免 mixin 难以维护的问题

示例

import { ref } from "vue";

export function useCounter() {
  const count = ref(0);
  const increment = () => count.value++;
  return { count, increment };
}

5️⃣ src/router/ - 路由管理

  • 使用动态路由
  • 路由懒加载(提升首屏加载速度)

示例

import { createRouter, createWebHistory } from "vue-router";

const routes = [
  { path: "/", component: () => import("@/views/Home.vue") },
  { path: "/login", component: () => import("@/views/Login.vue") },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

export default router;

📌 3. 面试官可能追问

❓ 为什么要划分 api/composables/store/

  • api/ 主要管理 后端接口请求,让组件与 API 解耦。
  • composables/ 用于封装可复用的业务逻辑,便于组合式 API 复用。
  • store/ 管理全局状态,避免跨组件通信复杂化。

❓ 你是如何优化首屏加载的?

优化方式

  1. 路由懒加载() => import('@/views/Home.vue')
  2. 异步组件defineAsyncComponent()
  3. 资源按需加载vite-plugin-components
  4. CDN 引入:减少 node_modules 依赖
  5. Gzip 压缩:减少资源体积

📌 4. 总结

合理的 Vue 目录结构 可以让项目更清晰、可维护、可扩展,同时优化代码复用性能
🚀 在面试中,重点强调 “解耦、复用、可维护性”,并结合实际经验解答

63.是否使用过nuxt.js?

是的,Nuxt.js 是一个基于 Vue.js 的框架,专门用来创建 服务端渲染(SSR)静态站点生成(SSG) 的应用。它通过自动化的路由生成、服务器端渲染和优化的构建工具,提供了更高效的开发体验。

Nuxt.js 的特点

  1. 服务端渲染(SSR)

    • Nuxt 提供了对 SSR 的支持,使得页面的首屏渲染能在服务器端进行,提高了 SEO 和性能。
  2. 静态站点生成(SSG)

    • Nuxt 可以预渲染整个网站并生成静态文件,减少服务器压力,提升加载速度。
    • 适用于内容更新不频繁的网站,如博客、文档等。
  3. 自动路由生成

    • Nuxt 会根据 pages/ 目录中的文件结构自动生成路由,无需手动配置。
  4. 插件系统

    • 支持通过 plugins/ 目录注入外部插件或第三方库,非常适合全局配置。
  5. 模块化开发

    • Nuxt 提供了大量的官方模块,帮助开发者轻松集成常见功能,如 axios 请求、PWA 支持等。
  6. 热重载和开发工具

    • Nuxt 集成了 webpack,支持热重载,提升开发效率。
  7. Vuex 状态管理

    • Nuxt 默认支持 Vuex,使得全局状态管理更加便捷。

使用 Nuxt.js 的场景

  • SEO 友好的应用:需要服务器端渲染来提升搜索引擎优化(SEO)的应用。
  • 静态网站:如博客、新闻网站,支持静态站点生成。
  • 复杂的前端应用:比如管理系统、内容管理平台等。

Nuxt.js 的优缺点

优点
  • SEO 优化:SSR 提供了更好的搜索引擎优化。
  • 开发效率高:自动路由生成、模块化支持、插件机制。
  • 性能优化:自动化的代码分割、静态站点生成等技术提升性能。
缺点
  • 学习曲线:虽然 Nuxt 提供了开箱即用的功能,但对于初学者而言,它的一些配置项和概念(如 SSR、SSG)可能需要一定的学习成本。
  • 灵活性限制:在某些极端的场景下,Nuxt 的约定可能会限制开发者的一些自定义需求。

总结

Nuxt.js 是构建 Vue 应用的强大工具,特别适合需要服务端渲染或静态生成的网站。它通过自动化和优化,能够显著提高开发效率和应用性能。

64.vue面试题 - SEO如何优化?

📌 Vue 项目中的 SEO 优化:

在面试中,关于 SEO 优化 的问题是常见的,尤其是对于 单页应用(SPA) ,因为 Vue 默认是通过客户端渲染的,搜索引擎爬虫可能无法有效抓取到页面内容。

📌 1. 使用服务端渲染(SSR)或静态站点生成(SSG)

  • 服务端渲染(SSR)静态站点生成(SSG) 是优化 SPA SEO 的有效方案。
  • 在 SSR 中,页面在服务器端渲染并返回完整的 HTML,这样爬虫能抓取到页面的完整内容。
  • Nuxt.js 是基于 Vue 的 SSR 框架,支持服务端渲染和静态站点生成,能够显著提升 SEO 性能。

解决方案:

  • 使用 Nuxt.js 来实现 SSR 或 SSG。
  • 使用 Vue Server Renderer 进行服务端渲染。

📌 2. 预渲染(Prerendering)

  • 预渲染 适合内容更新不频繁的小型网站或博客,预渲染是在构建时生成静态的 HTML 页面。
  • 这种方式通过工具(如 prerender-spa-plugin)可以将 Vue 应用在构建阶段转换为静态 HTML 页面。

解决方案:

  • 使用 prerender-spa-plugin 配合 Webpack 对指定页面进行预渲染。

📌 3. 动态添加元标签(Meta Tags)

  • 搜索引擎对 meta 标签(如 <title><meta><h1>)的内容非常敏感。这些标签能帮助搜索引擎正确理解页面内容。
  • 在 Vue 中,可以使用 vue-meta 插件动态更新页面的 <meta> 标签。

解决方案:

  • 安装并配置 vue-meta 来动态设置页面的 titlemeta 标签。

    import VueMeta from 'vue-meta'
    Vue.use(VueMeta)
    

    在组件中:

    export default {
      metaInfo: {
        title: '我的页面标题',
        meta: [
          { name: 'description', content: '页面描述' },
          { property: 'og:image', content: 'https://example.com/image.jpg' },
        ]
      }
    }
    

📌 4. 路由和 URL 结构优化

  • 确保页面的 URL 是 友好的简洁的,避免出现动态的、不易识别的 URL(如带有复杂查询参数的 URL)。
  • 使用 Vue Router 的路由重定向功能来确保 URL 规范。

解决方案:

  • 使用 SEO 友好的 URL,避免使用过多的查询参数。
  • 在 Vue Router 中配置 动态路由,为每个页面提供一个独特且简洁的路径。

📌 5. 提高页面加载速度

  • 页面加载速度 是影响 SEO 排名的重要因素,尤其是 Core Web Vitals 中的 Largest Contentful Paint (LCP)
  • 减少 JavaScript 文件大小、实现懒加载和异步加载静态资源,能有效提升性能。

解决方案:

  • 使用 代码分割(Code Splitting) 来减小 JavaScript 的初始加载体积。
  • 实现 懒加载:如对图片和路由进行懒加载。
  • 使用 ViteWebpack 的性能优化插件,如 Tree Shaking压缩代码分割

📌 6. 使用结构化数据(Schema Markup)

  • 结构化数据有助于搜索引擎更好地理解网页内容并增强搜索结果中的展示(如丰富摘要、星级评价等)。
  • 在 Vue 中,可以通过 <script type="application/ld+json"> 标签嵌入 JSON-LD 结构化数据。

解决方案:

  • 在页面中嵌入 JSON-LD 结构化数据,提升搜索引擎对内容的理解。

    示例:

    <script type="application/ld+json">
      {
        "@context": "https://schema.org",
        "@type": "WebPage",
        "name": "页面标题",
        "description": "页面描述",
        "url": "https://example.com/page"
      }
    </script>
    

📌 7. 确保页面可访问性

  • 确保网站内容 可以被搜索引擎爬虫索引,并且 没有阻止爬虫抓取的技术(如 JavaScript 渲染的内容)。
  • 通过 robots.txtmeta robots 标签来控制哪些页面允许被索引。

解决方案:

  • 配置 robots.txt 来管理搜索引擎爬虫的访问权限。
  • 在需要禁止索引的页面上添加 <meta name="robots" content="noindex, nofollow"> 标签。

📌 8. 使用 CDN 加速静态资源

  • 使用 CDN(内容分发网络)加速静态资源(如图片、CSS、JS 文件)的加载,提高页面加载速度。

解决方案:

  • 图片、字体、样式表 等静态资源托管在 CDN 上,提高访问速度。

📌 9. 定期更新和内容优化

  • 持续更新网站内容,并确保内容的 相关性质量
  • 采用 内容营销,如优化博客文章、产品描述等,增加页面的 关键词密度,提高排名。

📌 10. 总结:Vue 项目的 SEO 优化

在 Vue 项目中优化 SEO,首先要解决 客户端渲染 带来的问题,通过 SSR 或 SSG 技术(如 Nuxt.js),配合动态 meta 标签、合理的 URL 结构和页面加载优化,提升 SEO 排名和用户体验。同时,优化网站性能、使用结构化数据以及定期更新内容,能显著提高页面的可搜索性和可见性。

65.echarts有用过吗?常用的组件有哪些

是的,ECharts 是一个基于 JavaScript 的开源图表库,广泛用于数据可视化,提供了丰富的图表类型和灵活的配置。它可以用于展示各种类型的数据,并且支持交互和动态更新,适合在前端项目中进行数据展示。

常用的 ECharts 组件:

  1. 图表类型(Charts Types) ECharts 支持多种图表类型,可以根据不同的数据展示需求选择不同的图表。

    • 折线图 (Line Chart) :展示趋势数据的变化,常用于时间序列分析。
    • 柱状图 (Bar Chart) :用于展示各类数据的对比。
    • 饼图 (Pie Chart) :显示各部分占比,适合展示比例关系。
    • 散点图 (Scatter Plot) :用于展示数据的分布关系,适合用于大规模数据的可视化。
    • 雷达图 (Radar Chart) :多维度展示数据,适合对比多个类别。
    • K线图 (Candlestick Chart) :用于金融领域的股票、期货等数据展示。
    • 地图 (Map) :地理信息数据可视化,支持展示区域信息。
    • 仪表盘 (Gauge Chart) :用于展示某些度量值的实时进度。
  2. 坐标系(Coordinate System)

    • 直角坐标系 (Cartesian Coordinate System) :最常见的坐标系类型,适用于折线图、柱状图等。
    • 极坐标系 (Polar Coordinate System) :适用于雷达图等。
    • 地理坐标系 (Geo Coordinate System) :用于地图数据展示。
  3. 系列(Series)

    • line:折线图系列。
    • bar:柱状图系列。
    • pie:饼图系列。
    • scatter:散点图系列。
    • radar:雷达图系列。
    • map:地图系列。
    • gauge:仪表盘系列。
  4. 交互(Interactive Features)

    • toolbox:工具箱,提供例如放大、缩小、切换图表类型等功能。
    • tooltip:提示框,在鼠标悬停时显示数据详情。
    • legend:图例,显示各数据系列的名称。
    • dataZoom:数据区域缩放,用户可以通过拖动或滚动来缩放数据。
    • markLine:标记线,显示数据中的特定标记位置。
    • markPoint:标记点,用于突出显示特定数据点。
  5. 轴(Axis)

    • xAxis:横坐标轴,通常用于展示时间、分类或其他数值。
    • yAxis:纵坐标轴,显示数据值。
    • polar:用于雷达图的极坐标轴。
  6. 视觉映射(Visual Mapping)

    • color:颜色映射,常用于饼图、散点图等中,帮助可视化展示不同的数据范围。
    • size:大小映射,根据数据的大小变化来调整显示的图形大小。
  7. 动画(Animation)

    • ECharts 支持各种动画效果,可以在加载图表时使用动画进行过渡,增加可视化的动态感。
  8. 自定义样式和主题(Custom Styling and Themes)

    • ECharts 提供了灵活的主题配置,开发者可以自定义样式,如颜色、字体、边框等,也可以使用内置的主题。

ECharts 常用功能总结:

  1. 数据绑定与动态更新:ECharts 支持实时数据更新,可以使用 JavaScript 动态改变图表的展示内容,适合展示实时变化的数据显示。
  2. 响应式布局:支持根据容器大小自动调整图表尺寸,适应不同的设备屏幕。
  3. 事件处理:可以为图表绑定事件(如点击、鼠标移入、拖动等),用于触发不同的交互效果。
  4. 多图表布局:支持在同一页面上展示多个图表,并且可以通过配置调整每个图表的显示位置和大小。
  5. 数据透视与过滤:通过 toolboxdataZoom 等组件实现数据的筛选和缩放,方便查看更详细的数据内容。

总结:

ECharts 是一个功能丰富、易于配置和高效的图表库。常用的组件包括各种图表类型(如折线图、柱状图、饼图等)、交互功能(如工具箱、提示框)、以及数据更新和动画等。通过合理使用这些组件,能够实现丰富的可视化效果,提升用户体验。

66.snabbdom是干什么的

Snabbdom 是一个虚拟 DOM(Virtual DOM)库,用于高效地更新和渲染 DOM。它的核心理念是最小化 DOM 变化,从而提高性能。

主要特点:

  1. 轻量级:核心库只有几百行代码,压缩后大约 2KB,非常适合高性能应用。
  2. 模块化:提供丰富的模块(如类名操作、事件监听、样式绑定等),可以自由扩展。
  3. 高效的 Diff 算法:使用 双端 Diff 算法,能快速对比新旧虚拟 DOM 并更新真实 DOM。
  4. 函数式 API:不依赖框架,API 设计简单清晰。

主要应用场景:

  • Vue 2.x 的虚拟 DOM 实现:Vue 2.x 内部使用 Snabbdom 作为核心虚拟 DOM 处理库。
  • 轻量级 Web UI 渲染:如果不想使用 React 或 Vue,但仍然需要高效的虚拟 DOM 更新,Snabbdom 是个不错的选择。
  • 自定义前端框架:Snabbdom 提供了基础的虚拟 DOM 机制,可用于构建自定义前端框架。

使用示例:

import { init } from "snabbdom";
import h from "snabbdom/h";

// 初始化 patch 函数(引入默认模块)
const patch = init([]);

// 创建虚拟节点
const vnode = h("div#container", [
  h("h1", "Hello Snabbdom!"),
  h("p", "This is a lightweight virtual DOM library."),
]);

// 挂载到真实 DOM
const app = document.getElementById("app");
patch(app, vnode);

Vue 3 为什么不用 Snabbdom?

Vue 3 放弃 Snabbdom,改用自研的虚拟 DOM 机制(基于 block tree optimization 的 Diff 算法),以提升性能和灵活性。