目录
- 一、基础类
- 1. Vue2 和 Vue3 响应式的区别?
- 2. Vue3 中 Composition API 相比 Options API 的优势?
- 3. Vue3 中的
Teleport有什么作用? - 4. CSS优化技巧?
- 5. Vue3 中 ref 和 reactive 的区别?
- 6. Vue 中 keep-alive 的实现原理?
- 7. Vue 中 diff 算法的优化点?
- 8. Nuxt 3 的混合渲染模式 (Hybrid Rendering) 是什么?请举例说明其应用场景。
- 9. Vue3 中的 Tree-shaking 是如何实现的?
- 10. Vue 中 computed 的实现原理?
- 11. Vite 和 webpack 区别
- 12. Vue2 中的 nextTick 原理?
- 二、原理类
- 三、场景题
- 22. 如何优化 Vue 中上万条列表渲染?
- 23. 如何优化 Vue 项目中的首屏加载速度?
- 24. Vue 中频繁触发的输入框校验如何优化?
- 25. Nuxt 项目如何实现 SEO 优化?
- 26. 微前端中如何实现跨子应用的状态共享?
- 27. Three.js 大模型渲染如何优化?
- 28. 前端会有哪些安全问题,什么场景出现,如何预防
- 29. SSR 中如何避免状态污染?
- 30. Vue 项目中如何解决组件通信复杂的问题?
- 31. 微前端如何处理公共依赖重复打包的问题?
- 32. qiankun如何实现JS沙箱?
- 33. Vue 项目如何减少包体积?
- 34. uni-app 跨端兼容的常见问题?
- 35. Vue3 如何做骨架屏优化首屏体验?
- 四、实战经验类
- 36. 如何在 Vue 项目中做前端埋点?
- 37. 如何设计 Vue 项目的权限系统?
- 38. 项目中主题色动态切换的实现方案?
- 39. Nuxt 如何实现服务端缓存?
- 40. pc端项目部署更新后,如何通知用户去主动刷新?有什么方案
- 41. Three.js 如何处理模型交互(如点击选中)?
- 42. Vue 项目中如何避免内存泄漏?
- 43. 微前端项目如何统一 UI 风格?
- 44. Vue 项目如何实现国际化 i18n?
- 45. 前端如何与后端协作优化大数据渲染?
- 46. 如果线上应用出现白屏问题,你会从哪些方面进行排查?
- 47. 设计一个庞大陈旧的 Vue 2 项目向 Vue 3 迁移的策略。
- 48. 如何实现一个知识图谱(圆形拖拽、连线、撤销)的设计方案?
目录
一、基础类
1. Vue2 和 Vue3 响应式的区别?
- Vue 2: 基于
Object.defineProperty,在初始化时需要递归遍历所有属性,无法监听属性的新增和删除,对数组的修改需要重写原型方法。 - Vue 3: 基于
Proxy(ES6),通过代理整个对象拦截所有操作(包括增删),支持懒代理,性能更优,解决了 Vue 2 的诸多限制。
2. Vue3 中 Composition API 相比 Options API 的优势?
- 逻辑组织: 解决了 Options API 中逻辑分散的问题,允许将同一功能相关的代码(数据、方法、计算属性)聚合在一起,提高可读性和维护性。
- 逻辑复用: 替代了 Mixins,通过 Composable 函数(Hooks)实现更灵活、无命名冲突、数据来源清晰的代码复用。
- 类型推导: 与 TypeScript 结合更自然,类型推导更准确。
- 性能: 导出的函数利于打包工具进行 Tree-shaking,减小产物包体积。
3. Vue3 中的 Teleport 有什么作用?
Teleport 是一个内置组件,作用是 将组件的内容渲染到 DOM 树中指定的其他位置。它常用于模态框、通知、提示框等场景,解决 z-index 或 overflow: hidden 等父组件样式导致的显示问题。
4. css优化技巧
- 合理使用选择器
- 减少DOM操作,减少重绘和重排
- 去除无效的选择器
- 文件压缩
- 异步加载文件
- 减少@import的使用
5. Vue3 中 ref 和 reactive 的区别?
| 特性 | ref | reactive |
|---|---|---|
| 接收参数 | 任何类型(原始类型或对象) | 仅限对象类型 |
| 访问/修改 | 必须通过 .value | 直接访问属性 |
| 底层实现 | 内部包含一个值,如果值为对象则会调用 reactive | 使用 Proxy 对对象进行深度代理 |
| 模板使用 | 自动解包(无需 .value) | 直接使用 |
6. Vue 中 keep-alive 的实现原理?
keep-alive 是一个内置组件,通过在内部维护一个 缓存对象 和一个 Key 列表 来实现组件实例的缓存。当组件切换时:
- 检查缓存,如果命中,直接复用已有的组件实例(不重新执行
created/mounted)。 - 如果没有命中,则创建新实例并将其存储在缓存中。
- 如果缓存数量达到
max,则使用 LRU(最近最少使用) 策略淘汰最旧的组件。
7. Vue 中 diff 算法的优化点?
Vue 的 diff 算法基于同层比较和启发式策略优化性能。
- 同层比较: 只比较同一层级的节点,将复杂度从 O(n³) 降至 O(n)。
- 双端比较 (Vue 2): 引入四个指针(新旧头尾),通过快速比较头尾节点,高效处理列表的头尾增删或反转。
- 最长递增子序列 (LIS) (Vue 3): 在处理乱序列表时,计算 LIS 以找到无需移动的节点,最大限度减少 DOM 移动操作,性能更优。
- Key 的重要性: 通过
key确保 VNode 的唯一身份,从而实现高效复用和节点移动。
8. Nuxt 3 的混合渲染模式 (Hybrid Rendering) 是什么?请举例说明其应用场景。
混合渲染允许在 同一个 Nuxt 应用中,为不同的路由配置不同的渲染策略。
- 策略包括: SSR(动态服务端渲染)、SSG(构建时静态生成)、CSR(客户端渲染)和 ISG(增量静态生成)。
- 应用场景:
- 首页/文档: 使用 SSG 获得最高性能和 SEO。
- 用户中心/购物车: 使用 CSR 或 SSR 以确保实时性和个性化。
- 博客/商品详情页: 使用 ISG 兼顾性能和数据新鲜度。
9. Vue3 中的 Tree-shaking 是如何实现的?
Vue 3 的 Tree-shaking 并非 Vue 自身实现,而是通过 ES Modules (ESM) 的静态特性 和 函数化 API 设计 实现:
- ESM 静态分析: Vue 3 核心功能都是通过
import { ref, computed }导入的,打包工具(如 Rollup/Vite)可以静态分析出哪些函数被使用了。 - 函数化设计: 几乎所有功能都作为独立函数导出,相比 Vue 2 挂载在
this上的动态属性,Vue 3 的模块化设计更容易被摇树算法识别和移除未使用代码,从而减小最终包体积。
10. Vue 中 computed 的实现原理?
计算属性的核心是 惰性求值 (Lazy Evaluation) 和 缓存 (Caching)。
- Watcher 机制: 每个计算属性都对应一个
Computed Watcher,该 Watcher 默认是惰性 (lazy: true)。 - 依赖收集: 首次访问时,Watcher 执行计算函数,收集其依赖(如
ref或reactive)。 - 缓存: 结果被缓存,并设置
dirty标志为false。 - 依赖变化: 当依赖变化时,Watcher 收到通知,只将
dirty标志设为true,不立即重新计算。 - 重新求值: 只有当计算属性再次被访问时,因为它发现
dirty为true,才会重新执行计算函数,更新缓存,并重置dirty标志。如果dirty为false,则直接返回缓存结果。
11. Vite 和 webpack 区别
| 特性 | Vite | Webpack |
|---|---|---|
| 核心原理 | 基于浏览器原生 ESM,开发时无需打包,按需加载。 | 基于打包 (Bundling),将所有模块打包成 bundle 文件。 |
| 启动速度 | 极快(毫秒/秒级)。 | 较慢(随着项目增大而变慢)。 |
| HMR 速度 | 极快,HMR 仅针对修改的文件,与项目规模无关。 | 相对较慢,需要重新打包部分模块。 |
| 生产构建 | 使用 Rollup,专注于 Tree-shaking 和优化。 | 使用自身打包器,生态成熟,功能强大。 |
12. Vue2 中的 nextTick 原理?
nextTick 的作用是延迟回调函数到 下一次 DOM 更新循环之后 执行。
- 原因: Vue 的 DOM 更新是异步的,将数据变更缓冲在一个队列中。
- 原理:
nextTick利用 JavaScript 的 事件循环 机制,将回调函数推入到 微任务队列 中(优先使用Promise.then或MutationObserver,降级使用宏任务setTimeout(fn, 0))。 - Vue 在当前事件循环的同步代码执行完毕后,会清空微任务队列,Vue 的 DOM 更新任务在此时执行,
nextTick的回调紧随其后,从而保证了回调执行时 DOM 已经是最新的状态。
二、原理类
13. 如何封装一个带有自动请求和销毁逻辑的 useFetch Hook?
- 响应式状态: 使用
ref或shallowRef定义data,error,isLoading状态。 - 请求封装: 封装数据获取函数
fetchData。 - 自动取消: 在
fetchData中使用AbortController发出请求,并在onUnmounted钩子中调用controller.abort(),以确保组件销毁时能自动取消未完成的请求,防止内存泄漏。 - 自动执行: 在
setup函数中立即调用fetchData来发起请求。 - 返回值: 返回包含所有状态和手动刷新函数
refresh的对象。
14. Nuxt3 的 SSR 渲染流程?
- 服务器接收请求: Nitro 引擎接收到请求,创建一个新的 Vue 独立实例(避免状态污染)。
- 数据预取: 执行页面组件中的
useAsyncData/useFetch等服务端数据获取逻辑,等待数据就绪。 - 渲染 HTML: 使用 Vue 的 SSR 渲染器将带数据的 Vue 实例渲染成完整的 HTML 字符串。
- 序列化状态: 将预取到的数据状态序列化并注入到 HTML 的
<script>标签中 (window.__NUXT__)。 - 发送响应: 服务器将完整的 HTML 响应发送给浏览器。
- 客户端激活 (Hydration): 浏览器下载 JS,读取序列化状态,并用客户端 Vue 实例接管和“激活”已有的静态 DOM,附加事件监听器,使页面成为一个功能完整的 SPA。
15. Vue Router4 中的路由懒加载如何实现?
通过使用 ES6 动态 import() 语法实现。
在路由配置中,将组件定义为一个返回 import() 的函数:
{
path: '/about',
component: () => import('../views/About.vue')
}
构建工具(Webpack/Vite)会识别 import() 作为代码分割点,将对应的组件及其依赖打包成一个独立的 chunk,只有当路由被访问时才异步加载该文件。
16. 在大型 Vue 3 项目中,你是如何进行状态管理的?
通常采用 组合策略:
- Pinia: 作为 全局状态管理的首选。它类型安全、API 简洁、支持模块化和 Tree-shaking,适用于管理用户认证、全局配置等核心状态。
- Composable Functions (Hooks): 适用于 逻辑复用和模块级状态。用于封装
useFetch,useMousePosition等可复用的有状态逻辑。也可以用于轻量级的全局状态共享。 - Provide/Inject: 适用于 特定组件树内的通信,解决 Prop 逐层传递(Props Drilling)问题,多用于组件库或复杂的局部表单结构。
17. Vue3 是如何支持 TypeScript 的?TypeScript中的接口是什么?如何定义和使用接口
Vue 3 将 TypeScript 作为一等公民。
- 源码使用 TS 重写: 提供了准确、完整的类型定义文件。
- Composition API 的天然优势: 函数式的 API 易于类型推导。
- 类型安全的 API: 使用
defineProps<T>、defineEmits和ref<T>等泛型 API,提供了编译时的类型检查。 - Volar 语言工具: 提供了对 SFC (
.vue文件) 中模板和脚本的深度类型支持。
接口是一种用于定义对象的结构和类型的语法。可以使用interface关键字来定义接口。
18. 微前端架构中子应用的隔离原理?
隔离主要体现在三个方面:
- CSS 隔离: 使用 Shadow DOM(硬隔离)或 Scoped CSS/CSS Modules(软隔离)来避免样式冲突。
- JavaScript 隔离 (JS 沙箱): 主要通过
Proxy代理沙箱 来实现。它为每个子应用创建一个代理的window对象,所有对全局变量的修改都发生在代理对象上,从而防止全局变量污染和冲突。 - DOM 隔离: 通过约定每个子应用渲染到独立的容器中,并对
document.querySelector等 API 进行劫持,将查找范围限定在子应用的容器内。
19. 请说出三种减少页面加载时间的方法。(加载时间指感知的时间或者实际加载时间)
- 优化图片
- 图像格式的选择(GIF:提供的颜色较少,可用在一些对颜色要求不高的地方)
- 优化CSS(压缩合并css,如margin-top,margin-left...)
- 网址后加斜杠(如www.campr.com/目录,会判断这个“目录是什么文件类型,或者是目录。)
- 标明高度和宽度(如果浏览器没有找到这两个参数,它需要一边下载图片一边计算大小,如果图片很多,浏览器需要不断地调整页面。这不但影响速度,也影响浏览体验。当浏览器知道了高度和宽度参数后,即使图片暂时无法显示,页面上也会腾出图片的空位,然后继续加载后面的内容。从而加载时间快了,浏览体验也更好了。)
- 减少http请求(合并文件,合并图片)。
20. HTTP/2 相比 HTTP/1.1 的核心优势是什么?
- 二进制分帧: 将请求和响应拆分为二进制帧,解析更高效。
- 多路复用 (Multiplexing): 允许在 单个 TCP 连接 上同时传输多个请求和响应,彻底解决了 HTTP/1.1 的 队头阻塞 问题。
- 头部压缩 (HPACK): 使用字典和霍夫曼编码压缩 HTTP 头部信息,减少传输开销。
- 服务器推送 (Server Push): 服务器可以在客户端请求之前主动推送所需资源,减少往返时间。
21. 如何理解响应式网站?
响应式网站设计 (RWD) 是一种理念:使用一套 HTML 和 CSS 代码,使网站布局能够自动适应和优化在各种不同屏幕尺寸(PC、平板、手机)上的显示效果。
核心技术包括:
- 流式布局: 使用百分比、
vw等相对单位。 - 媒体查询 (Media Queries): 根据视口宽度设置不同的断点来应用不同的布局样式。
- 弹性图片: 使用
max-width: 100%确保图片不会溢出容器。
三、场景题
22. 如何优化 Vue 中上万条列表渲染?
核心思想是 只渲染用户可见的部分。
- 虚拟列表 / 虚拟滚动 (Virtual Scrolling): 最优解。通过计算只渲染视口内及其缓冲区内的列表项,并使用占位元素维持滚动条的整体高度。
- 时间分片 (Time Slicing): 如果不采用虚拟滚动,可将数据分批次渲染(如每帧渲染 50 条),利用
requestAnimationFrame避免阻塞主线程。 - 分页: 将数据分为多页展示,减少单次渲染量。
23. 如何优化 Vue 项目中的首屏加载速度?
- 减少包体积: 路由懒加载、组件按需引入、Tree-shaking、移除无用代码、使用包分析工具。
- 优化资源加载: 图片压缩、使用 WebP 格式、开启 Gzip/Brotli 压缩、使用 CDN。
- 渲染策略优化: 采用 SSR/SSG 或 预渲染,以加快内容渲染;使用 骨架屏 优化用户感知体验。
- 浏览器缓存: 配置 HTTP 缓存头,利用 Service Worker 或设置合适的
Cache-Control。
24. Vue 中频繁触发的输入框校验如何优化?
主要使用 防抖 (Debounce) 技术。
- 防抖: 在事件触发后延迟执行回调,如果在延迟时间内事件再次触发,则重新计时。
- 适用场景: 用户在连续输入时,只在用户停止输入后,执行一次校验或 API 请求。
- 实现: 可使用自定义 Composable(如
useDebouncedRef)或 Lodash 等工具库的debounce函数。
25. Nuxt 项目如何实现 SEO 优化?
Nuxt 默认的 SSR/SSG 是基础。在此之上,核心在于元数据管理:
- 动态元数据: 在页面组件中使用
useHeadComposable 动态设置页面的title,description,og:image等元标签。 - 结构化数据: 嵌入 JSON-LD 格式的 Schema.org 结构化数据,帮助搜索引擎理解内容(如产品、文章)。
- 工具链: 使用 Nuxt 模块生成 Sitemap (
sitemap.xml) 和robots.txt。 - 语义化 HTML: 确保使用正确的 HTML5 标签和清晰的标题层级 (
<h1>-<h6>)。
26. 微前端中如何实现跨子应用的状态共享?
- 共同的状态管理库 (Pinia/Redux): 最推荐的方案。 由主应用初始化一个 Store 实例,并将其传递给子应用。所有应用共享这个实例。
- 主应用下发 Props: 通过微前端框架的 API,主应用将共享数据作为 Props 传递给子应用。
- Module Federation (模块联邦): 允许一个应用暴露 Store 或状态函数,其他应用运行时按需加载,实现去中心化的共享。
- 浏览器原生通信: 使用
CustomEvent或postMessage实现基于发布-订阅的通信(适合少量简单数据)。
27. Three.js用过吗? 大模型渲染如何优化?
- 模型资产优化:
- 减面: 减少模型多边形数量。
- LOD (Level of Detail): 根据距离动态切换高、中、低精度模型。
- 压缩: 使用 Draco 压缩几何体,使用 KTX2 压缩纹理。
- 减少 Draw Call:
- 合并几何体: 将相同材质的小物体合并为单个 Mesh。
- 实例化渲染: 对大量重复物体使用
InstancedMesh。
- 渲染策略: 确保开启 视锥剔除;合理使用阴影;如果场景静止,使用 按需渲染(只在必要时调用
renderer.render())。 - 资源管理: 及时调用
dispose()释放几何体、材质和纹理占用的 GPU 显存。
28. 前端会有哪些安全问题,什么场景出现,如何预防
| 安全问题 | 场景 | 预防措施 |
|---|---|---|
| XSS (跨站脚本攻击) | 评论区、URL 参数注入恶意 JS 脚本。 | 输入过滤和输出转义;使用 CSP (Content Security Policy) 限制脚本来源;谨慎使用 v-html。 |
| CSRF (跨站请求伪造) | 诱导用户点击恶意链接,冒用身份发起请求。 | Anti-CSRF Token;验证 Referer;设置 SameSite=Strict Cookie。 |
| 中间人攻击 (MITM) | 在公共网络拦截并窃听/篡改通信。 | 全站启用 HTTPS;配置 HSTS。 |
| 点击劫持 | 透明 <iframe> 覆盖在敏感操作按钮上。 | 设置 X-Frame-Options: DENY 响应头。 |
29. SSR 中如何避免状态污染?
状态污染是由于多用户请求共享服务器上的单例状态导致的。
- 解决方案: 核心是 为每一次独立的请求创建一个全新的、隔离的状态实例。
- 实践: 必须使用 工厂函数 模式来创建 Vue 实例、Router 实例和 Pinia/Vuex Store 实例。确保 Pinia Store 的
state必须是一个 返回新对象的函数,以保证每次请求的状态都是独立的。
30. Vue 项目中如何解决组件通信复杂的问题?
- 父子通信: 优先使用
Props和$emit,保持单向数据流。 - 跨层级通信: 使用
Provide / Inject解决 Prop 逐层传递问题。 - 任意组件通信:
- 状态管理库 (Pinia / Vuex): 首选方案。将全局或跨模块状态集中管理,实现组件间的解耦和清晰的数据流。
- Composable Functions: 适用于模块级的、轻量级的状态共享。
- (避免使用 Event Bus)
31. 微前端如何处理公共依赖重复打包的问题?
核心是实现 依赖共享。
- Module Federation (模块联邦): 最推荐的方案。 使用 Webpack 5 的
shared配置,允许在不同应用间运行时共享单例依赖,并智能处理版本兼容。 externals+ CDN: 传统方案。将 Vue、React 等库通过 CDN 全局引入,子应用在构建时将这些库配置为externals,不进行打包。- 公共组件库 npm 包: 虽然解决了代码复用,但如果不配合模块联邦,仍可能存在重复下载依赖的问题。
32. qiankun如何实现JS沙箱?
qiankun 主要使用两种沙箱:
- 快照沙箱 (SnapshotSandbox): 兼容方案。在子应用激活前记录
window状态快照,在失活时恢复状态。不支持多实例共存。 - 代理沙箱 (ProxySandbox): 主流方案。利用
Proxy为每个子应用创建一个“假的”window代理对象。读操作穿透到真实window,写操作只发生在代理对象上,从而实现多实例共存下的隔离。
33. Vue 项目如何减少包体积?
- 代码分割: 路由懒加载、异步组件 (
defineAsyncComponent)。 - 按需引入: 使用插件(如
unplugin-vue-components)或手动按需引入 UI 库和工具库。 - Tree-shaking: 确保使用 ESM 语法,并配置构建工具移除死代码。
- 资源优化: 图片压缩、字体文件裁剪、使用 CDN 托管大文件。
- 传输优化: 在服务器端开启 Gzip/Brotli 压缩。
34. uni-app 跨端兼容的常见问题?
- API 兼容性: 不同平台(H5/小程序/App)API 支持不一致。解决方案: 查阅文档,使用 条件编译 (
// #ifdef H5) 编写平台特定代码。 - 样式兼容性: 单位、选择器和 Flex 布局的差异,尤其在
nvue中。解决方案: 优先使用 Flex 布局,使用rpx作为尺寸单位。 - 原生组件层级:
<video>,<map>等原生组件层级最高。解决方案: 在这些组件上覆盖内容时,使用cover-view或cover-image。
35. Vue3 如何做骨架屏优化首屏体验?
骨架屏通过在数据加载前展示页面轮廓来优化用户感知体验。
- 手动编写: 创建一个与内容结构相似的
Skeleton组件,并使用 CSS 动画实现加载效果。 - 组件控制: 在页面中使用
v-if或el-skeleton等组件,通过一个loading状态来控制显示骨架屏还是真实内容。 - 自动化方案(高级): 使用 Puppeteer 等工具,在构建时自动截取页面结构,生成高保真的骨架屏组件代码,并内联到 HTML 中。
四、实战经验类
36. 如何在 Vue 项目中做前端埋点?
通常采用 混合埋点 方案:
- 代码埋点(核心):
- 实现: 封装一个
tracker模块,通过navigator.sendBeacon或Image发送请求。 - 使用: 通过
app.provide或globalProperties注入到组件中。在关键业务操作(如购买、注册)处手动调用this.$tracker.trackClick(...)并附带业务参数。 - PV 自动上报: 结合 Vue Router 的
afterEach导航守卫自动上报页面浏览事件。
- 实现: 封装一个
- 无痕埋点(基础): 通过全局监听 DOM 事件,自动采集用户点击和行为路径(通常由第三方或自研 SDK 实现)。
37. 如何设计 Vue 项目的权限系统?
权限系统分为 路由权限 和 元素权限。
- 权限数据获取: 登录后,从后端获取用户角色的 权限码列表(如
user:create)。 - 路由权限: 在 Vue Router 的
beforeEach导航守卫 中实现。- 检查用户权限信息是否存在,如果不存在则异步获取。
- 使用
router.addRoute(),根据用户的权限码过滤并动态注册可访问的路由表。 - 使用
next({ ...to, replace: true })确保重定向。
- 元素权限: 通过 自定义指令
v-permission实现。- 指令的
mounted钩子读取指令值(所需权限码),检查用户是否拥有该权限。 - 如果没有权限,则直接从 DOM 中移除元素 (
el.parentNode.removeChild(el))。
- 指令的
38. 项目中主题色动态切换的实现方案?
基于 CSS 变量 (CSS Custom Properties) 的方案是最佳实践。
- 定义变量: 在
:root下定义主色、背景色等 CSS 变量(如--el-color-primary)。 - 使用变量: 在所有组件样式中使用
var(--variable-name)引用颜色。 - 动态切换:
- 预定义主题: 通过 JS 切换
<html>元素的 class(如light/dark),对应 class 下定义不同的 CSS 变量值。 - 自定义颜色: 使用 JS 调用
document.documentElement.style.setProperty('--el-color-primary', newColor)动态修改变量值。
- 预定义主题: 通过 JS 切换
39. Nuxt 如何实现服务端缓存?
Nuxt 3 的服务端引擎 Nitro 提供了强大的缓存能力。
- 路由规则缓存 (
routeRules): 最推荐。 在nuxt.config.ts中为特定路由配置isr(增量静态生成)或cache策略,Nitro 会缓存整个页面或 API 响应。 - 服务端函数缓存 (
cachedFunction): 在服务端 API 或工具函数中使用cachedFunction包裹耗时的计算或数据获取逻辑,实现函数级别的精细缓存。 - 外部缓存层: 集成 Redis 或 Varnish/Nginx 等反向代理层进行缓存,适用于高性能和分布式部署。
40. pc端项目部署更新后,如何通知用户去主动刷新?有什么方案
最佳方案:基于文件 Hash 的轮询检查。
- 生成版本文件: 每次部署时,生成一个不缓存的
version.json文件,内含当前版本号(如 Git Hash)。 - 前端轮询: 客户端定时(如 5 分钟)请求该文件,并比较版本号。
- 提示刷新: 如果版本号不一致,说明有新版本部署,弹出通知(如
ElNotification),引导用户点击按钮执行location.reload(true)强制刷新。
41. Three.js 如何处理模型交互(如点击选中)?
使用 光线投射 (Raycasting)。
- 标准化坐标: 将屏幕上的鼠标
clientX/Y坐标转换为 Three.js 的标准化设备坐标 (NDC)[-1, 1]。 - 创建 Raycaster: 使用
new THREE.Raycaster()并调用raycaster.setFromCamera(mouse, camera),从摄像机向鼠标方向发射一条射线。 - 相交检测: 调用
raycaster.intersectObjects(objects)检测射线与场景中的哪些物体相交。 - 处理结果: 检查返回的相交数组,通常选取第一个相交物体(最近的),对其进行高亮或其他交互操作。
42. Vue 项目中如何避免内存泄漏?
核心是 在组件销毁时(onUnmounted)手动清理不受 Vue 自动管理的所有外部资源。
- 清理事件监听器: 移除在
window,document或其他全局对象上添加的所有addEventListener监听器。 - 清理定时器: 清除所有
setTimeout和setInterval。 - 销毁第三方库实例: 手动调用第三方库(如 ECharts, Three.js 渲染器)提供的
dispose()或destroy()方法来释放其占用的内存。 - 避免全局引用: 避免在 Vue 实例外部持有对组件内部响应式数据或 DOM 节点的持久引用。
43. 微前端项目如何统一 UI 风格?
- 设计系统: 建立一套统一的设计规范(颜色、字体、间距、组件行为)。
- 公共组件库: 将核心组件封装成一个独立的组件库。
- Module Federation (推荐): 利用模块联邦在运行时共享这个公共组件库,确保所有子应用使用同一份 UI 代码,解决了依赖重复和版本不一致的问题。
- CSS 变量: 全局加载一套基础 CSS 变量,确保所有子应用的主题色和基础样式一致。
44. Vue 项目如何实现国际化 i18n?
使用 vue-i18n 库。
- 配置: 创建
createI18n实例,设置默认语言和回退语言,并传入各语言的翻译文件 (messages)。 - 模板使用: 在模板中使用
{{ $t('key') }}或 Composition API 中的t('key')函数。 - 切换语言: 通过修改
i18n.global.locale的响应式属性,视图会自动更新。 - 优化: 对于大型应用,将语言文件设置为 异步组件,按需懒加载,以减小首屏体积。
45. 前端如何与后端协作优化大数据渲染?
核心是前后端共同实现 数据分片、按需提供。
- API 协议: 统一接口协议,支持分页(
page/pageSize)或无限加载(offset/limit)。 - 后端分片: 后端在数据库层面进行高效分页查询,只返回前端请求的数据子集。
- 数据聚合/抽样: 对于可视化场景(如大型图表),后端应根据前端的缩放级别,返回聚合或抽样后的数据,避免传输大量原始数据。
- 动态加载: 树形结构或复杂列表应采用懒加载模式,前端按需请求子节点或下一批数据。
- 实时数据: 使用 WebSocket 进行流式传输增量数据,而不是频繁轮询。
46. 如果线上应用出现白屏问题,你会从哪些方面进行排查?
- 信息收集: 确定影响范围(所有用户/特定用户)、浏览器和最近的发布记录。
- 前端监控平台: 检查 Sentry 等平台是否有致命的 JS 错误堆栈,定位到引发白屏的代码行。
- 网络检查 (F12 Network): 检查关键 JS/CSS 资源文件是否加载失败(404/500),判断是否是 CDN 或部署问题。
- 控制台检查 (F12 Console): 查找应用初始化阶段是否有阻塞性的 JS 报错。
- 服务端/配置检查: 确认 API 接口是否正常,服务器配置是否正确,如 Gzip 压缩是否导致文件损坏。
- 代码回滚: 如果无法快速定位,立即回滚到上一个稳定版本。
47. 设计一个庞大陈旧的 Vue 2 项目向 Vue 3 迁移的策略。
采用渐进式、不中断业务的迁移策略:
- 环境共存: 引入
@vue/compat(迁移构建),让 Vue 3 兼容 Vue 2 的语法,使旧代码能在新环境下运行。 - 引入 TS/Vite: 将构建系统从 Vue CLI/Webpack 迁移到 Vite,并配置 TypeScript (
allowJs: true)。 - 自下而上迁移组件: 从最底层的、依赖最少的叶子组件开始,将其重构为 Composition API +
<script setup lang="ts">语法。 - 状态分步升级: 新模块使用 Pinia,旧 Vuex 模块逐步重构迁移到 Pinia。
- 清理与收尾: 当所有核心代码都迁移完成后,移除
@vue/compat,使项目完全运行在纯 Vue 3 模式下。
48. 如何实现一个知识图谱(圆形拖拽、连线、撤销)的设计方案?
- 技术选型: 优先选择 SVG。SVG 图形是 DOM 元素,天然支持事件监听和交互,相比 Canvas 更容易实现拖拽和点击选中。
- 渲染层:
- 连线: 使用 SVG 的
<path>元素绘制,路径坐标根据源节点和目标节点的实时位置动态计算。 - 节点: 使用 SVG 的
<circle>和<text>封装在<g>元素中,使用transform: translate()定位。
- 连线: 使用 SVG 的
- 交互(拖拽): 监听节点的
mousedown事件,在画布的mousemove事件中更新节点在 Pinia Store 中的x/y坐标,视图自动响应更新。 - 撤销/重做: 采用 命令模式 (Command Pattern)。将每个操作(移动节点、添加连线)封装成一个包含
execute()和undo()方法的命令对象,并维护undoStack和redoStack两个堆栈。