ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Vue 3 高频面试题全解析:响应式原理到组件通信与工程实践

Vue 3 高频面试题全解析:响应式原理到组件通信与工程实践 面试季又到了后台私信里问得最多的就是 Vue 相关的问题。我翻了一下后台记录光“vue面试题”这个词最近一个月就被搜了几百次。想想也正常Vue 在国内前端圈的占有率确实高不管你是面大厂还是中小型公司Vue 都是绕不开的一环。而且这两年 Vue 3 全面铺开面试官问的问题也越来越刁钻从响应式原理到组件通信从路由传参到性能优化几乎每个知识点都能往深了挖。我自己这几年面试别人和被别人面攒了不少 Vue 相关的题目和思路。说实话很多候选人基础 API 背得滚瓜烂熟但一问到底层原理或者“为什么这样设计”就卡壳了。这篇我就把热词里出现的那些高频考点——响应式手写、路由参数、生命周期、v-model 自定义、自定义指令、Vue 和 React 的区别等等——串起来做一个系统梳理每个问题都附上我的理解和回答思路。不管你是准备跳槽还是刚学完 Vue 想查漏补缺这篇文章都值得你花半小时读完。1. 响应式系统深挖从“会用”到“能手写”1.1 面试官为什么总爱问 reactive、ref、effect、computed热词里有一条很有意思“脱离 vue 源码使用原生 proxy 手写一个包含 reactive、ref、effect、computed”。这道题我至少见过五个面试官出过可以说现在是 Vue 3 面试的保留项目了。先说清楚这几个 API 的关系这是回答这类题目的地基。effect是响应式的“地基”它的作用就是把一个函数变成响应式副作用——当函数里读到的响应式数据发生变化时这个函数会重新执行。reactive和ref是创建响应式数据的两种方式reactive接收对象ref可以包装任意类型。computed则是在effect之上封装的一个特殊副作用它的特点是会缓存计算结果只有依赖变化时才重新计算。面试官问这个题表面上是考 API 使用实际上是想看三件事第一你知不知道 Proxy 相比 Object.defineProperty 的优势第二你理不理解依赖收集和触发更新的完整链路第三你能不能把这种“发布订阅”模式用代码讲清楚。我面试别人的时候只要候选人能画出“get 时收集依赖set 时触发更新”这个闭环这道题基本就过了一半。1.2 手写一个精简版响应式系统用原生 Proxy 手写响应式核心就三个部分依赖存储结构、reactive 函数、effect 函数。依赖存储我用一个 WeakMap 来做键是原始对象值是一个 MapMap 的键是对象的属性名值是一个 Set里面存的是依赖这个属性的 effect 函数。之所以用 WeakMap是因为它能被垃圾回收避免对象不使用了还占着内存。// 依赖存储target - key - deps const targetMap new WeakMap() // 当前正在执行的 effect let activeEffect null然后是effect函数。它的职责很简单把传入的函数放进一个“当前正在执行的副作用”的全局变量里然后立即执行一次这个函数执行过程中访问响应式属性时就会触发 get 拦截从而完成依赖收集。function effect(fn) { const effectFn () { activeEffect effectFn fn() activeEffect null } effectFn() return effectFn }接着是track和trigger这两个内部函数。track在 get 拦截器里调用负责把activeEffect收集到对应属性的依赖集合中。trigger在 set 拦截器里调用负责取出依赖集合逐个执行。function track(target, key) { if (!activeEffect) return let depsMap targetMap.get(target) if (!depsMap) { depsMap new Map() targetMap.set(target, depsMap) } let deps depsMap.get(key) if (!deps) { deps new Set() depsMap.set(key, deps) } deps.add(activeEffect) } function trigger(target, key) { const depsMap targetMap.get(target) if (!depsMap) return const deps depsMap.get(key) if (deps) { deps.forEach(fn fn()) } }最后是reactive用 Proxy 拦截 get 和 setfunction reactive(obj) { return new Proxy(obj, { get(target, key, receiver) { const result Reflect.get(target, key, receiver) track(target, key) return result }, set(target, key, value, receiver) { const result Reflect.set(target, key, value, receiver) trigger(target, key) return result } }) }注意这里一定要用Reflect.get和Reflect.set因为它们能正确传递receiver保证在读取器或继承场景下this指向正确。我见过不少候选人直接用target[key]取值这样在嵌套对象和数组场景下容易出问题。ref和computed的实现就顺理成章了。ref本质上就是把{ value: 原始值 }这个对象变成响应式的function ref(value) { return reactive({ value }) }computed则是在effect基础上加缓存function computed(getter) { let value let dirty true const effectFn effect(() { value getter() dirty false }) return { get value() { return value } } }注意这只是一个教学版实现真正的 Vue 源码还处理了数组方法重写、嵌套响应式、调度执行scheduler等复杂场景。但在面试中你能写到这个程度已经能证明你理解响应式核心链路了。1.3 追问环节为什么 Vue 3 用 Proxy 而不用 defineProperty这个问题几乎紧跟手写题后面。回答要点有三个缺一不可。第一Object.defineProperty只能拦截属性的读取和赋值对于新增属性、删除属性这些操作无能为力。Vue 2 里你给data里不存在的属性赋值页面不会更新必须用Vue.set这就是根本原因。第二Object.defineProperty初始化时要递归遍历对象的所有属性对象层级越深初始化开销越大。而 Proxy 是“懒代理”只有在真正访问到某个属性时才会递归代理子对象性能上更有优势。第三就defineProperty只是监听属性而 Proxy 能拦截has、deleteProperty、ownKeys等十多种操作这让 Vue 3 能实现更多高级特性比如effectScope、onTrack、onTrigger这些调试钩子。还有一个小点容易被忽略Proxy 代理的是整个对象所以数组的索引操作、length修改都能被正确拦截不需要像 Vue 2 那样额外重写数组的push、pop等方法。这一点可以在回答时顺带提一下会显得你考虑得比较周全。2. 组件通信与自定义能力高频考点的正确打开方式2.1 生命周期不是背出来而是讲清楚“什么时候用”Vue 生命周期的面试题几乎没有公司不考。但大多数候选人只会背这不够。面试官真正想看的是你知不知道“每个阶段适合做什么事”。Vue 3 的组合式 API 生命周期对应关系是这样的setup是组件创建之前相当于 Vue 2 的beforeCreate和created合并onBeforeMount在挂载前调用onMounted在挂载后调用这时候可以访问 DOM、可以发起请求、可以初始化第三方库onBeforeUnmount在卸载前调用适合清理定时器、取消事件监听、销毁图表实例。我面试的时候特别爱问一个场景题父组件和子组件挂了它们的生命周期执行顺序是什么答案是父组件先执行onBeforeMount然后子组件执行onBeforeMount和onMounted最后父组件再执行onMounted。这个顺序和 DOM 挂载时机有关系子组件必须先把 DOM 渲染好父组件的挂载才算完成。很多候选人没考虑过这个一追问就露馅。另外有个坑要提醒setup在生命周期上确实替代了beforeCreate和created但业务代码里如果你在setup里同步访问useRoute()或useStore()是没问题的但如果想拿this就没了。Vue 3 里this在setup中是undefined这个要特别小心。2.2 自定义 v-model面试官想听“双向绑定”的本质热词里有“vue的自定义v-model”这是组件封装方向的高频题。要答好这道题先得想清楚v-model到底是什么。v-model在 Vue 3 里是modelValue属性和update:modelValue事件的语法糖。也就是说Child v-modelmsg /等价于Child :modelValuemsg update:modelValuemsg $event /。Vue 2 里默认是value属性和input事件Vue 3 改成modelValue是为了语义更明确也支持一个组件上绑定多个v-model。自定义 v-model 的写法我一般推荐这么写script setup const props defineProps({ modelValue: { type: String, default: } }) const emit defineEmits([update:modelValue]) function handleInput(event) { emit(update:modelValue, event.target.value) } /script template input :valuemodelValue inputhandleInput / /template除了默认的modelValueVue 3 还支持带参数的v-model:title这种写法。比如封装一个表单组件你可以这样Child v-model:titletitle v-model:contentcontent /这样父组件可以同时绑定多个值子组件用title和content分别声明 props配对的update:title、update:content事件。这个特性在做复杂表单组件时非常实用面试时主动提出来会显得你确实做过实际项目。2.3 自定义指令知道钩子还不够得理解使用场景自定义指令的考点核心是mounted、updated、unmounted这三个钩子。因为指令本质上是“直接操作 DOM”的补充手段所以凡是能用模板和组件解决的都不建议用指令。但有些场景还是绕不开的比如自动聚焦、点击外部关闭、按钮权限控制、水印、拖拽、懒加载。我写过比较多的一个指令是权限控制比如v-permission。思路是在mounted钩子里读一下当前用户的权限列表没有权限就把这个 DOM 元素移除const permission { mounted(el, binding) { const required binding.value const hasPermission checkPermission(required) if (!hasPermission) { el.parentNode el.parentNode.removeChild(el) } } }还有一个容易忽略的点如果指令用到了组件内部的数据或者函数mounted时可以通过binding.instance访问组件实例。比如某个指令需要调用组件里的一个方法可以直接通过这个属性拿到。这个细节很多人不知道提出来面试官会眼前一亮。2.4 组件通信全家桶把场景和解法配对组件通信也是必考题常见的方案有 props/emit、v-model、provide/inject、ref 获取组件实例、事件总线Vue 3 已不推荐、Pinia 状态管理、插槽传数据。我建议面试时不要只列方案而是按场景去答。父子通信用 props 和 emit这是最基础也最常用的跨层级比较深的时候用provide和inject但要注意provide是“非响应式”的如果需响应式得传入ref或reactive对象兄弟节点或者复杂状态直接用 Pinia别硬用事件总线事件总线在 Vue 3 里需要额外引入第三方库而且出了 bug 很难排查。我见过一个坑就是provide传了普通对象子组件改了它父组件里却没有更新。这个问题在面试中也经常被问到回答时你要说清楚provide传出的属性默认不是响应式的要传ref或reactive包装后的值。这是 Vue 3 和 Vue 2 行为不同的地方不少人踩过坑。3. 路由与工程化实战面试里的“应用级”考察3.1 路由参数query、params 和动态路由的区别“vue路由参数”和“vue动态路由”在热词里都有。路由传参有两种常见方式query和params。query方式用router.push({ path: /detail, query: { id: 1 } })URL 里会变成/detail?id1刷新页面参数不会丢适合传可分享的参数。params方式分两种。一种是动态路由path: /user/:id访问/user/123通过route.params.id获取。另一种是命名路由配合paramsrouter.push({ name: user, params: { id: 1 } })这种参数不会体现在 URL 里刷新页面就会丢所以不适合传需要持久化的数据。动态路由还有一个高频面试点组件复用。当用户从/user/1跳转到/user/2时因为渲染的是同一个组件实例Vue 会复用这个组件onMounted不会重新执行但route.params已经变了。解决方案有两个一是用watch监听() route.params.id二是给router-view加上:keyroute.params.id强制组件重新渲染。这两种方案各有适用场景面试时最好能都答出来。3.2 路由守卫常见的三种角色和触发时机路由守卫在项目里用得非常多面试也爱考。Vue Router 4 的守卫主要分三类全局守卫、路由独享守卫、组件内守卫。全局前置守卫beforeEach是用的最多的。登录状态校验、页面标题设置、动态权限路由的生成都适合放在这里。比如常见的需求未登录用户访问需要登录的页面跳转到登录页router.beforeEach((to, from) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { return { path: /login, query: { redirect: to.fullPath } } } })注意 Vue Router 4 的守卫回调里返回对象就表示跳转到对应路由返回false表示取消当前导航返回true或什么都不返回表示放行。Vue Router 3 里需要通过next()函数来放行或拦截这是两者最大的区别。面试时能说出来这一点说明你真的用过 Vue Router 4。组件内守卫onBeforeRouteLeave和onBeforeRouteUpdate也是常考点。前者适合做“表单未保存离开前确认”的拦截后者适合处理同一个组件内不同参数切换的场景。3.3 DevTools 调试与常见工具链热词里有“vue devtools插件下载”这个虽然不算高深问题但确实是新人容易卡住的点。DevTools 调试 Vue 应用的核心优势有两点一是组件树可视化能直接看到组件的 props、data、computed 的实际值二是 Pinia/Vuex 面板能直接查看和修改状态甚至 time-travel 调试。我提一个实用技巧如果你在开发环境装了 DevTools 但面板不显示先别急着重装检查一下是不是用了生产环境的构建版本。vue包在production模式下会禁用 DevTools 钩子只有开发模式才会注入。还有一个常见原因是浏览器版本太老DevTools 插件要求 Chrome 比较新的版本这个也容易被忽略。顺带说一下热词里的“vue打debug”。Vue 3 应用调试除了浏览器 DevTools还有两个技巧。一个是onRenderTracked和onRenderTriggered这两个调试钩子能告诉你“组件为什么重新渲染了”“是哪个响应式数据触发的”。另一个是在main.js里开启Vue.config.devtools true但这个只对 Vue 2 有效Vue 3 默认就是开启的。搞清楚这些排查 re-render 性能问题会快很多。3.4 性能优化必答题列表、懒加载和虚拟滚动Vue 项目上线之前性能优化是逃不掉的面试也必问。我整理几个最常考的点。第一列表渲染一定要加key。而且key不要用数组索引要用稳定且唯一的 id。原因是 Vue 的 diff 算法会通过key判断节点是否需要复用用索引当key在列表顺序变化时会导致状态错乱。第二路由懒加载。把路由组件改成component: () import(/views/xxx.vue)能让首屏只加载需要的代码块。配合 Webpack 或 Vite 的代码分割首屏体积能小不少。第三组件级别的defineAsyncComponent按需加载。弹窗、详情页这种不是首屏必须的组件可以异步加载减少主 bundle 体积。第四大数据列表用虚拟滚动。vue-virtual-scroller或tanstack/vue-virtual这类库只渲染可视区域的元素几万条数据也不会卡。还有一个容易被忽略的点响应式数据的“精度控制”。有些数据根本不需要响应式比如常量、不会变化的配置对象用ref包起来反而增加了代理开销。可以用markRaw或者shallowRef来跳过代理这在性能敏感的场景里很有用。4. 框架对比与生态扩展拉开差距的加分项4.1 Vue 和 React 的区别别背八股要讲设计理念“vue和react的区别”和“react与vue的区别 fiber的作用”都在热词里。这个问题几乎是前端面试必考但大多数候选人答得又长又乱。我的建议是抓住几个核心维度讲清楚背后的设计差异。第一响应式模型不同。Vue 用的是细粒度的响应式依赖追踪数据变化时能精确知道哪些组件需要更新更新力度是“组件级”。React 默认是无脑从根组件开始 re-render靠虚拟 DOM diff 和以后的并发特性来优化。这也是为什么 React 需要memo、useMemo、useCallback来手动控制渲染而 Vue 不太需要。第二模板 vs JSX。Vue 的模板是静态分析友好的编译器能在编译期做大量优化比如静态节点提升、Patch Flags 标记动态属性。React 的 JSX 更灵活但在编译期能做的优化有限很多优化要靠运行时去判断。这也是为什么 Vue 性能分析和优化很多时候是“编译器帮你做了”。第三React 的 Fiber 架构是它最大的特色之一。Fiber 把渲染拆成一个个可中断的单元配合调度器实现并发渲染让 React 能优先处理用户交互不被大列表渲染阻塞。Vue 3 没有引入类似的架构因为它的响应式系统本身就足够细粒度不太需要“中断渲染来响应交互”这个能力。这不是谁好谁坏的问题而是设计路径不同。我面试候选人时如果能把第三点讲明白说明他确实研究过框架本质而不是只背 API会非常加分。4.2 Vue 3 新特性Composition API 和新的内置组件Vue 3 的核心新特性也是必考内容。Composition API不用说了setup、ref、reactive、computed、watch、provide/inject。但面试官更想听的可能是你为什么要用 Composition API这个问题建议从“代码复用”和“逻辑组织”两个角度去答。选项式 API 的mixins虽然有复用能力但来源不清晰多个 mixins 混在一起很难追踪某个属性来自哪个 mixin也容易命名冲突。Composition API把同一种逻辑的代码放在一起通过自定义 hooks 实现复用来源清晰类型推导也更好。script setup是 Vue 3.2 引入的语法糖它让 Composition API 用起来更简洁顶层的 import 和变量在模板里直接用不需要return。这已经是目前 Vue 3 项目的主流写法了面试时说不会写script setup基本等于说你还停留在 Vue 2。Vue 3 还新增了三个内置组件Teleport、Suspense、Transition的增强。Teleport特别常用比如模弹窗、组件挂载到 body 下避免被父组件的overflow: hidden裁剪或被z-index压住。Suspense则用来处理异步组件的加载状态配合异步 setup 很顺手。4.3 项目和生态从 Spring Boot 到音视频播放的实际场景热词里有一批和实际项目相关的比如“springboot vue前后端分离”、“ruoyi vue pro”、“springboot vue 社区”、“vue播放m3u8”、webrtc vue使用、基于springboot vue的项目。这反映了一件事Vue 相关的面试题已经不只是纯前端了还会结合你简历里的项目问。前后端分离是目前的主流架构。Vue 开发服务器走vite或webpack-dev-server接口通过/api代理到后端 Spring Boot生产环境则由 Nginx 统一托管前端静态文件并把/api转发到 Java 服务。面试中常见的问题是“跨域怎么解决”我来梳理一下几种方案开发环境Vite 配置server.proxyWebpack 配置devServer.proxy。原理是浏览器不直接请求后端而是请求同源的开发服务器由开发服务器转发自然就不存在跨域了。生产环境Nginx 配置location /api { proxy_pass http://java-server; }。原理一样浏览器访问 Nginx 同源地址Nginx 转发到后端。如果后端接口域名和前端不同且不方便代理后端配置 CORS 响应头。音视频播放也是一个常见的功能点。热词里的“vue播放m3u8”指的就是 HLS 直播/视频流。HTML 原生video不支持 m3u8 格式需要引入hls.js来转封装后播放。简单用法是import Hls from hls.js if (Hls.isSupported()) { const hls new Hls() hls.loadSource(videoUrl) hls.attachMedia(videoElement) }WebRTC在 Vue 里的使用主要是实时音视频通信比如视频会议。一般会封装成一个useRTC的 hook管理getUserMedia、RTCPeerConnection、信令服务器通信这些逻辑。面试问到这类项目重点不在于你用了什么库而在于你清不清楚 WebRTC 的 SDP 交换、ICE 候选这些基本流程。还有“vue 和腾讯地图”、vue 和 minio、idea社区版vue开发这些热词总结起来就是Vue 已经不是单纯的“数据驱动页面”了它已经渗透到了音视频、地图、文件存储、低代码平台等各个方向。面试中遇到这类题目建议从“如何在 Vue 生命周期中正确接入非 Vue 生态的库”这个角度回答也就是在onMounted里初始化实例在onBeforeUnmount里销毁清理避免内存泄漏。4.4 Pinia vs Vuex状态管理怎么答才出彩状态管理在热词里没直接出现但几乎是前端面试的必选项。Vuex 和 Pinia 的区别最精简的回答是Pinia 去掉了mutations用更接近 Composition API 的方式定义状态。没有getters的字符串路径而是直接写函数和 TypeScript 类型推导更友好。我个人推荐面试时这样展开Vuex 的actions通常用来处理异步逻辑然后 commit mutation 修改状态Pinia 则直接把 state 和 action 都放在一个 store 里。对比一下// Vuex 写法 state: () ({ count: 0 }), mutations: { increment(state) { state.count } }, actions: { async fetchAndIncrement({ commit }) { await fetchSomething() commit(increment) } } // Pinia 写法 export const useCounterStore defineStore(counter, { state: () ({ count: 0 }), actions: { async fetchAndIncrement() { await fetchSomething() this.count } } })说实话Pinia 对 TypeScript 的支持比 Vuex 好太多这也是 Vue 生态往 TS 方向发展的必然趋势。面试时如果能提一句“Vuex 4 虽然也能在 Vue 3 中用但语法有割裂感官方生态已转向 Pinia”会显得你对技术趋势有判断力。5. 高频细节知识决定你“过没过”的小考点5.1 虚拟 DOM 与 diff 算法Vue 的更新策略虚拟 DOM 是 Vue 2 面试的必考点Vue 3 其实仍然在问只是角度变了——更多是问“Vue 3 在虚拟 DOM 上做了哪些优化”。Vue 3 的编译优化有几个关键点PatchFlags标记动态属性diff 时只对比带标记的节点静态提升把不会变化的节点直接提出来不参与更新事件缓存把内联事件函数缓存起来避免每次渲染都生成新函数引发子组件更新。这些优化的核心是尽量在编译期确定哪些内容会变运行时就不用做无用功。手写一个简化版 diff 思路可以作为回答的主干同层比较、通过tag和key判断节点是否可复用、可复用则 patch 属性和子节点、不可复用则直接替换。够用面试官也知道你理解 diff 的基本逻辑就行不需要你背源码。5.2 Vue 2 升 Vue 3兼容性和破坏性变更如果你简历里写了 Vue 2 项目经历面试官很可能问你“升级到 Vue 3 过程中遇到过哪些破坏性变更”。这个问题值得认真准备因为答案能体现你的实践经验。常见的破坏性变更包括全局 API 从Vue.xxx改为app.xxxfilters被移除需用computed或方法替代$on、$off、$once被移除因为事件总线不支持了v-model默认属性从value改为modelValuescoped样式中的/deep/改为:deep()v-if和v-for同时使用时Vue 2 中v-for优先生效Vue 3 中v-if优先生效$listeners被合并到$attrs中。这些不用全背挑几个你真正踩过坑的来讲面试官一听就知道你是有实战经验的而不是背题背出来的。5.3 热词里的那些“奇怪”问题怎么答我注意到热词里还有几个比较“冷门”的问题这里也简单说一下。“vue 如何使用 vnodesvnodes 是个组件?”——vnode是虚拟节点不是组件它描述的是真实 DOM 节点在内存中的对象表示。你可以直接return () h(div, hello)返回一个 vnode 渲染函数也可以手写render函数。面试问这个是想看你对虚拟 DOM 概念是否真正理解而不是只知道模板写法。“vue样式”和“vue style 单个页面应用”——这是问 scoped 样式和全局样式的区别。scope是通过给元素加>const userInfo { name: 张三 } provide(userInfo, userInfo)子组件 inject 到这个对象后如果父组件里给userInfo.name赋新值子组件不会有任何感知。因为普通对象不具备响应式代理provide只是传递了引用并没有建立依赖关系。正确的做法是用ref或reactive包装后再 provideconst userInfo ref({ name: 张三 }) provide(userInfo, userInfo)然后子组件这样使用const userInfo inject(userInfo) // 模板里用 userInfo.name就是响应式的注意如果子组件直接修改userInfo.value.name因为底层是一个响应式对象父组件也会感知这同样打破了单向数据流。更好的做法是 provide 一个只读版本比如import { readonly } from vue provide(userInfo, readonly(userInfo))阅读readonly能防止子组件意外修改父组件提供的状态。这个细节在面试中能主动说出来很能体现你对 Vue 响应式体系的理解深度。7.2 生命周期与性能的联动优化Vue 组件首屏加载慢很多时候不是接口慢而是渲染阶段做了太多无用功。举两个实际项目中常见的例子。第一在onMounted里同步执行了耗时计算。onMounted执行时组件已经渲染完了此时如果做重活用户会明显感觉到交互卡顿。解决办法是把非关键计算放到nextTick之后或者直接用setTimeout拆成宏任务必要时配合requestIdleCallback做空闲时计算。第二列表组件里每个单元格都用了大量computed和watch。在数据频繁变化的场景下computed依赖更新会产生连锁计算watch默认是组件更新前触发如果回调里又修改了其他响应式数据极端情况下会引发循环更新。排查这类问题最有效的手段就是我前面提过的onRenderTriggered它能明确告诉你是谁触发了这轮渲染。还有一个容易忽略的细节watch默认在组件实例被销毁后会自定停止但如果你用watch监听了window的resize事件记得在onBeforeUnmount里手动移除监听。类似的还有setInterval、addEventListener这类外部资源在组件销毁前一定要清理不然页面切来切去会累积一堆没用的监听器内存占用和事件冲突都会来找你。7.3 从“会写”到“会设计”组件设计模式的实践思考面试到了终面面试官不会只考 API 了会更关注你的设计能力。Vue 相关的常见设计题有让你设计一个通用弹窗组件、一个表单组件、一个无限滚动列表、一个拖拽组件。我的建议是掌握几个经典设计套路。第一受控与非受控。一个组件如果完全由父组件控制状态就是受控组件比如 props 传入visible如果内部自己管状态就是非受控组件比如modelValue是可选的不传就让内部维护。好的组件设计应该支持这两种模式。第二插槽设计。通用组件切忌把所有功能都通过 props 暴露尽量提供插槽让父组件自定义内容比如表格组件的操作列、弹窗的 footer。第三属性透传。封装原生组件时把没被子组件处理的 attrs 透传到根元素上比如input的placeholder、maxlength这样使用方不需要写一堆冗余 props。举一个实际的例子我封装过一个表单输入组件核心逻辑是这样的——props 接收modelValue和labelemits暴露update:modelValue同时通过$attrs透传placeholder、type、maxlength等原生属性。使用者可以像用原生 input 一样传属性不需要我在组件里预先声明。这类“少即是多”的设计思路就是面试官听到会点头的加分项。7.4 全栈场景下的 Vue 项目实践热词里出现了大量前后端结合的关键词我再补充一下在“vue Spring Boot”这种全栈项目里Vue 层面需要考虑的事。首先是接口层设计。Vue 项目里一般会封装一个request.js统一处理 baseURL、请求拦截器和响应拦截器。请求拦截器里加 token响应拦截器里统一解包数据、处理 401 跳转登录。这个封装不复杂但几乎是必问的“项目亮点”因为它能解决实际工程问题。其次是路由权限控制。常见的做法是前端在路由表里给需要权限的页面打上meta.requiresAuth标记全局前置守卫里判断用户的登录状态和角色权限没有权限就跳 403 或者重定向到登录页。更复杂的方案是用后端返回的权限列表动态生成路由router.addRoute动态注册。动态路由在 rbac 系统里很常见比如 RuoYi Vue 这类框架就支持这种方案。面试被问到权限控制能说出“静态路由 守卫校验”和“动态路由 addRoute”两条路线基本就稳了。最后是构建发布。npm run build生成dist目录交给 Nginx 托管。需要注意publicPath的配置部署在子路径下时如果不改base或publicPath资源会 404。Vite 里配置base: /admin/Nginx 里对应location /admin这样一套下来就不会乱。热词里的“windows发布vue”大概率也是问这个。这块平时在本地开发容易忽略但面到后面聊项目上线就是区分“会写 demo”和“做过真实项目”的分水岭了。写在最后花了不小篇幅把 Vue 面试的知识点串了一遍回到最初那个问题面试题这东西到底是背答案还是学原理我的答案始终是——结合真实项目去理解原理。你可以把这篇里的手写响应式代码抄下来自己跑一遍把 v-model 自定义组件亲手封装一个把路由权限的守卫逻辑在项目里落地。只有真正敲过、调试过、踩过坑面试时你讲出来的东西才会有底气。我自己招人的时候最怕的不是候选人“不懂”而是候选人“不懂装懂”。你说你不会 Fiber完全没问题我们一起聊 VNode 就好但你说你会然后又讲不明白那就比较尴尬了。所以建议大家按照这篇文章的脉络把自己的知识盲区逐个补上重点放在“为什么”上面。最后分享一个小技巧面试前一天把你自己做过的最有亮点的 Vue 项目从头到尾在脑子里过一遍从项目架构、技术选型、遇到的坑、怎么解决的到如果重做一次你会怎么优化能流畅讲清楚这套比刷一百道题都有用。祝这波面试季大家都能拿到满意的 offer。
返回列表