拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Vue 面试收官篇:SSR、性能优化落地、30 道高频面试题精讲(附标准答案)

Vue 面试收官篇SSR、性能优化落地、30 道高频面试题精讲附标准答案系列最后一篇。把这 30 道题吃透Vue 面试你可以反向输出。写在前面这是 Vue 面试系列的最终篇第 1 篇核心基础 第 2 篇Composition API 进阶 第 3 篇Router/Pinia/响应式源码。前面三篇是知识输入这一篇是**“面试输出”**SSR 到底解决什么问题Nuxt 3 怎么用测试怎么写很多候选人只会说写过但不会写30 道高频面试题的完整标准答案——基础题、进阶题、源码题、实战题四个梯队适合谁看✅ 面试前一周做最后冲刺的同学第 4 节的 30 道题就是模拟考卷✅ 想了解 SSR / 测试但一直没动手实践的人✅ 想收藏一份 Vue 面试题库随时翻的人阅读建议第 4 节建议合上文章自己先答一遍再对照答案找差距。 目录服务端渲染SSR测试Vue 生态工具最佳实践高频面试题精选30 道标准答案附全系列知识点总索引26. 服务端渲染SSR26.1 先想清楚为什么需要 SSRCSR客户端渲染的问题 浏览器 → 下载 HTML(空壳) → 下载 JS → 执行 JS → 渲染页面 ↑ 这一段用户看到的是白屏 SSR服务端渲染 浏览器 → 下载 HTML(已填充内容) → 立即显示 → 下载 JS → 接管(hydration) ↑ 首屏内容直出收益SEO 友好爬虫拿到完整内容、首屏时间大幅缩短、弱网设备体验好。代价服务器算力开销、开发复杂度上升注意窗口对象、生命周期差异、mounted之前的钩子外的环境差异。判断口诀内容型站点官网、博客、电商需要 SEO → 上 SSR后台管理系统、登录后的应用 → CSR 足够。26.2 Nuxt 3 实战速览// nuxt.config.tsexportdefaultdefineNuxtConfig({ssr:true,// 开 SSRfalse 则变 SPApages:true,// pages/ 目录自动路由components:true,// 组件自动导入modules:[pinia/nuxt],// Pinia 官方模块runtimeConfig:{apiSecret:,// 仅服务端可见public:{apiBase:/api}// 客户端可见}});!-- pages/index.vue —— 约定式路由 服务端取数 -- script setup // useFetchSSR 阶段在服务端请求客户端 hydration 复用结果不重复请求 const { data, pending, error } await useFetch(/api/articles); // SEO useHead({ title: 首页, meta: [{ name: description, content: 页面描述 }] }); // 页面级中间件 指定布局 definePageMeta({ middleware: auth, layout: default }); /script// server/api/articles.ts —— 服务端 APINitro可部署为 Serverless 函数exportdefaultdefineEventHandler(async(event){constquerygetQuery(event);returnawaitgetArticles(query);}); 面试题精选Q1SSR 和 CSR 的区别什么时候用 SSR答决策导向特性SSRCSR首屏服务端直出 HTML快下载 JS 后渲染慢SEO✅ 爬虫拿到完整内容❌ 需要额外预渲染服务器成本高低开发复杂度高生命周期、环境判断低适用内容站、SEO 刚需后台系统、Web 应用什么时候用有 SEO 需求/首屏性能要求高移动端弱网→ SSR 或 SSG纯交互型应用管理后台→ CSR。加分话术还有第三条路SSG静态生成——构建时把页面渲染成静态 HTML没有服务器渲染开销又保留 SEO博客、文档站首选VitePress/Nuxt SSG。选型上我会先看页面是否依赖实时数据 是否需要 SEO这两个维度。Q2SSR 时数据是怎么获取的为什么 hydration 不会重复请求答Nuxt 的useFetch/useAsyncData在服务端渲染阶段就发起请求结果序列化内联进 HTML__NUXT_DATA__脚本客户端 hydration 时读取这份内联数据直接复用不会重新发请求。如果客户端导航到新页面则走正常的客户端请求。 坑点提醒SSR 阶段没有 window/document访问浏览器 API 要放在onMounted里或import.meta.client判断否则服务端报错。27. 测试 面试被问写过测试吗答写过和答写过我是这么组织的是两回事。这节给你能讲出来的代码。27.1 组件测试Vitest Vue Test Utils// components/Counter.spec.tsimport{describe,it,expect}fromvitest;import{mount}fromvue/test-utils;importCounterfrom./Counter.vue;describe(Counter.vue,(){it(正确渲染初始值,(){constwrappermount(Counter,{props:{initialCount:0}});expect(wrapper.text()).toContain(0);});it(点击按钮递增并发出事件,async(){constwrappermount(Counter);awaitwrapper.find(button.increment).trigger(click);// 断言 DOM 更新expect(wrapper.find(.count).text()).toBe(1);// 断言事件 emitted() 拿到所有触发记录 expect(wrapper.emitted(update)![0]).toEqual([1]);});it(渲染插槽内容,(){constwrappermount(Counter,{slots:{default:span自定义内容/span}});expect(wrapper.html()).toContain(自定义内容);});it(mock 子组件专注测当前组件,(){constwrappermount(MyPage,{global:{stubs:{RouterLink:true,HeavyChart:true}}});});}); 三个最常用的断言姿势wrapper.find()查 DOM、trigger(click)触发事件注意要 await、wrapper.emitted()验证事件。27.2 Pinia 测试// stores/counter.spec.tsimport{describe,it,expect,beforeEach}fromvitest;import{setActivePinia,createPinia}frompinia;import{useCounterStore}from./counter;describe(Counter Store,(){beforeEach((){// ⚠️ 每个用例前创建全新 pinia 实例保证用例间不互相污染setActivePinia(createPinia());});it(increment 生效,(){conststoreuseCounterStore();store.increment();expect(store.count).toBe(1);});it(getter 计算正确,(){conststoreuseCounterStore();store.count5;expect(store.doubleCount).toBe(10);});}); 面试题精选Q1Vue 项目的测试策略怎么设计答测试金字塔单元测试最多工具函数、Composables、Store——Vitest毫秒级组件测试关键交互组件的渲染/事件/emit——Vue Test UtilsE2E 测试最少但关键登录、下单等核心链路——Cypress / Playwright加分话术我的实践是Store 和 Composables 必测它们是业务逻辑核心展示组件抽几个交互复杂的测核心链路用 E2E 兜底。测试的价值在于重构时的安全感不是覆盖率数字。Q2mount和shallowMount的区别答mount完整挂载组件包括子组件shallowMount把子组件替换成占位 stub只测当前组件本身。现在 VTU 2.x 推荐用mountglobal.stubs显式声明要 stub 的组件语义更清晰。28. Vue 生态工具 这节不用背知道有什么轮子即可面试时提到是项目经验加分项。28.1 VueUse组合式函数工具库必装import{useMouse,useLocalStorage,useDark,useDebounceFn,useIntersectionObserver,useEventListener}fromvueuse/core;// 自动处理监听和卸载的工具比自己写少踩 80% 的坑const{x,y}useMouse();constthemeuseDark();// 暗黑模式一行搞定constkeywordref();constdebouncedSearchuseDebounceFn(search,300);// 元素进入视口图片懒加载、无限滚动useIntersectionObserver(el,([{isIntersecting}]){if(isIntersecting)loadMore();});// 事件监听自动随组件卸载移除useEventListener(window,resize,handler);28.2 表单验证 VeeValidate Zodscript setup import { useForm, useField } from vee-validate; import { z } from zod; import { toTypedSchema } from vee-validate/zod; const schema toTypedSchema(z.object({ email: z.string().email(请输入有效邮箱), password: z.string().min(6, 密码至少 6 位) })); const { handleSubmit, errors } useForm({ validationSchema: schema }); const { value: email } useField(email); const { value: password } useField(password); const onSubmit handleSubmit((values) { /* values 类型安全 */ }); /script template form submitonSubmit input v-modelemail / span{{ errors.email }}/span button typesubmit提交/button /form /template28.3 其他生态速览UI 库Element Plus中后台、Vant移动端、VuetifyMaterial国际化vue-i18nt(message.hello, { name })CSS 方案UnoCSS / Tailwind原子化、SCSS modules文档VitePressVue 官方文档站就是面试话术组件库选型我会看三点——TS 类型支持、按需引入/Tree-shaking 能力、主题定制体系CSS 变量方案优先方便换肤。29. 最佳实践29.1 项目结构src/ ├── api/ # API 请求层按业务模块拆分 │ └── modules/ ├── components/ │ ├── common/ # 通用组件无业务 │ └── business/ # 业务组件 ├── composables/ # 组合式函数 ├── directives/ # 自定义指令 ├── layouts/ # 布局组件 ├── router/ # 路由 ├── stores/ # Pinia ├── styles/ # 全局样式/主题变量 ├── types/ # TS 类型 └── utils/ # 纯工具函数29.2 组件设计原则被问如何设计组件时按这四点答script setup langts // ① 单一职责一个组件只做一件事做大了就拆 // ② Props类型明确 默认值 校验 interface Props { title: string; type?: primary | secondary; } const props withDefaults(definePropsProps(), { type: primary }); // ③ 事件动词命名参数语义明确 const emit defineEmits{ (e: update, value: string): void; (e: delete, id: number): void; }(); // ④ 插槽提供合理的默认内容使用者不传也能用 /script template div classcard slot nameheaderh3{{ title }}/h3/slot slot默认内容/slot /div /template29.3 错误处理体系// main.ts全局兜底配合 Sentry 等上报app.config.errorHandler(err,instance,info){reportError(err);};// 组件级onErrorCaptured 做局部兜底如表单区错误不拖垮整页onErrorCaptured((err){showToast(该模块出错了);returnfalse;// 阻止继续向上传播});// API 层统一拦截401 跳登录、其他弹 toastrequest.interceptors.response.use(resres.data,err{if(err.response?.status401)router.push(/login);returnPromise.reject(err);});30. 高频面试题精选 使用方式合上文自己先答再对答案。标 ⭐ 的是出现率最高的。第一梯队基础题必对错一道都危险Q1Vue 的核心特性有哪些响应式数据绑定、组件化、虚拟 DOM、指令系统、模板语法、渐进式框架。展开见第 1 篇 §1Q2⭐v-if和v-show的区别v-if条件渲染false 时销毁组件切换开销大v-show仅切 display初始渲染开销大切换快。频繁切换用 v-show条件稳定用 v-if。Q3⭐ Vue 2 和 Vue 3 的主要区别响应式defineProperty→Proxy、Composition API、编译优化Tree-shaking、Fragment、原生 TS。展开见第 1 篇 §3Q4⭐ 为什么v-for要加 keyindex 做 key 有什么问题key 是节点身份标识帮助 diff 复用/移动节点。index 在顺序变化时导致组件状态错乱输入框串位、选中态错位。用唯一 id。第 1 篇 §6第二梯队进阶题决定面试评级Q5⭐ Composition API 解决了什么问题Options API 中同一逻辑代码分散、Mixins 复用有命名冲突和来源不清问题Composition API 按逻辑功能组织Composables 实现干净复用。第 2 篇 §12Q6⭐ Pinia 和 Vuex 的区别去掉 mutationsaction 直接改 state、TS 原生支持、扁平模块化、体积 ~1KB。Pinia 是官方钦定的 Vuex 继任者。第 3 篇 §21Q7⭐watch和watchEffect的区别watch 显式指定源、可拿旧值、懒执行watchEffect 自动追踪、立即执行、无旧值。副作用清理都用 onCleanup 防竞态。第 2 篇 §13Q8组件通信有哪些方式props/emit父子、provide/inject跨层、Pinia全局、mitt任意。加分项按场景选型 说明 Event Bus 在大型项目难追踪的缺陷。第 1 篇 §10第三梯队源码题拉开差距Q9⭐⭐ Vue 3 响应式原理能手写吗Proxy 拦截 track 依赖收集 trigger 派发更新WeakMap 存依赖防泄漏惰性代理提升初始化性能。建议默写第 3 篇 §23.2 的迷你实现。Q10⭐ Vue 的虚拟 DOM 和 Diff 算法数据变化生成新 vnode → 与旧 vnode 比对 → 最小化更新真实 DOM。Vue 3 的优化Patch Flags 标记动态内容只比对动态部分、Block Tree 跳过静态、最长递增子序列优化移动。意义用 JS 计算换 DOM 操作DOM 操作才是性能瓶颈。Q11Vue 3 编译时优化有哪些静态提升hoistStatic、Patch Flags、Block Tree、静态节点字符串化、事件监听缓存——运行时快的原因大头在编译期。Q12为什么 ref 需要 .valueref 用包装对象承载任意类型Proxy 只能代理对象拦截发生在 .value 这一层。模板中自动解包。第 1/3 篇第四梯队实战题体现项目深度Q13⭐ 如何实现前端权限控制两种方案① 路由 meta 全局守卫做拦截② 登录后按角色 addRoute 动态注册路由表里没有更安全配合 404 兜底。实际项目两者结合。第 3 篇 §19Q14⭐ Vue 项目性能优化有哪些四层框架加载层懒加载/按需/图片优化/CDN、运行时层虚拟滚动/防抖节流/shallowRef/markRaw/v-memo、更新层合理 key/组件拆细/computed、体验层keep-alive/骨架屏/SSR。先测量再优化。第 3 篇 §24Q15如何设计一个通用组件/组件库单一职责Props 类型默认值校验事件动词命名插槽给默认内容主题用 CSS 变量Storybook 写文档单测快照构建支持 Tree-shaking 按需引入语义化版本管理。Q16nextTick的原理和使用场景Vue 更新是异步批量的微任务队列数据变了 DOM 不会立即更新。await nextTick()后在回调里拿最新 DOM。场景改数据后需要测量/操作新 DOM。Q17说说 Vue 的 hydration水合SSR 页面拿到的是静态 HTML客户端 JS 加载后需要激活它——对比服务端 HTML 与虚拟 DOM挂上事件监听和响应式让它变成可交互的。hydration 失败常见原因服务端与客户端渲染结果不一致。Q18Vuex/Pinia 的持久化怎么做store.$subscribe订阅变化写 localStorage注意只存可序列化数据、敏感数据不要存、可配 pinia-plugin-persistedstate。Q19海量数据渲染怎么优化虚拟滚动定高用 useVirtualList不定高用动态高度缓存分页/无限滚动加载shallowRef 存数据避开深层代理web worker 做数据处理。Q20封装 axios 你做了哪些事baseURL 环境区分、请求拦截注入 token、响应拦截统一拆 data/错误处理401 跳登录/刷新 token、取消重复请求防抖队列、loading 与错误 toast 全局开关。第 4 篇附录有完整代码参考Q21-30路由守卫完整执行顺序、SSR 与 SSG 选型、keep-alive 原理、Teleport 场景、作用域插槽与无渲染组件、自定义指令场景、跨域与代理配置、v-model组件封装、错误处理体系、组件库按需引入原理——全部覆盖在前三篇对应章节此处不重复展开面试前请按速查表回查。全系列知识点总索引篇目覆盖内容第 1 篇核心基础模板语法、响应式、computed/watch、指令、组件通信、生命周期第 2 篇进阶Composition API、侦听器、异步组件/keep-alive/Teleport、自定义指令、插槽、动画、Provide/Inject第 3 篇硬核Router、Vuex、Pinia、Vue3 新特性、响应式源码手写、性能优化、TypeScript第 4 篇收官本文SSR/Nuxt、测试、生态工具、最佳实践、30 道面试题 最后四篇写完这套 Vue 知识体系总算完整了。从模板语法到响应式源码从组件通信到 SSR——收藏这一篇等于收藏了整个系列。如果这个系列帮到了你点赞 ❤️ 收藏 ⭐是对创作者最大的鼓励评论区可以留下你面试遇到的新题我会持续补充到题库中更新日志见下接下来想看什么系列React 八股文 / 前端工程化 / 算法面试——评论区投票票数高的优先写 附录两个可以直接放进项目的工具封装1. 通用的 loading 组合式函数// composables/useLoading.tsimport{ref}fromvue;exportfunctionuseLoading(initialfalse){constloadingref(initial);asyncfunctionwithLoadingT(fn:()PromiseT):PromiseT{loading.valuetrue;try{returnawaitfn();}finally{loading.valuefalse;// finally 保证异常时也能复位}}return{loading,withLoading};}// 使用const { loading, withLoading } useLoading();// await withLoading(() api.getList())2. 封装好的 axios 实例面试常问的请求层// utils/request.tsimportaxiosfromaxios;constrequestaxios.create({baseURL:import.meta.env.VITE_API_BASE,timeout:10000});request.interceptors.request.use((config){consttokenlocalStorage.getItem(token);if(token)config.headers.AuthorizationBearer${token};returnconfig;});request.interceptors.response.use((response)response.data,(error){if(error.response?.status401){// 清 token 跳登录window.location.href/login;}returnPromise.reject(error);});exportdefaultrequest;
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门