2021前端面试题解析:Vue核心考点、响应式原理与手写实现

发布时间:2026/10/17 1:30:12
2021前端面试题解析:Vue核心考点、响应式原理与手写实现 2021前端面试题和笔试题——Vue每年开春都是前端岗位面试的高峰期Vue在国内的普及率实在太高了不管你去面中小厂还是大厂Vue相关的问题基本躲不开。我整理了2021年我在面试别人和自己被面试过程中经常出现的Vue面试题和笔试题把每一类题背后真正要考察的东西也一并拆开讲清楚。这份内容不是让你死记硬背答案的而是帮你搞明白“考官到底想听到什么”。先说个大实话Vue面试题看似考的是API记忆实际上大多数题目都在验证两件事——你平时写代码的时候有没有想过“Vue帮我做了哪些事”以及“当Vue不帮你做事的时候你知不知道怎么处理”。所以你会发现下面这些题目里越是基础的概念越容易被问到细节。你如果只是会用背几个指令那很容易被问穿。你如果稍微去源码层面看过一眼回答出来的深度和层次就完全不同了。这篇文章适合这几类人准备跳槽的前端开发在校准备找实习的应届生还有带团队需要出题面试的Leader。文章里我会把面试题的分类、高频考点、手写实现、以及我在真实面试中看到的各种回答水平都讲一遍你完全可以把里面的问题当作模拟题来练手。1. 2021年Vue面试题的整体出题思路与考点分布先把GitHub上各种2021前端面试题仓库和几份知名互联网公司的面经拉通看了一遍我发现Vue相关题目虽然面上五花八门但归纳下来其实只有六类生命周期、响应式原理、组件通信、路由和状态管理、模板编译与渲染机制、手写实现题。这六类出现的频率并不平均响应式和组件通信占了接近一半的题量。为什么出题人会这么偏爱这些方向因为这几块直接反映一个人在真实项目里能不能把Vue用好。你在业务里写的组件天天都在和通信打交道页面频繁卡顿大概率就是响应式设计出了问题路由权限控制、状态共享是每个中大型项目都绕不过去的坎。换句话说这些考点不是面试官随手翻文档摘出来的是从日常痛点倒推出来的。从面经数据看2021年还有个明显变化Vue 3已经发布一年多了但市场上大部分公司还在用Vue 2。所以面试题经常出现新旧对比比如Options API和Composition API各自的适用场景、Vue 2的Object.defineProperty和Vue 3的Proxy差异、v-model实现机制的变化等等。你要是能把两代Vue的差异讲清楚哪怕只是中高级岗位的面试也会明显加分。另一类占比不小的题型是“场景选择题”比如给一段模板代码让你指出渲染结果是什么给一个生命周期钩子里的代码问执行顺序或者给出父子组件的多个生命周期日志让你按顺序输出。这类题其实比概念题更难因为它考察的是你脑子里有没有形成完整的执行流程图。我刚才说的这六类里手写实现题是2021年笔试题的重灾区。很多公司笔试根本不跟你聊直接给30分钟让你在在线编辑器里写一个简易版的Vue.use、写一个事件总线、或者实现一个响应式对象。这时候背概念已经没用了平时没练过手写真的写不出来。这篇文章后面的部分我会单独拿一大节来讲手写题的思路和参考实现你照着练一遍心里会踏实很多。另外2021年的面试还有一个趋势越是基础岗位考官越喜欢在简历的项目里挑一个点然后一路追问到Vue的源码细节。比如你说你用Vue做过一个Table组件对方可能随口就问“当你给Table传入一个新数组界面是怎么刷新的中间经历了什么”这个问题如果你能一口气说出依赖收集、派发更新、patch和diff的过程基本就稳了。2. 响应式原理几乎必考的Vue核心考点2.1 Vue 2响应式原理与Object.defineProperty的边界Vue 2的响应式原理是2021年面试中当之无愧的No.1高频题基本没有一场技术面能绕开它。你要回答清楚得从三个层面展开对象属性的劫持方式、依赖收集的过程、派发更新的过程。对象属性的劫持核心就是Object.defineProperty(obj, key, { get, set })。初始化时Vue会递归遍历data里的每个属性通过defineReactive这个函数把每一个key转换成带有getter和setter的访问器属性。getter负责依赖收集setter负责派发更新。这当中要注意Vue对数组的处理是另一套逻辑通过重写数组的7个变更方法来实现侦听push、pop、shift、unshift、splice、sort、reverse。面试官特别爱问的一个坑是“为什么Vue 2无法检测对象新增属性和数组下标赋值”。因为defineProperty是在对象已有属性上做拦截新增属性本身并没有getter和setter所以响应不到。这也是vm.$set存在的根本原因——它做的事情就是如果目标是数组直接调用重写过的splice方法如果目标是对象且属性不存在就手动调用defineReactive把新属性变成响应式的。我在面试别人时经常听到的答案是“因为defineProperty无法监听新增属性所以要用$set。”这个答案虽然对但属于背诵级别。更进一步你最好还能补充一句“对于数组下标赋值比如this.list[0] xxx本质上也是同样的问题——改动这个索引时并没有触发被重写的数组方法所以视图不会更新。”这一下就能显出你真的踩过这个坑。2.2 依赖收集与Watcher的闭环逻辑响应式系统的完整闭环保你串起来其实特别像“发布订阅模式”每个响应式属性都有一个Dep实例每个组件或计算属性、侦听器在渲染时会创建自己的Watcher。当你访问this.xxx时就会触发该属性gettergetter就把当前的Watcher收集到自己的Dep里。当你修改这个属性时setter触发Dep.notify()里面把收集到的所有Watcher重新执行一遍组件重新渲染。这套逻辑里有个重要细节渲染Watcher的入队机制。Vue不会每次notify都同步立刻去更新DOM而是把Watcher放进一个异步队列通过nextTick统一在下一次事件循环中批量执行。这就是为什么你修改了data里的值紧接着读DOM拿到的还是旧值必须要this.$nextTick()回调用才能拿到新DOM。2021年很多笔试题都会出这个场景让你判断输出顺序这时候你只要牢记异步批量更新的队列逻辑答案就不容易错。我建议你在准备这道题时画一条线走一遍流程不要求能徒手写出整段源码但要能闭合地说出触发getter → 依赖收集 → 触发setter → Dep通知 → Watcher入队 → nextTick刷新 → 执行render函数 → 新旧VNode对比更新DOM。如果你面试时把这条链路说完整面试官大概率不会再追问太多底层东西。2.3 Vue 3的Proxy响应式区别Vue 3已经正式发布2021年的面试中问Vue 3和Vue 2响应式区别的题开始明显增多。不少同学在准备的时候很慌其实核心就几条学会了就能稳得住。第一条Vue 3用Proxy替代了Object.defineProperty因此它可以监听整个对象的新增、删除属性不再有$set的问题。它提供的拦截能力包括get、set、has、deleteProperty等多个手柄覆盖的操作类型更全。数组更简单直接拦截索引赋值和长度变化。第二条Vue 3的依赖收集粒度变成了“属性级”而Vue 2是“组件级”。什么意思Vue 2里组件A只要在模板里用到了data中的任意一个属性这个组件就被视为该属性的依赖这个属性变了整个组件重新渲染。Vue 3用effect配合track精确到每个key模板编译后会生成一个精准的依赖关系哪个key变化就只更新对应部分。所以Vue 3应用普遍比Vue 2更省渲染性能。第三条Proxy的目标对象本身不做递归劫持而是采用“惰性响应”只有在读取到某个深层对象时才对其内部进行递归reactive包裹。这解决了Vue 2初始化时对全量data深度递归导致的开销问题大对象数据场景下初始化速度明显更快。回答这道题的加分项是能主动提到toRef和toRefs这些API是基于响应式拆分和结构时的保留响应式关联。实际业务中你用reactive定义一个大对象然后再把它解构出来结构出来的字段会丢失响应性这时候toRefs就是用来救场的。面试官听到这个就会认为你在项目里是真用过Vue 3处理过数据。3. 生命周期与组件通信的高频面试题拆解3.1 父子组件挂载阶段的执行顺序生命周期相关的题目里最经典的一道就是“有父子组件时挂载顺序是什么”。这道题在2021年出现在多份笔试套题里考察形式包括排序题和输出题。答案本身并不复杂父组件beforeCreate→ 父组件created→ 父组件beforeMount→ 子组件beforeCreate→ 子组件created→ 子组件beforeMount→ 子组件mounted→ 父组件mounted。这个顺序背后对应的是Vue的组件渲染流程父组件在beforeMount之后需要生成渲染函数执行在生成其VNode时发现子组件占位符于是递归去创建子组件实例。子组件要完成自己的初始化、编译、挂载全部走完了父组件的挂载才算完成。所以“子先父后”的本质不是哪条硬性规定而是渲染机制的必然结果。同理更新阶段和销毁阶段也有类似规律。销毁时顺序是父beforeDestroy→ 子beforeDestroy→ 子destroyed→ 父destroyed。你在回答的时候如果能顺手说出更新阶段的顺序——父beforeUpdate→ 子beforeUpdate→ 子updated→ 父updated——就显得你平时真的验证过而不是死记的。我自己的一个经验建议是不要在回答里只念顺序最好补一句“这个顺序在设计上保证了子组件能够先完成自身的初始化和销毁父组件不会在子组件就绪前就依赖它的DOM”。这句话既展示了你理解了设计动机也能让面试官觉得你不是在背书。3.2 八个常用通信方式怎么选组件通信题目考得最多的不是“列出所有方式”而是给出一个具体场景让你选合适的方案并说明理由。2021年的面试反馈里这类场景题基本上就是把表格中的方案拿出来做对比面试官喜欢听你说“为什么这个场景不能用bus而得用Vuex”。通信方式的核心选项一共是八种props/emit、ref父拿子实例、$parent/$children新版本里$children已移除、eventBus事件总线、provide/inject、Vuex、$attrs/$listenersVue 2.4以后、slot插槽。其中props/emit是默认推荐的方案适合父子之间的直接通信Vuex适合跨组件、模块级状态共享尤其是需要被多个不相关组件读写的全局状态provide/inject适合深层级的祖先组件向后代传递数据但它不是响应式的除非传入的是响应式对象本身。“eventBus应该怎么用”是被问到频率很高的点。它的实现逻辑其实就三行挂载一个新的Vue实例用$emit发出事件用$on注册监听最后在组件销毁时用$off移除监听。很多项目里用它做非父子组件通信但容易造成事件满天飞、难以定位问题的情况。面试官很爱追问“你项目里用eventBus吗有没有遇到什么坑”此时你最好主动承认这个方案在组件少、通信简单时很轻量但在大型项目中会带来排错困难、命名冲突、内存泄漏忘记$off等问题所以主流的大型项目还是用Pinia/Vuex这类集中式状态管理。关于provide/inject在2021年的面试中属于进阶考点。我建议你记住它的关键缺陷祖先组件提供的值默认不具备响应式。如果你需要一个响应式的值下去必须用computed或reactive把它包裹起来否则后代组件初始化之后就再也不更新了。很多初级开发在封装业务组件时只看到这个API很方便却忽略了响应式这一点导致线上出现神奇的“值变了界面没变”问题。3.3 v-model的原理与自定义组件上的实现v-model的原理这道题属于又基础又容易答漏的。它只是一个语法糖在普通表单元素上它等价于绑定了value属性并监听input事件。在自定义组件上它等价于传入一个名为value的prop同时监听组件$emit(input, value)把新值传回来。为了不让你背得模棱两可这里给一个自定义组件参考实现笔试或手写题里偶尔会让你补全代码template input typetext :valuevalue input$emit(input, $event.target.value) / /template script export default { name: MyInput, props: { value: { type: String, default: } } } /script2021年已经开始出现Vue 3的变体考法在Vue 3里v-model由modelValueprop和update:modelValue事件取代支持v-model:title这样的多参数绑定。这个变化很好记——Vue 3的v-model变得更像sync修饰符的通用化。面试官如果问“Vue 3里怎么实现多个v-model”实际就是让你写多个props 对应emit事件。这里我插一句实战提醒当你在自定义组件上使用v-model时组件的输入框直接改了父组件的值这会带来一个反直觉的现象——父组件的data变化触发子组件重新渲染而你在子组件内部监听到的valueprop变化容易造成“光标跳动”或“输入卡顿”。解决方式是在子组件内部搞一个本地副本用watch同步外部值或者用计算属性的get/set写法。这个问题在面试时主动提出来绝对是个加分项因为它说明你不只会用API还知道边界情况。4. 路由与状态管理面试中的高频场景题4.1 路由守卫的知识点与应用场景Vue Router是面试中的一个独立模块2021年几乎每一轮技术面都会问到路由守卫。它一共分成三类全局守卫beforeEach、beforeResolve、afterEach、路由独享守卫beforeEnter、组件内守卫beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave。面试最爱问的是“在axios拦截器和路由守卫之间二选一如何做登录鉴权”。这个问题本质考的是你会不会用路由守卫做前置拦截。常规实现是全局beforeEach里判断目标路由的meta.requiresAuth是否为真如果是再看本地有没有存token没有就重定向到登录页。代码大概是长这样的router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.matched.some(record record.meta.requiresAuth)) { if (!token) { next({ path: /login, query: { redirect: to.fullPath } }) return } } next() })这个写法本身不复杂但有几个隐藏细节值得展开。第一个是redirect参数很多刚做前端的人不知道登录成功后还要回跳你可以通过this.$route.query.redirect来读回跳地址。第二个是beforeRouteEnter里无法直接拿组件实例this因为此时组件还没创建只能通过to、from、next三个参数操作想要访问this需要在next里传入回调如next(vm { vm.initData() })。第三个是路由重复点击同一段地址时Vue Router会抛出一个NavigationDuplicated错误2021年的笔试题中就有问处理方案的一般是升级到新版路由或在内层catch掉这个promise rejection。从实际项目的经验出发路由守卫的使用边界也需要说明一下它只适合做“页面级别”的权限控制不适合做“按钮级别”的精细权限判断。按钮权限应该交给自定义指令或v-if去判断由状态管理里的权限列表来驱动。你在面试中主动把责任划分说出来会让面试官觉得你经历过体系化的权限设计方案。4.2 Vuex核心概念与模块化实践Vuex方面的高频问题一般围绕五个核心概念展开state、getters、mutations、actions、modules。死记这些名词没有用面试官更希望你能用自己的话解释“Vuex为什么设计成mutations是同步的而actions可以异步”。正确的理解是这样的Vuex要求状态变更必须通过commit mutation来实现而mutation是同步函数这样可以保证每次状态变更都是可追踪、可回溯的。如果mutation里面放了异步逻辑那么你无法确定状态在哪个时间点被修改DevTools的时间旅行就没法精确还原每一步。异步逻辑放在actions里actions内部做完异步请求后最终还是通过 commit mutation 去改 state。模块化设计是Vuex的另一个必考点。当项目足够大时所有状态堆在一个store里会非常臃肿所以我们会业务拆模块比如分成user、cart、order模块。模块内可以开启namespaced: true之后组件里访问模块状态要用this.$store.state[模块名].xxx提交mutation时this.$store.commit(模块名/xxx)action同理。如果开了命名空间别忘了一个细节getters也会被模块前缀隔离使用时为this.$store.getters[模块名/xxx]。在Vuex的辅助函数使用上mapState、mapGetters、mapMutations、mapActions这四个辅助函数能把store中的状态和方法映射到组件的computed和methods中。使用mapMutations映射的方法本质是在组件方法里帮你调用了store.commit所以你在组件的click事件里可以直接写this.increment(payload)。这个写法很常见但在面试中属于“基础不牢说不出为什么实际写字又快又好”的类型所以你在回答时最好加上一句“这些辅助函数只是语法糖核心还是store本身的commit和dispatch机制”。4.3 路由懒加载与组件按需加载与路由密切相关的还有一个很容易被当成“性能题”出出来的知识点路由懒加载。题目一般这么问“你的项目打包出来后首屏加载太慢怎么优化”答案里大概率要出现动态导入。Vue Router 4和Vue Router 3都支持把路由组件写成函数形式{ path: /user, name: User, component: () import(/views/User.vue) }这个() import()会被打包工具识别给每个路由生成独立的chunk文件。用户访问/user时才会请求对应chunk而不是一开始就全部加载下来。与之配合的还有webpackChunkName魔法注释可以给这个chunk取一个可读性更强的名字方便定位文件和做缓存的配合。在Vue组件内部同样支持异步组件的写法2021年Vue 2.6里是Vue.component(AsyncComponent, () import(./AsyncComponent.vue))。如果项目用的是Vue 3则更推荐defineAsyncComponent它可以脱离全局的Vue.component调用直接在单文件组件里声明。这个话题面试官之所以爱问因为它直接关联到性能优化这个所有项目都避不开的话题。回答时如果还能顺着说到“loading组件的替换”和“error组件的替换”那就是标准的深入回答。5. 手写实现题笔试题里分值最高的硬骨头5.1 简易版响应式系统实现2021年的笔试题里出现频率最高的手写题之一是让你实现一个极简的响应式系统。很多公司的要求是给定一个data对象让你用defineProperty或Proxy实现修改值时自动执行依赖它的回调函数。这个题其实难度介于中低级之间但容易暴露你对依赖收集的理解深度。我用最简单的逻辑先实现一版核心就三个角色Dep负责收集和通知observe负责劫持每个属性Watcher负责把回调塞进Dep里。class Dep { constructor() { this.subs [] } depend(sub) { if (sub !this.subs.includes(sub)) { this.subs.push(sub) } } notify() { this.subs.forEach(sub sub.update()) } } function observe(obj) { Object.keys(obj).forEach(key { const dep new Dep() let value obj[key] Object.defineProperty(obj, key, { get() { // 收集依赖 if (window.__currentWatcher) { dep.depend(window.__currentWatcher) } return value }, set(newVal) { if (newVal ! value) { value newVal dep.notify() } } }) }) } class Watcher { constructor(fn) { this.fn fn window.__currentWatcher this this.fn() window.__currentWatcher null } update() { this.fn() } }有的笔试环境里要求使用Proxy版那么你要实现的核心是把observe换成用一个Proxy代理整个对象在get里收集依赖在set里触发更新。Proxy版的优点是不需要用递归去挨个定义属性性能更好缺点是兼容性要求和理解门槛高一点。如果笔试时间里充裕我建议你把两个版本都练熟考场上能根据题目的约束条件选择。除了实现本身2021年有些笔试会追加一个小问“如何在组件销毁时取消Watcher的依赖收集防止内存泄漏”这就是前面提到的$off或者是渲染Watcher的teardown过程。你在实现Watcher时如果顺手设计一个remove方法说明你对Vue源码的Watcher销毁机制是有概念的很容易得到更高的评分。5.2 手写事件总线Event Bus事件总线这道题比较简单但它在一些公司的笔试里被当成“考察原型链和this指向”的载体。要求通常是封装一个简单的EventBus类支持on、emit、off、once四个方法。参考实现class EventBus { constructor() { this.events {} } on(eventName, callback) { if (!this.events[eventName]) { this.events[eventName] [] } this.events[eventName].push(callback) return this } emit(eventName, ...args) { const callbacks this.events[eventName] || [] callbacks.forEach(cb cb(...args)) return this } off(eventName, callback) { const callbacks this.events[eventName] || [] this.events[eventName] callbacks.filter(cb cb ! callback) return this } once(eventName, callback) { const wrapper (...args) { callback(...args) this.off(eventName, wrapper) } this.on(eventName, wrapper) return this } }一个小注意点once不能直接注册原始callback而是要包装一层在执行完原始回调后自动从数组里移除自己。有些同学写了半天once调了好几次事件监听数不减反增就是忘了包装。实际项目里事件总线除了on/emit/off以外还有两个细节值得在答卷中顺带写出来一是为了避免重复注册on的时候可以检查是否存在相同引用二是这只是一个内存中的事件中心要避免把异步任务结果只通过事件总线派发组件销毁时一定要off否则会出现“组件已经销毁但回调还在执行”的诡异问题。5.3 手写diff算法关键步骤有的公司会考到虚拟DOM diff但2021年的笔试题里能让候选人直接写完整diff的极少更多是让你写“key在列表渲染中的作用”或者给出两个新旧数组让你模拟Vue的diff过程。如果你想稳妥过关至少要能写出一版简化版的同一层级diff它包含三项核心策略只做同层比较、通过key复用节点、打补丁更新。我有一个特别简单的口头总结方法diff不是比对整棵树的全部节点而是“逐层比对、同层比较、逐节点打补丁”。核心函数可以抽象成两步第一步判断节点是否相同第二步递归处理子节点function patch(oldVnode, newVnode) { if (oldVnode.key newVnode.key oldVnode.tag newVnode.tag) { // 更新属性 const el (newVnode.el oldVnode.el) // 更新子节点 const oldCh oldVnode.children || [] const newCh newVnode.children || [] if (oldCh.length newCh.length) { updateChildren(el, oldCh, newCh) } else if (newCh.length) { addVnodes(el, newCh) } else if (oldCh.length) { removeVnodes(oldCh) } } else { replaceNode(oldVnode, newVnode) } }如果是笔试题你不需要写出完整的updateChildren但务必说明它的核心优化手段双端指针比较从两边往中间收缩尽量用O(1)复杂度完成旧节点复用。能把key的作用讲明白——同一层级的节点通过key判断是否是同一个节点、是否可复用——很多面试官就会放过你。5.4 手写路由的hash模式与history模式路由模式相关的笔试题也出现过不少要求不高的话就让你写一个简单的hash路由监听。hash模式的核心是window.addEventListener(hashchange, ...)它利用的是URL中#后面的部分变化不会触发页面刷新这一浏览器特性。history模式则是基于HTML5的history.pushState和popstate事件它不依赖hash看起来URL更干净。不过history模式在部署到服务器时需要把所有路由地址都指向index.html否则直接刷新二级路由页面时会404。2021年面试中问这两种模式的差异时能主动说出“history模式需要服务器配置fallback”的人不多你答到了就是加分项。手写一个简易hash路由的骨架并不长很多笔试时写上这么一版就够了class HashRouter { constructor() { this.routes {} window.addEventListener(hashchange, () { this.render() }) } register(path, callback) { this.routes[path] callback } render() { const hash window.location.hash.slice(1) || / const cb this.routes[hash] if (cb) { cb() } else { console.warn(route not found:, hash) } } }这里的注册表、监听器、渲染函数其实对应着Vue Router里的路由表、监听和组件渲染三个核心模块。你在笔试时如果能这样点一下“这对应Vue Router的哪个能力”考官会觉得你能看穿封装的本质而不是只会调API。6. 常见笔试题错题与排查技巧实录6.1 经典易错判断题对象引用赋值我翻看过的2021年Vue笔试题里有一道错题率非常高的选择题大概是这样data里有一个对象obj模板中同时用到了obj.a和obj.b页面加载后你执行this.obj.a 1然后又执行this.obj.b 2问DOM更新了几次很多人的第一反应是“两次因为两个属性都变了”。但Vue 2中每次同步修改属性都会触发Dep.notify()虽然异步更新队列会把多个Watcher合并但如果你在同一轮事件循环里连续修改两个属性它们会进入同一个异步批处理队列最终在下一次nextTick时一起走一遍渲染。所以回答是DOM只会更新一次。这里面展示的知识点是响应式的setter触发和Watcher入队去重机制。类似的分支题还会这样出“同步修改一个属性100次DOM更新几次”答案还是1次。因为Watcher入队时会根据组件id去重。这个机制的代价是你拿到的DOM不是立刻更新后的最新状态必须通过this.$nextTick(() { ... })去读。如果你没有nextTick急着用document.querySelector读取新DOM极大概率读到旧DOM。笔试里这种题其实考的不是记不记得住答案而是你有没有亲自在项目里验证过。6.2 生命周期输出顺序错乱与排查另一个常见考法是给一段代码里面有父子组件、setTimeout、$nextTick让你按顺序输出生命周期钩子和定时器。这类题做错的原因通常是把$nextTick回调的执行时机理解成了“当前任务执行完就立即执行”。实际上$nextTick的回调是在本次微任务队列中执行它排在同步代码之后、DOM更新以后但和setTimeout宏任务之间又有区别。一条清晰的判断规则是同步代码会先执行完之后依次执行微任务Promise回调、nextTick回调微任务清空后再执行宏任务setTimeout、事件回调。所以你写代码跑一遍就能验证先打印created之类同步钩子再打印nextTick回调最后才轮到setTimeout。同样的问题也常出现在Vuex的异步action里你dispatch一个action后面紧接着打印一个state很大概率打印的还是旧值因为action内部可能还有异步请求。排查这类错题的最好方法不是硬记而是在开发环境里用一个最小复现Demo跑一遍并在关键位置打console.log。我每次带新人的时候都强调不要被“背答案”习惯害死Vue的执行时机本来就是一个闭环的调度系统跑一遍print比背十道题都有效。6.3 v-if与v-show的选择问题与优化技巧v-if与v-show的面试题虽然简单但高频出现在2021年的首轮电话面试中。背标准答案是“v-if是真正的条件渲染它确保条件块在切换时销毁和重建组件v-show只是简单地切换CSS的display属性”。但考官真正想听的选型逻辑是如果元素需要频繁显示隐藏用v-show因为v-if每次切换都要走一遍创建/销毁流程开销大如果元素几乎不会切换用v-if因为从一开始就不渲染这个节点节省首次挂载时的DOM节点数。另一个冷门考点是v-if与v-for同时用在同一个元素上的优先级问题。Vue 2中v-for的优先级更高所以循环多次、每次都判断一次v-if性能很差Vue 3中v-if的优先级更高但一旦v-if判断为false循环体根本不会执行。很多2021年面经里都提到这道题因为版本差异能区分一个开发是否真的关心过Vue版本迭代带来的行为变化。实务中的优化建议是永远不要在同一元素上同时使用v-for和v-if要么在计算属性里先过滤完再循环要么在模板外层包一层template v-if再内层循环。在笔试题里如果让写优化前后的代码对比这个思路是稳稳的得分点。6.4 响应式丢失的排查技巧后台项目里最常见的“组件不更新”问题绝大多数都和响应式丢失有关。面试时经常会拿这种场景来考你“表格的数据是从接口拿到的你用res.data直接替换整个数组为什么表格有时候不刷新”这里要分几种情况讨论。如果res.data是全新的数组直接this.tableData res.data在Vue 2和Vue 3中都会正确触发更新。但如果你先this.tableData.length 0再逐项pushVue 2能侦听到length变化吗答案是不能因为length 0不是被重写的变更方法也不会被defineProperty拦截到。正确做法是this.tableData.splice(0, this.tableData.length)或者直接整体替换。另一个高频迷坑是把data里的对象作为prop传给了子组件子组件里改变了它的某个属性父组件界面却没有更新。这通常不是响应式系统坏了而是你改了对象但父组件模板并没有依赖这个属性或者你在父组件里把整个对象改成新对象导致子组件还没来得及响应。这类问题排查的思路很固定先看Vue DevTools中的状态有没有变化再看模板是否确实引用了对应字段最后看拦截器里是不是发生过异步顺序问题。这里面的经验在你面试被追问“你项目里面遇到过组件不刷新的问题吗怎么排查”时可以直接套用讲出来的效果会非常真实。7. 2021年面试题还绕不开的进阶话题Composition API、渲染函数与性能优化7.1 Composition API为什么出现2021年Vue 3相关的面试题比重相比2020年明显上升其中出镜率最高的是对Composition API的整体认知。面试官喜欢问的是有了Options API为什么还要Composition API不是让你背官方文档的“更好的逻辑复用”而是让你结合项目经验说痛点。标准痛点有四条第一Options API的代码是按选项类型组织的比如data放一堆数据、methods放一堆方法、watch放一堆监听当一个组件里同时管理多个业务逻辑时它们会被拆散到不同区块互相跳转非常痛苦。第二逻辑复用只能用mixin而mixin存在属性来源不清晰、命名冲突、隐式依赖的问题Observer无法从代码上直接看出某个mixin里具体干了什么。第三TypeScript支持不友好Options API里this.xxx的推导天然比较弱。第四逻辑组织单元的大小和组件的生命周期绑定在一起很难做到把一段业务逻辑独立提取并测试。Composition API允许你按功能而非选项来组织代码把某个业务的所有相关逻辑写在一起。比如表单提交场景你可以把表单的数据、校验、提交方法、重置方法都放进一个useForm函数里组件中一行调用就行。面试官问到这里时加分点是主动说到setup的执行时机和访问限制setup在beforeCreate之前执行里面无法直接使用this但可以接收props和context两个参数通过context.emit来触发父组件事件。很多人刚开始转Vue 3时总想在里面this.$emit结果报错这个细节就值得你在面试时主动聊聊。7.2 渲染函数与模板编译原理模板编译原理并不是人人都能答上来的初中级题但它属于高级开发面试的经典切入题。2021年一些技术面第二轮喜欢问“Vue的模板是怎么变成真实DOM的”完整的链路是模板字符串 → 解析器parser生成AST → 优化器标记静态节点 → 代码生成器把AST转换成渲染函数字符串 → 执行渲染函数生成VNode → 虚拟DOM patch到真实DOM。笔试中对应的小题是为什么要在编译阶段标记静态节点答案是做静态提升和跳过更新比较减少diff时不必要的遍历。Vue 3里这个优化进一步升级叫“静态树提升”会把静态节点提升到渲染函数外部定义避免每次重新创建VNode对象。如果你能说出patchFlag如TEXT、CLASS、PROPS等动态标记说明你对Vue 3的编译优化有过深入了解这会让你的回答明显区别于一众只会调接口的候选人。在项目里用得比较多的渲染函数知识点是函数式组件和h函数。虽然现在单文件组件模板已经足够应付绝大多数场景但碰到动态渲染多层嵌套结构、或者做自定义渲染器时渲染函数反而更直观。用h函数创建VNode的写法是import { h } from vue export default { setup() { return () h(div, { class: box }, hello) } }很多人看到h函数就不想碰觉得模板够用了。但如果面试官针对“template和render函数各自的使用边界”抛问题你最好能答出模板更适合静态结构和可读性要求高的场景渲染函数更适合高度动态、由数据驱动结构生成的场景。两者在生产环境中可以共存不是替代关系。7.3 Vue 3升级带来的性能优化点有关性能优化的面试题2021年问得最多的三个方向分别是首屏加载、长列表渲染、复杂组件频繁更新。第一个方向我已经在前面路由懒加载里讲过了但还有几个配套的优化手段需要补充组件异步加载、第三方库按需引入、骨架屏、gzip压缩和CDN分流。长列表方向的核心是“虚拟列表”。你可以先回答一个完整思路固定可视区域高度通过滚动事件计算第一条可见数据索引只渲染可视区内的少量节点节点间用绝对定位撑开。2021年有些公司会要求写一个简易虚拟列表组件这种题与其说是考Vue不如说是考你对性能计算的敏感度。关键参数是三个可视高度、item高度、滚动偏移量计算很简单startIndex Math.floor(scrollTop / itemHeight)然后渲染[startIndex, startIndex visibleCount]范围的项目。复杂组件频繁更新方向的经典手段是“函数式组件子组件拆分”。如果你把一个大组件渲染的全部内容都写在一起当其中一个数据变化时整个大组件的render会全部重跑即使其他数据没有变化。优化思路是用computed把依赖的单一数据拆下来或者拆成更小的子组件让变化尽量局部化。Vue 3的shallowRef和markRaw也是这个方向的工具前者只做浅层响应适合大对象后者让某个对象跳过响应式转换比如第三方类实例、常量配置能省下不少代理开销。8. 一份来自一线的复习建议和避坑提醒到了最后我给你一些掏心窝子的复习建议都是我这些年面试别人和帮朋友准备面试时总结出来的。第一不要只刷题一定要动手跑Demo。Vue面试题里起码有三分之一只要你自己写一个最小页面验证过就永远不会答错。比如生命周期顺序、$nextTick时机、defineProperty和Proxy差异这些远比背面经印象深刻。我在带人的时候发现能敲一遍手写响应式系统的人写业务代码时遇到响应式丢失基本瞟一眼就能定位。第二回答源码类问题时不要试图背源码背“机制闭环”就够了。把每个问题的链路画清楚比精准复述某个内部函数的变量名重要得多。比如从“点击按钮修改data”到“DOM刷新”中间涉及事件回调、setter、Dep、Watcher、入队、nextTick、render、patch、diff这一整条链路你把链路讲出来面试官已经能判断出你的水平。第三学会用场景回答问题。面试官问“怎么在Vue里做权限控制”你不要直接甩一段beforeEach而是先说“我们项目分页面权限和按钮权限两层”然后分别展开把Vue Router、Vuex、自定义指令串联起来。这比孤立地背某个API高出一个段位因为面试的本质是看你解决问题的思路不是考你记忆力。第四不要忽略版本差异。Vue 2和Vue 3同时存在的过渡期很多公司线上项目还是Vue 2但面试时喜欢说Vue 3。所以在准备时每一个核心知识点的“Vue 2是什么样Vue 3变成了什么样”都要能对照着说。这不需要你背一整张表只需要记住每个知识点的关键差异点就行比如响应式从defineProperty到Proxy、v-model的prop名从value到modelValue、$children从有到无等等。如果你能把上面这些内容都消化一遍2021年遇到的大部分Vue面试题和笔试题你都具备了拆解的底气。最后分享一个小技巧面试前拿出一张A4纸把Vue的知识点画成一棵树从“初始化”开始分支到响应式、渲染、组件、路由、状态管理每一条分支上写三五个关键词不要写完整句子。考前过三遍这张图比你翻十份面经都有用。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询