1. 什么是 Vue.js?它的核心特点是什么?
Vue.js 是一个渐进式 JavaScript 框架,用于构建用户界面。它的核心特点包括:
- 响应式数据绑定
- 组件化开发
- 虚拟 DOM
- 指令系统
- 轻量级且易于集成
- 丰富的生态系统(Vue Router, Vuex, Vue CLI等)
2. Vue 的双向数据绑定原理是什么?
Vue 使用数据劫持结合发布者-订阅者模式实现双向绑定:
1.通过 Object.defineProperty() 或 Proxy 对数据对象进行劫持2. 在 getter 中收集依赖(Watcher)3. 在 setter 中通知变化,触发更新4. 编译器解析模板指令,初始化视图5. Watcher 作为桥梁,连接 Observer 和 Compile
3. Vue 2.x 和 Vue 3.x 的主要区别有哪些?
1. 响应式系统:Vue 2 使用 Object.defineProperty,Vue 3 使用 Proxy
2. 性能:Vue 3 打包体积更小,虚拟 DOM 重写,性能更好
3. Composition API:Vue 3 引入新的代码组织方式
4. 片段支持:Vue 3 支持多根节点组件
5. Teleport 和 Suspense:Vue 3 新增的内置组件
6. TypeScript 支持:Vue 3 有更好的 TS 集成
7. 自定义渲染器 API
8. 全局 API 改为应用实例 API
4. 什么是 MVVM 模式?Vue 是如何实现 MVVM 的?
MVVM 是 Model-View-ViewModel 的缩写:- Model:数据模型- View:UI 界面- ViewModel:连接 View 和 Model 的桥梁Vue 实现 MVVM:1. View:模板(template)2. Model:数据对象(data)3. ViewModel:Vue 实例,通过响应式系统和模板编译连接 View 和 Model
5. Vue 的生命周期钩子有哪些?分别在什么阶段调用?
Vue 2.x 生命周期:beforeCreate:实例初始化后,数据观测之前
created:实例创建完成,数据观测完成
beforeMount:挂载开始之前
mounted:实例挂载到 DOM 后
beforeUpdate:数据更新时,DOM 更新前
updated:数据更新后,DOM 更新完成
beforeDestroy:实例销毁前
destroyed:实例销毁后
Vue 3.x 对应:
beforeCreate → 使用 setup()
created → 使用 setup()
beforeMount → onBeforeMount
mounted → onMounted
beforeUpdate → onBeforeUpdate
updated → onUpdated
beforeUnmount → onBeforeUnmount
unmounted → onUnmounted
6. Vue 组件间通信的方式有哪些?
1. 父子组件通信:Props 向下传递自定义事件向上传递 ($emit)v-model / .sync 语法糖parent/children (不推荐)ref 获取组件实例2. 兄弟组件通信:通过共同的父组件中转Event BusVuex3. 跨层级通信:provide / injectVuex全局事件总线4. 其他:attrs/listeners (Vue 2)v-bind="$attrs" (Vue 3)状态管理库 (Vuex, Pinia)
7. 什么是单向数据流?为什么 Vue 要采用这种设计?
单向数据流是指:
父组件通过 props 向子组件传递数据
子组件不能直接修改 props,只能通过事件通知父组件修改
原因:
使数据流更易于理解和调试
防止子组件意外修改父组件状态
降低组件间耦合度
更好的维护性和可预测性
8. 动态组件是什么?如何使用?
动态组件是通过 <component>
元素和 is 特性实现的组件动态切换:
<component :is="currentComponent"></component>
使用场景:
标签页切换
根据条件渲染不同组件
动态布局系统
注意事项:
可以使用 <keep-alive> 缓存组件状态
切换时组件会销毁和重建
9. 异步组件是什么?如何实现?
异步组件是按需加载的组件,可以提高首屏加载速度。
Vue 2.x 实现:
components: {AsyncComponent: () => import('./AsyncComponent.vue')
}
Vue 3.x 新增 defineAsyncComponent:
import { defineAsyncComponent } from 'vue'const AsyncComp = defineAsyncComponent(() => import('./components/AsyncComponent.vue')
)
高级配置:
const AsyncComp = defineAsyncComponent({loader: () => import('./Foo.vue'),loadingComponent: LoadingComponent,errorComponent: ErrorComponent,delay: 200,timeout: 3000
})
10. 什么是函数式组件?有什么特点?
函数式组件是无状态、无实例的组件,渲染开销小。
特点:
没有响应式数据
没有实例(this)
只接受 props
无生命周期钩子
渲染性能高
Vue 2.x 声明:
Vue.component('functional-comp', {functional: true,render(h, context) {// ...}
})
Vue 3.x 声明:
import { h } from 'vue'
const FunctionalComp = (props, context) => {return h('div', props.msg)
}
11. v-if 和 v-show 的区别是什么?
v-if:条件渲染,元素不存在于 DOM 中,切换时销毁/重建组件
v-show:条件显示,元素始终存在于 DOM 中,只是切换 display 属性
使用场景:
v-if:切换频率低,条件不太可能改变
v-show:频繁切换,初始渲染成本高
性能考虑:
v-if 有更高的切换开销
v-show 有更高的初始渲染开销
12. v-for 指令的 key 属性有什么作用?
key 的作用:
帮助 Vue 识别节点身份,高效更新虚拟 DOM
避免就地复用元素
维护组件状态和子组件状态
最佳实践:
使用唯一标识作为 key
避免使用索引作为 key(当列表顺序变化时会有问题)
在 v-for 中总是提供 key
13. v-model 的原理是什么?如何自定义 v-model?
答案:
v-model 是语法糖,默认相当于:
<input :value="value" @input="value = $event.target.value" />
自定义组件 v-model (Vue 2.x):
model: {prop: 'checked',event: 'change'
},
props: {checked: Boolean
}
Vue 3.x 支持多个 v-model:
<ChildComponent v-model:title="title" v-model:content="content" />
14. Vue 有哪些内置指令?各自的作用是什么?
答案:
常用内置指令:
v-text:更新元素的 textContent
v-html:更新元素的 innerHTML
v-show:条件显示
v-if/v-else-if/v-else:条件渲染
v-for:列表渲染
v-on (@):绑定事件
v-bind (:):绑定属性
v-model:双向绑定
v-slot (#):插槽
v-pre:跳过编译
v-once:只渲染一次
v-memo:记忆渲染 (Vue 3.2+)
v-cloak:防止闪现
15. 自定义指令是什么?如何实现?
答案:
自定义指令用于对普通 DOM 元素进行底层操作。
注册全局指令:
// Vue 2
Vue.directive('focus', {inserted(el) {el.focus()}
})// Vue 3
app.directive('focus', {mounted(el) {el.focus()}
})
注册局部指令:
directives: {focus: {mounted(el) {el.focus()}}
}
钩子函数 (Vue 2 → Vue 3):
bind → beforeMount
inserted → mounted
update → 移除,改用 updated
componentUpdated → updated
unbind → unmounted
16. Vue 2.x 的响应式原理是什么?有什么缺陷?
答案:
原理:
使用 Object.defineProperty 劫持对象属性
在 getter 中收集依赖
在 setter 中通知更新
每个组件实例对应一个 Watcher 实例
缺陷:
无法检测对象属性的添加或删除(需要 Vue.set/Vue.delete)
无法检测数组索引和长度的变化
对 ES6+ 的 Map、Set 等不支持
初始化时递归遍历所有属性,性能有影响
17. Vue 3.x 的响应式系统有什么改进?
答案:
改进:
使用 Proxy 代替 Object.defineProperty可以检测属性添加/删除支持 Map、Set 等数据结构
性能优化:惰性响应式(按需响应)更好的缓存机制
更精确的变更检测
支持嵌套对象的自动解包
独立的响应式模块(可单独使用)
18. Vue.set/Vue.delete 的作用是什么?Vue 3 中还需要吗?
答案:
Vue 2.x 中:
Vue.set:向响应式对象添加新属性并触发视图更新
Vue.delete:删除属性并触发视图更新
Vue 3.x 中:
不再需要,因为 Proxy 可以检测到属性的添加和删除
但仍保留了 set 和 delete 的 API 用于兼容
19. 什么是响应式数据的副作用函数?Vue 中如何收集依赖?
答案:
副作用函数是指会对外部产生影响的函数,如修改 DOM、发送请求等。
Vue 收集依赖的过程:
在组件渲染时,会执行 render 函数访问响应式数据
触发数据的 getter,将当前 Watcher(副作用函数)添加到依赖中
数据变化时,触发 setter,通知所有依赖的 Watcher 更新
Watcher 执行更新函数,重新渲染或执行副作用
20. computed 和 watch 的区别是什么?
答案:
computed:
计算属性,基于依赖缓存
必须有返回值
适合派生状态
同步操作
watch:
观察特定数据变化
无返回值
适合执行异步或复杂操作
可以观察多个数据源
使用场景:
computed:模板中的复杂表达式、数据格式化
watch:数据变化时需要执行异步操作或复杂逻辑
虚拟 DOM 和渲染
21. 什么是虚拟 DOM?它的优点是什么?
答案:
虚拟 DOM 是真实 DOM 的轻量级 JavaScript 表示。
优点:
减少直接操作 DOM 的性能开销
提供跨平台能力(如 SSR、Native 渲染)
高效的差异比较算法(diff)
批量更新 DOM
开发体验更接近声明式编程
工作原理:
用 JavaScript 对象表示 DOM 结构
状态变化时生成新的虚拟 DOM
比较新旧虚拟 DOM (diff)
将差异应用到真实 DOM (patch)
22. Vue 的 diff 算法是怎样的?
答案:
Vue 的 diff 算法特点:
同级比较,不跨级
使用 key 识别节点身份
双端比较策略(Vue 3 优化为更高效的算法)
优先处理特殊情况(如相同节点、文本节点)
优化策略:
静态节点提升(Vue 3)
事件缓存
区块树优化(Vue 3)
更高效的 patch 标志
23. Vue 3 在虚拟 DOM 方面有哪些优化?
答案:
Vue 3 虚拟 DOM 优化:
静态提升:将静态节点提升到渲染函数外
补丁标志:为动态节点添加标志,减少比较范围
缓存事件处理函数
区块树:将静态和动态内容分离
更高效的 diff 算法
支持片段(多根节点)
更快的挂载和更新性能
24. 什么是渲染函数?什么情况下需要使用渲染函数?
答案:
渲染函数是用于编程式创建虚拟 DOM 的函数(h 函数)。
使用场景:
动态性很强的组件
需要更灵活的模板逻辑
高阶组件
需要 JavaScript 的完整编程能力
性能敏感场景(比模板更高效)
示例:
render(h) {return h('div', { class: 'container' }, [h('h1', 'Title'),this.showSubtitle ? h('h2', 'Subtitle') : null])
}
25. Vue 3 的 h 函数有什么变化?
答案:
Vue 3 中 h 函数的变化:
需要从 vue 显式导入
更灵活的参数:可以省略不用的参数props 结构更一致
支持 VNode 的标准化
更好的 TypeScript 支持
Vue 2:
h('div', { class: 'foo', on: { click: handler } }, 'hello')
Vue 3:
h('div', { class: 'foo', onClick: handler }, 'hello')
路由系统
26. Vue Router 的导航守卫有哪些?如何使用?
答案:
导航守卫分类:
全局守卫:beforeEach:前置守卫beforeResolve:解析守卫afterEach:后置钩子路由独享守卫:beforeEnter组件内守卫:beforeRouteEnterbeforeRouteUpdatebeforeRouteLeave
使用示例:
router.beforeEach((to, from, next) => {// 必须调用 next()
})// 组件内
beforeRouteLeave(to, from, next) {// 确认离开?next(confirm('确认离开?'))
}
27. 路由懒加载的原理是什么?如何实现?
答案:
路由懒加载是通过动态导入实现的代码分割技术。
原理:
使用动态 import() 语法
Webpack 将其识别为代码分割点
路由被访问时才加载对应 chunk
实现:
const routes = [{path: '/about',component: () => import('./views/About.vue')}
]
Vue 3 还可以使用 defineAsyncComponent:
const About = defineAsyncComponent(() => import('./About.vue'))
28. Vue Router 有几种路由模式?区别是什么?
答案:
两种主要模式:
hash 模式:使用 URL hash (#)兼容性好不需要服务器配置示例:http://example.com/#/about
history 模式:
使用 HTML5 History API
URL 更美观
需要服务器配置(避免 404)
示例:http://example.com/about
配置:
const router = createRouter({history: createWebHashHistory(), // hashhistory: createWebHistory(), // history
})
29. 如何实现动态路由?有哪些应用场景?
答案:
实现方式:
使用冒号定义动态参数:
{ path: ‘/user/:id’, component: User }
通过 props 接收参数:
{ path: ‘/user/:id’, component: User, props: true }
编程式导航:
router.push('/user/' + userId)
应用场景:
用户个人主页
商品详情页
博客文章页
任何需要根据 ID 展示不同内容的页面
30. 如何实现路由鉴权?
答案:
常见鉴权方案:
路由元信息 + 全局守卫:
{path: '/admin',meta: { requiresAuth: true }
}router.beforeEach((to, from, next) => {if (to.matched.some(record => record.meta.requiresAuth)) {if (!auth.isLoggedIn()) {next('/login')} else {next()}} else {next()}
})
动态路由:
根据权限动态生成路由表
使用 router.addRoute() 添加路由
组合式 API (Vue 3):
import { onBeforeRouteUpdate } from 'vue-router'onBeforeRouteUpdate((to, from, next) => {// 鉴权逻辑
})
31. Vuex 的核心概念有哪些?
答案:
Vuex 核心概念:
State:单一状态树
Getters:计算属性
Mutations:同步修改状态(commit)
Actions:异步操作(dispatch)
Modules:模块化
工作流程:
组件 dispatch Action
Action commit Mutation
Mutation 修改 State
State 变化触发组件更新
32. Vuex 和 Pinia 的主要区别是什么?
答案:
Pinia 是 Vuex 的替代方案,主要区别:
API 设计:Vuex 有 mutations/actionsPinia 只有 actions(可同步/异步)
TypeScript 支持:Pinia 有更好的 TS 支持
模块化:Vuex 需要 modulesPinia 自动模块化
体积:Pinia 更轻量
Composition API:Pinia 专为 Vue 3 设计
开发体验:Pinia 更简洁直观
33. 什么情况下应该使用 Vuex/Pinia?
答案:
使用场景:
多个组件共享状态
多个组件需要修改同一状态
需要维护复杂的状态逻辑
需要状态的时间旅行调试
需要服务端渲染的状态保持
不适合场景:
简单应用(可以用 provide/inject)
父子组件通信(用 props/emit)
简单全局状态(可以用 reactive)
34. 如何实现 Vuex 的模块热重载?
答案:
Vuex 模块热重载配置:
if (module.hot) {module.hot.accept(['./modules/moduleA'], () => {const newModuleA = require('./modules/moduleA').defaultstore.hotUpdate({modules: {moduleA: newModuleA}})})
}
Pinia 自动支持热更新:
if (import.meta.hot) {import.meta.hot.accept(acceptHMRUpdate(useStore, import.meta.hot))
}
35. Vuex 的严格模式是什么?有什么作用?
答案:
严格模式会深度监测状态变更是否来自 mutation。
启用:
const store = new Vuex.Store({strict: true
})
作用:
确保所有状态变更都经过 mutation
防止在 mutation 外修改状态
开发时更好的调试体验
注意:
不要在生产环境使用(性能影响)
可以用发布时去除:strict: process.env.NODE_ENV !== 'production'
组合式 API
36. 什么是组合式 API?解决了什么问题?
答案:
组合式 API 是 Vue 3 引入的新代码组织方式,包括:
setup 函数
ref 和 reactive
生命周期钩子函数
自定义组合函数
解决的问题:
选项式 API 在复杂组件中逻辑分散
更好的逻辑复用
更好的 TypeScript 支持
更灵活的逻辑组织
更小的函数粒度
37. setup 函数是什么?如何使用?
答案:
setup 是组合式 API 的入口函数:
在 beforeCreate 之前调用
没有 this
接收 props 和 context 参数
返回对象暴露给模板
示例:
setup(props, context) {const count = ref(0)function increment() {count.value++}return {count,increment}
}
context 包含:
attrs
slots
emit
expose
38. ref 和 reactive 的区别是什么?
答案:
ref:
用于基本类型(也可以用于对象)
通过 .value 访问
模板中自动解包
更适合独立的基本值
reactive:
用于对象
直接访问属性
解构会失去响应性
更适合复杂对象
转换:
reactive({ count: 1 }) 类似 ref(1).value
ref(obj) 类似 reactive(obj)
39. 什么是自定义 hook?如何实现?
答案:
自定义 hook 是使用组合式 API 封装的逻辑复用函数。
实现:
// useCounter.js
import { ref } from 'vue'export function useCounter(initialValue = 0) {const count = ref(initialValue)function increment() {count.value++}return {count,increment}
}
// 使用
import { useCounter } from './useCounter'setup() {const { count, increment } = useCounter()return { count, increment }
}
特点:
以 use 前缀命名
可以组合其他 hook
返回响应式状态和方法
40. watch 和 watchEffect 的区别是什么?
答案:
watch:
明确指定侦听源
惰性执行(默认)
可以获取旧值
更精确控制触发时机
watchEffect:
自动收集依赖
立即执行
没有旧值
适合副作用清理
示例:
// watch
watch(count, (newVal, oldVal) => {})// watchEffect
watchEffect(() => {console.log(count.value)
})
清理副作用:
watchEffect((onInvalidate) => {const timer = setInterval(() => {})onInvalidate(() => clearInterval(timer))
})
41. 什么是 Teleport?使用场景是什么?
答案:
Teleport 是 Vue 3 内置组件,可以将内容渲染到 DOM 树的指定位置。
使用:
<teleport to="body"><div class="modal">...</div>
</teleport>
场景:
模态框
通知提示
加载条
任何需要突破父组件 DOM 层级限制的 UI
注意:
to 可以是 CSS 选择器或 DOM 元素
内容仍保持组件上下文(如数据、指令)
42. 什么是 Suspense?如何使用?
答案:
Suspense 是 Vue 3 内置组件,用于处理异步组件加载状态。
使用:
<Suspense><template #default><AsyncComponent /></template><template #fallback><div>Loading...</div></template>
</Suspense>
工作原理:
等待异步组件加载
显示 fallback 内容
加载完成后显示默认内容
注意:
实验性功能,API 可能变化
可以配合 async setup 使用
43. 什么是渲染函数 JSX?如何在 Vue 中使用 JSX?
答案:
JSX 是 JavaScript 的语法扩展,可以在 JavaScript 中编写类似 HTML 的结构。
Vue 中使用:
安装插件:
npm install @vue/babel-plugin-jsx -D
配置 babel:
plugins: [“@vue/babel-plugin-jsx”]
使用:
render() {return <div class="container">{this.message}</div>
}
Vue 3 中:
import { defineComponent } from 'vue'const Component = defineComponent({setup() {return () => <div>JSX in Vue 3</div>}
})
44. Vue 3 的 Fragment 是什么?有什么好处?
答案:
Fragment 允许组件有多个根节点。
Vue 2 中:
组件必须有且只有一个根元素
Vue 3 中:
好处:
更灵活的模板结构
减少不必要的包装 div
更符合语义化
与 React 保持一致
45. 什么是自定义渲染器?使用场景是什么?
答案:
自定义渲染器 API 允许自定义 Vue 的渲染逻辑。
使用场景:
非 DOM 渲染:小程序CanvasWebGL终端输出
特殊 DOM 操作需求
创建特定领域的框架
示例:
import { createRenderer } from 'vue'const { render, createApp } = createRenderer({patchProp,insert,remove,createElement// ...其他平台特定API
})
46. Vue 应用常见的性能优化手段有哪些?
答案:
常见优化:
代码层面:合理使用 v-if 和 v-show为 v-for 设置 key避免同时使用 v-if 和 v-for使用 computed 缓存计算拆分复杂组件使用 keep-alive 缓存组件打包层面:路由懒加载按需引入组件库代码分割压缩代码Tree-shaking运行时:减少响应式数据量避免不必要的组件渲染防抖/节流虚拟滚动 (virtual scroller)其他:SSRCDN服务端缓存
47. 什么是 keep-alive?如何使用?
答案:
keep-alive 是 Vue 内置组件,用于缓存不活动的组件实例。
使用:
<keep-alive><component :is="currentComponent"></component>
</keep-alive>
属性:
include:匹配的组件名会被缓存
exclude:匹配的组件名不会被缓存
max:最多缓存组件实例数
生命周期:
activated:组件激活时
deactivated:组件停用时
48. Vue 3 的 v-memo 是什么?如何使用?
答案:
v-memo 是 Vue 3.2+ 新增指令,用于缓存模板子树。
使用:
<div v-for="item in list" :key="item.id" v-memo="[item.id === selected]">{{ item.text }}
</div>
工作原理:
依赖项不变时跳过更新
必须与 v-for 一起使用
可以显著提升性能
场景:
大型列表
复杂条件渲染
频繁更新的 UI
49. 如何分析 Vue 应用的性能瓶颈?
答案:
分析方法:
Vue Devtools:性能时间线组件渲染时间事件追踪Chrome DevTools:Performance 面板Memory 面板Coverage 面板特定工具:webpack-bundle-analyzerspeed-measure-webpack-pluginLighthouse代码层面:检查不必要的重新渲染检查大型响应式对象检查内存泄漏
50. 如何实现虚拟滚动?有什么好处?
答案:
虚拟滚动只渲染可见区域的列表项。
实现方式:
使用第三方库:vue-virtual-scrollervue-virtual-scroll-grid手动实现:计算可见区域动态渲染可见项处理滚动事件
好处:
减少 DOM 数量
提高渲染性能
支持超大型列表
更流畅的滚动体验
51. Vue 组件的单元测试应该测试哪些方面?
答案:
测试重点:
渲染输出:是否正确渲染条件渲染逻辑列表渲染用户交互:点击等事件表单输入自定义事件状态变更:props 变化数据变化计算属性生命周期:挂载/卸载更新异步行为:API 调用定时器
52. 常用的 Vue 测试工具和库有哪些?
答案:
常用工具:
测试运行器:JestVitest测试工具:Vue Test Utils (官方)Testing Library辅助工具:@vue/test-utilsvue-jestjest-transform-stub其他:Cypress (E2E)Storybook (可视化测试)
53. 如何测试 Vue 组件中的异步逻辑?
答案:
测试异步逻辑方法:
使用 async/await:
test('async test', async () => {await wrapper.find('button').trigger('click')expect(wrapper.text()).toContain('Updated')
})
模拟定时器:
jest.useFakeTimers()
// 触发定时器
jest.runAllTimers()
模拟 API 调用:
jest.mock('axios')
axios.get.mockResolvedValue({ data: {} })
flush-promises:
await flushPromises()
54. 什么是快照测试?在 Vue 中如何实现?
答案:
快照测试是比较组件渲染结果与保存的快照是否一致。
实现:
test('component snapshot', () => {const wrapper = mount(MyComponent)expect(wrapper.html()).toMatchSnapshot()
})
特点:
第一次测试生成快照文件
后续测试比较结果
快照需提交到版本控制
适合静态组件
注意:
快照不是替代品,需结合其他测试
动态内容可能导致失败
定期更新快照
55. 如何测试 Vuex store?
答案:
测试 Vuex store 方法:
单元测试 mutations:
test('increment mutation', () => {const state = { count: 0 }mutations.increment(state)expect(state.count).toBe(1)
})
测试 getters:
test('evenOrOdd getter', () => {const state = { count: 1 }expect(getters.evenOrOdd(state)).toBe('odd')
})
测试 actions:
test('increment action', async () => {const commit = jest.fn()await actions.increment({ commit })expect(commit).toHaveBeenCalledWith('increment')
})
集成测试:
const store = createStore(options)
store.dispatch('action')
expect(store.state).toEqual(...)
56. 什么是 SSR?Vue 中如何实现 SSR?
答案:
SSR (Server-Side Rendering) 是在服务器端生成 HTML 发送到客户端。
Vue 实现方式:
使用 Nuxt.js 框架
手动配置:vue-server-rendererExpress/Koa 服务器Webpack 配置
优点:
更好的 SEO
更快的内容到达时间
对低端设备更友好
缺点:
开发复杂度高
服务器负载大
部分库需要特殊处理
- SSR 和 CSR 的区别是什么?
答案:
SSR (Server-Side Rendering):
在服务器生成完整 HTML
客户端"激活"交互
首屏加载快
SEO 友好
服务器压力大
CSR (Client-Side Rendering):
服务器返回空 HTML 和 JS
客户端渲染所有内容
首屏加载慢
SEO 不友好
服务器压力小
混合方案:
预渲染
部分 SSR
渐进式激活
- Vue SSR 的性能优化手段有哪些?
答案:
SSR 优化手段:
代码层面:避免单例状态使用 bundleRenderer组件级别缓存减少序列化数据基础设施:使用 microcache负载均衡CDN流式渲染其他:延迟加载非关键组件预取数据服务端压缩
59. 什么是 hydration?SSR 中如何处理?
答案:
hydration 是客户端 Vue 接管服务器渲染的静态 HTML 并使其交互的过程。
处理要点:
客户端和服务器输出必须匹配避免 hydration 不匹配警告特殊处理客户端特有代码:if (process.client) {// 只在客户端执行的代码
}使用 <ClientOnly> 组件包装客户端特有内容注意生命周期钩子调用时机
60. 如何处理 SSR 中的身份认证?
答案:
SSR 认证处理:
Cookie 认证:服务器自动发送 cookie适合传统 sessionToken 认证:服务器通过 initialState 传递 token客户端存储 token双重验证:服务器检查 session客户端检查 token
注意:
避免在服务器上使用 localStorage
处理异步用户状态
统一客户端和服务端状态
61. Vue CLI 和 Vite 的区别是什么?
答案:
Vue CLI:
基于 Webpack
功能全面
配置复杂
启动和热更新较慢
适合复杂项目
Vite:
基于原生 ES 模块
开发服务器极快
配置简单
生产打包使用 Rollup
适合现代浏览器项目
选择:
新项目推荐 Vite
已有 Vue CLI 项目可逐步迁移
62. 如何优化 Vue 应用的打包体积?
答案:
优化手段:
代码分割:路由懒加载组件异步加载按需引入:组件库按需导入工具函数按需导入压缩:JS/CSS 压缩图片压缩Gzip/Brotli分析:webpack-bundle-analyzer移除重复依赖其他:使用现代模式 (Vue CLI)外部化大型库
63. 如何实现 Vue 应用的预渲染?
答案:
预渲染是在构建时生成静态 HTML 文件。
实现方式:
使用 prerender-spa-plugin:
new PrerenderSPAPlugin({staticDir: path.join(__dirname, 'dist'),routes: ['/', '/about'],
})
使用 Vue CLI 插件:
vue add prerender-spa
使用 Vite 插件:
import { vitePrerender } from 'vite-plugin-prerender'
适用场景:
营销页面
内容不常变化的页面
改善 SEO
64. 如何部署 Vue 应用到不同的环境?
答案:
部署方案:
静态部署:Nginx/ApacheCDNGitHub Pages服务端渲染:Node.js 服务器Docker 容器Serverless现代部署:VercelNetlifyCloudflare Pages
环境变量管理:
.env 文件
Vue CLI 模式
Vite 环境变量
65. 如何处理 Vue 应用的跨域问题?
答案:
跨域解决方案:
开发环境:配置 devServer.proxy (Vue CLI)devServer: {proxy: {'/api': {target: 'http://localhost:3000',changeOrigin: true}}
}生产环境:后端配置 CORSNginx 反向代理API 网关其他方案:JSONP (仅 GET)后端转发浏览器插件临时解决
进阶概念
66. 什么是高阶组件?如何实现?
答案:
高阶组件 (HOC) 是接收组件并返回新组件的函数。
实现:
function withLoading(WrappedComponent) {return {data() {return { isLoading: true }},mounted() {setTimeout(() => {this.isLoading = false}, 1000)},render(h) {if (this.isLoading) {return h('div', 'Loading...')}return h(WrappedComponent, {props: this.$props})}}
}
使用:
const EnhancedComponent = withLoading(MyComponent)
Vue 3 替代方案:
组合式函数
渲染函数
插槽
67. 什么是递归组件?如何使用?
答案:
递归组件是调用自身的组件。
使用:
通过 name 选项递归:
<template><div><my-component v-if="condition" /></div>
</template><script>
export default {name: 'MyComponent'
}
</script>
通过组件引用递归:
import RecursiveComponent from './RecursiveComponent.vue'
export default {components: {RecursiveComponent}
}
注意:
必须有终止条件
可能影响性能
适合树形结构数据
68. 什么是作用域插槽?使用场景是什么?
答案:
作用域插槽允许子组件向插槽传递数据。
使用:
<slot :item="item" :index="index"></slot>
<template v-slot:default="slotProps">{{ slotProps.item }} - {{ slotProps.index }}
</template>
简写 (Vue 2.6+):
<template #default="{ item, index }">{{ item }} - {{ index }}
</template>
场景:
数据列表组件
表格组件
任何需要灵活内容渲染的组件
69. Vue 3 的 createApp 和 new Vue() 有什么区别?
答案:
区别:
创建方式:Vue 2: new Vue({...})Vue 3: createApp({...})全局 API:Vue 2: 全局修改 Vue 原型Vue 3: 应用实例作用域配置:Vue 2: 全局配置Vue 3: 每个应用独立配置挂载:Vue 2: $mount()Vue 3: mount()
示例:
// Vue 2
new Vue({ el: ‘#app’ })
// Vue 3
createApp(App).mount(‘#app’)
70. 什么是响应式丢失问题?如何解决?
答案:
响应式丢失是指解构或展开响应式对象时失去响应性。
常见场景:
解构 props:
const { foo } = this.props // 失去响应性
展开响应式对象:
return { ...reactiveObj } // 失去响应性
解决方案:
使用 toRefs:
const { foo } = toRefs(props)
保持引用:
return { reactiveObj }
使用 computed:
const foo = computed(() => props.foo)
71. Vue 应用常见的错误有哪些?如何捕获?
答案:
常见错误:
渲染错误
事件处理错误
生命周期钩子错误
异步操作错误
自定义指令错误
捕获方式:
全局错误处理:
app.config.errorHandler = (err, vm, info) => {// 处理错误
}
生命周期钩子:
errorCaptured(err, vm, info) {// 捕获子孙组件错误
}
异步错误:
window.addEventListener('unhandledrejection', e => {})
第三方监控:SentryBugsnag### 72. 如何处理 Vue 路由中的 404 页面?答案:
处理方式:通配符路由:```javascript
{ path: '/:pathMatch(.*)*', component: NotFound }
导航守卫:
router.beforeEach((to, from, next) => {if (!to.matched.length) {next('/404')} else {next()}
})
服务器配置 (history 模式):
location / {try_files $uri $uri/ /index.html;
}
73. Vue 3 的 effectScope 是什么?如何使用?
答案:
effectScope 是 Vue 3.2+ 特性,用于组织和管理 effect。
使用:
import { effectScope, reactive, watch } from 'vue'const scope = effectScope()scope.run(() => {const state = reactive({ count: 0 })watch(() => state.count, console.log)state.count++
})scope.stop() // 停止所有 effect
场景:
组件 setup 中管理 effect
可组合的函数中
测试时清理 effect
74. 如何处理 Vue 中的内存泄漏?
答案:
内存泄漏常见原因:
全局变量
未清除的定时器/事件监听
未卸载的第三方库
闭包
未清理的 Vue 相关资源
解决方案:
组件卸载时清理:
onUnmounted(() => {clearInterval(timer)eventBus.off('event', handler)})
避免意外全局变量使用弱引用 (WeakMap/WeakSet)使用内存分析工具定位
75. Vue 3 的 Suspense 如何处理错误?
答案:
Suspense 错误处理:
使用 onErrorCaptured:
onErrorCaptured((err) => {error.value = errreturn false // 阻止错误继续向上传播
})
错误边界组件:
<Suspense><template #default><AsyncComponent /></template><template #fallback><div v-if="error">{{ error.message }}</div><div v-else>Loading...</div></template>
</Suspense>
全局错误处理:
app.config.errorHandler = (err) => {// 处理 Suspense 错误
}
76. Vue 3 中如何更好地使用 TypeScript?
答案:
最佳实践:
使用 defineComponent:
import { defineComponent } from 'vue'export default defineComponent({// 类型推断
})
类型化 props:
props: {message: {type: String as PropType<string>,required: true}
}
类型化 ref:
const count = ref<number>(0)
类型化事件:
const emit = defineEmits<{(e: 'update', value: string): void
}>()
类型化 computed:
const double = computed<number>(() => count.value * 2)
77. 如何为 Vue 组件定义 TypeScript 类型?
答案:
定义组件类型:
import { DefineComponent } from 'vue'interface Props {title: stringcount?: number
}interface Emits {(e: 'update', value: string): void
}const MyComponent: DefineComponent<Props, {}, {}, {}, {}, {}, {}, Emits> = {props: {title: String,count: Number},emits: ['update'],setup(props, { emit }) {// 类型推断可用}
}
Vue 3.3+ 更简洁:
defineProps<{title: stringcount?: number
}>()defineEmits<{update: [value: string]
}>()
78. 如何为 Vuex/Pinia 添加 TypeScript 支持?
答案:
Vuex 类型支持:
interface State {count: number
}const store = createStore<State>({state: {count: 0},mutations: {increment(state) {state.count++ // 类型推断}}
})
Pinia 类型支持:
export const useStore = defineStore('main', {state: (): State => ({count: 0}),actions: {increment() {this.count++ // 类型推断}}
})
组件中使用:
const store = useStore()
store.count // 类型安全
79. 如何为自定义 hook 添加类型?
答案:
自定义 hook 类型:
import { ref, Ref } from 'vue'interface UseCounterOptions {initialValue?: numberstep?: number
}interface UseCounterReturn {count: Ref<number>increment: () => voiddecrement: () => void
}export function useCounter(options: UseCounterOptions = {}): UseCounterReturn {const count = ref(options.initialValue || 0)function increment() {count.value += options.step || 1}function decrement() {count.value -= options.step || 1}return {count,increment,decrement}
}
80. Vue 3 中如何类型化全局属性和方法?
答案:
类型化全局属性:
// main.ts
app.config.globalProperties.$filters = {formatDate(date: Date): string {return date.toLocaleDateString()}
}// 类型声明
declare module '@vue/runtime-core' {interface ComponentCustomProperties {$filters: {formatDate: (date: Date) => string}}
}// 组件中使用
const { proxy } = getCurrentInstance()
proxy.$filters.formatDate(new Date())
81. 如何实现权限控制系统?
答案:
权限控制实现方案:
路由级别:
{path: '/admin',meta: { requiresAdmin: true }
}router.beforeEach((to) => {if (to.meta.requiresAdmin && !user.isAdmin) {return '/login'}
})
组件级别:
<template><admin-panel v-if="user.isAdmin" />
</template>
指令级别:
Vue.directive('permission', {inserted(el, binding) {if (!checkPermission(binding.value)) {el.parentNode.removeChild(el)}}
})
动态菜单:根据权限生成菜单后端返回可访问路由
82. 如何实现多主题切换功能?
答案:
主题切换实现:
CSS 变量方案:
:root {--primary-color: #42b983;
}.theme-dark {--primary-color: #333;
}function toggleTheme() {document.body.classList.toggle('theme-dark')
}
类名切换:
<div :class="[themeClass]"></div>
动态样式表:
function loadTheme(themeName) {const link = document.createElement('link')link.href = `/themes/${themeName}.css`link.rel = 'stylesheet'document.head.appendChild(link)
}
状态管理:将主题信息存储在 Vuex/Pinia持久化到 localStorage
83. 如何实现全局加载状态?
答案:
全局加载状态实现:
使用状态管理:
// store
state: { isLoading: false },
mutations: {SET_LOADING(state, value) {state.isLoading = value}
}// 组件
<loading-spinner v-if="$store.state.isLoading" />使用 provide/inject:// 根组件
provide('isLoading', ref(false))// 子组件
const isLoading = inject('isLoading')使用事件总线:// 请求拦截器
axios.interceptors.request.use(config => {eventBus.emit('loading', true)return config
})
组合式函数:
export function useLoading() {const isLoading = ref(false)function withLoading(fn) {return async (...args) => {isLoading.value = truetry {await fn(...args)} finally {isLoading.value = false}}}return { isLoading, withLoading }
}
84. 如何实现表单验证系统?
答案:
表单验证方案:
使用 Vuelidate:
import { required, email } from '@vuelidate/validators'export default {setup() {return {v$: useVuelidate()}},data() {return { email: '' }},validations: {email: { required, email }}
}
使用 VeeValidate:
<Form @submit="onSubmit"><Field name="email" rules="required|email" /><ErrorMessage name="email" />
</Form>
自定义验证:
function validate(form) {const errors = {}if (!form.name) errors.name = 'Required'return errors
}
异步验证:
async function checkEmailUnique(email) {const res = await api.checkEmail(email)return res.available
}
85. 如何实现拖拽排序功能?
答案:
拖拽排序实现:
使用 SortableJS:
import Sortable from 'sortablejs'onMounted(() => {Sortable.create(el, {onEnd: (e) => {// 处理排序}})
})
使用 Vue Draggable:
<draggable v-model="list" @end="onDragEnd"><div v-for="item in list" :key="item.id">{{ item.name }}</div>
</draggable>
原生实现:
function handleDragStart(e) {e.dataTransfer.setData('text/plain', e.target.id)
}function handleDrop(e) {const id = e.dataTransfer.getData('text/plain')// 重新排序
}
移动端支持:
使用 touch 事件
使用 Hammer.js 等库
86. 如何设计大型 Vue 应用的目录结构?
答案:
推荐目录结构:
src/
├── assets/ # 静态资源
├── components/ # 公共组件
│ ├── ui/ # 基础UI组件
│ └── … # 其他组件
├── composables/ # 组合式函数
├── stores/ # 状态管理
├── router/ # 路由配置
├── views/ # 页面组件
├── services/ # API服务
├── utils/ # 工具函数
├── styles/ # 全局样式
├── types/ # 类型定义
├── App.vue # 根组件
└── main.ts # 入口文件
模块化方案:
按功能模块划分:src/modules/
├── auth/
│ ├── components/
│ ├── store/
│ └── services/
└── user/├── components/├── store/└── services/按业务领域划分混合方式
87. 如何实现微前端架构中的 Vue 应用?
答案:
Vue 微前端方案:
使用 qiankun:
// 主应用
registerMicroApps([{name: 'vue-app',entry: '//localhost:7101',container: '#subapp',activeRule: '/vue'}
])// 子应用
export async function mount(props) {render(props)
}
使用 Module Federation:
// webpack 配置
new ModuleFederationPlugin({name: 'host',remotes: {app1: 'app1@http://localhost:3001/remoteEntry.js'}
})
其他方案:single-spaiframe
关键点:
样式隔离
状态隔离
路由协调
公共依赖
88. 如何设计可复用的组件库?
答案:
组件库设计要点:
组件设计原则:单一职责可组合明确的接口良好的文档技术实现:使用 Vue CLI/Vite 打包支持按需导入类型定义主题定制文档:Storybook示例代码API 文档发布:npm 包版本管理changelog测试:单元测试可视化测试端到端测试
89. 如何实现 Vue 应用的国际化?
答案:
国际化方案:
使用 vue-i18n:
import { createI18n } from 'vue-i18n'const i18n = createI18n({locale: 'en',messages: {en: { welcome: 'Welcome' },zh: { welcome: '欢迎' }}
})app.use(i18n)
组件中使用:
<p>{{ $t('welcome') }}</p>
动态切换语言:
i18n.global.locale = 'zh'高级功能:懒加载语言包复数处理日期/数字格式化
90. 如何实现 Vue 和原生应用的混合开发?
答案:
混合开发方案:
WebView 嵌入:原生应用内嵌 WebViewVue 应用适配移动端原生桥接:
// Android
window.androidBridge.callNativeMethod()// iOS
window.webkit.messageHandlers.nativeHandler.postMessage()
使用 Capacitor:
import { Plugins } from '@capacitor/core'
const { Camera } = Pluginsasync takePhoto() {const image = await Camera.getPhoto()
}
其他方案:
Cordova
NativeScript-Vue
Weex
91. Vue 3 的编译器优化有哪些?
答案:
Vue 3 编译器优化:
静态节点提升:将静态节点提升到渲染函数外避免重复创建 VNode补丁标志:标记动态节点类型减少 diff 时需要比较的内容区块树:将动态节点按结构划分区块减少需要追踪的动态节点数量缓存事件处理函数:避免不必要的重新渲染更快的 SSR 渲染
92. Vue 3 的静态提升是什么原理?
答案:
静态提升原理:
编译时分析模板:识别完全静态的节点识别只有 class/style 动态的节点提升静态节点:const _hoisted_1 = createVNode("div", null, "static content")function render() {return _hoisted_1
}运行时直接复用:跳过这些节点的 diff/patch大幅提升更新性能
效果:
减少 VNode 创建开销
减少内存占用
提高渲染性能
93. Vue 3 的区块树优化是什么?
答案:
区块树 (Block Tree) 优化:
将模板划分为区块:每个区块包含动态节点静态内容作为整体处理动态节点收集:编译时收集动态节点引用形成扁平数组结构更新时:直接遍历动态节点数组跳过静态内容比较
优势:
减少虚拟 DOM 树遍历
更精确的更新目标
提高 diff 效率
94. Vue 3 的按需编译是什么?如何实现?
答案:
按需编译原理:
基于 ES 模块的 tree-shaking:只打包实际使用的代码移除未使用的功能编译器优化:根据模板实际使用的功能生成代码例如不使用 v-model 就不编译相关代码组合式 API 设计:按需导入功能函数更好的 tree-shaking
实现方式:
使用 Vite/Rollup
配置优化选项:
// vite.config.js
export default {build: {rollupOptions: {treeshake: true}}
}
95. Vue 3 的 CSS 变量注入是什么?如何使用?
答案:
CSS 变量注入允许在 JS 中定义 CSS 变量并在样式中使用。
使用:
<script setup>
import { ref } from 'vue'const color = ref('red')
</script><template><div class="text">Hello</div>
</template><style>
.text {color: v-bind(color);
}
</style>
编译为:
.text {color: var(--xxxxxx);
}
原理:
编译时将 v-bind 转换为 CSS 变量
运行时动态更新变量值
优势:
更好的 JS-CSS 集成
动态主题支持
类型安全
综合问题
96. Vue 3 相比 React 有哪些优势和劣势?
答案:
Vue 3 优势:
更简单的学习曲线
更灵活的模板和 JSX 支持
更精细的响应式系统
更小的运行时体积
更好的性能优化
组合式 API 更灵活
React 优势:
更大的生态系统
更丰富的第三方库
更成熟的移动端方案
更灵活的渲染目标
更早引入的并发特性
选择考虑:
偏好模板语法选 Vue
需要最大灵活性选 React
性能敏感场景 Vue 可能更优
大型团队 React 可能更合适
97. Vue 3 和 Svelte 的主要区别是什么?
答案:
主要区别:
编译策略:Svelte 编译为高效命令式代码Vue 保留运行时响应式:Svelte 使用编译时响应式Vue 使用运行时响应式系统状态管理:Svelte 使用简单的 storeVue 提供 Vuex/Pinia生态系统:Vue 有更成熟的生态系统Svelte 更轻量学习曲线:Svelte 更简单Vue 提供更多高级功能
98. Vue 3 未来的发展方向是什么?
答案:
Vue 未来方向:
更好的 TypeScript 支持
更强大的编译时优化
更小的运行时体积
更好的开发工具体验
更完善的 SSR 支持
更强大的组合式 API
可能的并发渲染支持
更友好的移动端方案
社区趋势:
Vite 成为标配
Pinia 取代 Vuex
组合式 API 成为主流
更多基于编译的优化
99. 如何成为 Vue 高级开发者?
答案:
进阶路径:
深入理解核心原理:响应式系统虚拟 DOM编译器掌握高级特性:自定义渲染器编译器宏源码定制性能优化专家:渲染性能打包优化内存管理架构设计能力:大型应用架构微前端组件库设计社区贡献:参与开源编写插件分享经验
100. 如何设计一个 Vue 技术架构面试题?
答案:
好的 Vue 架构面试题应包含:
技术选型:Vue 2 vs Vue 3状态管理方案构建工具选择应用结构:目录组织代码拆分模块划分性能考虑:加载性能运行时性能内存使用扩展性:新功能添加团队协作长期维护实际场景:给定业务需求特定约束条件权衡取舍