
2023年Vue3面试题八股集合从背题到理解框架设计最近不少朋友在准备前端跳槽问我有没有Vue3面试题的“八股集合”。说实话每年这个时候都会有一波面试季而Vue3相关的问题几乎已经成了前端面试的必考点。有人觉得八股文就是死记硬背但我在实际面试和带团队的过程中感受很深真正的Vue3面试题考察的从来不只是你是否记得API而是你有没有理解框架为什么要这样设计。这篇内容我整理了2023年我实际面试过、也帮候选人复盘过的Vue3高频题从响应式原理、Composition API到底层源码、工程化场景再加一些面试答题技巧和避坑经验。不管你是准备跳槽的进阶开发者还是刚学完Vue2想转向Vue3的新人这篇文章应该能给你一个清晰的复习框架。1. 为什么Vue3面试题成了前端面试的分水岭1.1 Vue3普及度到了什么程度先看一个现实现在打开招聘网站前端岗位的职位描述里如果还写着“熟练掌握Vue2”那大概率是存量项目维护岗但只要写着“熟悉Vue3”或者“了解Composition API”对应的往往是新项目开发或者架构升级岗。这不是说Vue2没有价值了而是从2022年底开始Vue3已经成了新项目的默认选项。从技术演进的角度看Vue3不是简单的版本号升级。它在响应式系统上从Object.defineProperty换成了Proxy在逻辑复用上推出了Composition API在类型推导上对TypeScript做了彻底的重写还有Vite这套开发工具链的配合。这些变化不是孤立的它们共同指向一个核心目标让前端代码在项目变大之后依然可维护。所以面试官问Vue3本质上不是在考你对某个API记不记得住而是在考察你有没有从Vue2时代的思维模式切换过来。比如你问他v-model在Vue3里怎么用如果你只回答了语法变了但说不清它底层是modelValue和update:modelValue这两个属性的语法糖那面试官会认为你只是会写代码没有理解框架。1.2 面试官通过Vue3问题在考察什么我经常和做技术的朋友聊招聘大家有个共识面试题从来不是为了难住候选人而是为了在有限时间内快速判断一个人的技术深度和思维方式。拿Vue3八股来说面试官通常有三个层次的考察目标。第一层是基础知识的掌握度也就是你知道有哪些API、语法怎么写这一层靠背就能过第二层是原理解析能力比如问你响应式数据为什么要用Proxy而不是defineProperty这需要你真正读过源码或者深入思考过第三层是工程实践能力比如在真实项目中Composition API怎么组织才能避免代码混乱这需要你有实战经验。很多候选人在第二层就卡住了。原因不是不努力而是复习方式不对——只背结论不追原因。举个例子很多人知道Vue3的ref在模板中会自动解包但很少有人能解释为什么在reactive对象中访问ref不需要.value而解构出来就需要。这个看似奇怪的差异背后其实是响应式代理的实现细节。2. Vue3核心八股知识点拆解2.1 响应式原理Proxy与Reflect的搭配逻辑Vue3面试题里最高频的题目毫无疑问是“Vue3的响应式原理是什么”。这道题考察的深度可以从一句话聊到源码级别。先说结论。Vue3的响应式系统基于ES6的Proxy通过拦截对象的get、set、deleteProperty等操作在读取时收集依赖在修改时触发更新。具体实现上有四个核心角色reactive创建响应式代理对象ref将基本类型包装成带value属性的响应式引用effect注册副作用函数而track和trigger分别是依赖收集和派发更新的内部函数。但面试时你不能只说到Proxy比Object.defineProperty强就完了。要真正答好这道题至少要说清楚三个层面的东西。第一为什么要用ProxyObject.defineProperty只能劫持对象已有的属性新增属性和删除属性是监听不到的所以要额外提供Vue.set和Vue.delete这两个API来弥补。而Proxy直接代理整个对象新增、删除、属性是否存在都能拦截不需要补丁API。同时Proxy还能拦截数组的索引访问和length变化这让数组的响应式处理逻辑大大简化。第二为什么在get拦截器里要使用Reflect.get而不是直接target[key]这里面有个隐蔽的坑。如果对象上有继承关系或者属性定义了自己的getter直接用target[key]拿到的this指向是原始对象target而不是代理对象proxy。这会导致子类修改属性时父类收集到的依赖不会被触发。而Reflect.get(target, key, receiver)会把this指向正确地绑定到receiver也就是代理对象保证依赖触发的准确性。第三依赖收集的具体流程是怎样的。当你访问一个响应式对象的属性时get拦截器会调用track函数把当前正在执行的effect通过一个全局变量activeEffect记录添加到这个属性的依赖集合中。当你修改属性时set拦截器会调用trigger函数遍历执行这个属性的所有依赖副作用。整个机制可以类比成一个微型的事件发布订阅系统。注意面试中如果你能顺手提一句“Vue3的响应式是惰性的只有组件渲染时访问过的数据才会建立依赖”这个细节很加分。因为它说明你理解响应式的完整生命周期而不只是看了个大概。2.2 ref与reactive为什么需要两种APIref和reactive的区别是Vue3面试题里另一个超级高频的考点。很多初学者对这两者的使用场景有点混乱实际上它们的概念定位完全不同。reactive只接收对象类型返回的代理对象可以直接访问属性它是基于Proxy实现的。ref则可以包装任意数据类型包括基本类型和对象访问时需要.value。为什么不能用reactive包装基本类型因为Proxy只能代理对象无法拦截基本类型的操作。所以Vue3提供了ref这个API它在内部会创建一个RefImpl类的实例把原始值存在.value属性上然后通过类的getter和setter拦截value的读取和修改。那为什么说ref在处理对象时内部其实还是调用了reactive因为ref构造函数在接收到对象类型的值时会调用reactive去创建代理再把代理挂到.value上。这就是你直接用ref({ count: 1 })也能正常工作而且修改obj.value.count同样触发响应式的原因。面试时还有一道衍生题也值得注意在模板中为什么ref不需要写.value这个问题的答案涉及模板编译原理。Vue3的模板编译器会对ref类型的变量做自动解包处理但对于解构出来的ref变量编译器无法判断它是不是ref所以只有模板顶层访问的ref才默认解包。这属于编译器的“约定优于配置”你不理解这个约定就会在写复杂表达式时遇到奇怪的bug。2.3 Composition API为什么说是Vue3的灵魂如果你去面一个资深前端岗面试官大概率会问“Composition API比Options API好在哪里”。这个问题如果你只回答“代码更清晰、复用更方便”那等于没有回答。面试官想听的是你真正理解这两种组织方式背后的思维转变。Options API的组织方式是“按选项类型划分”data、computed、methods、watch各自放在独立的区域。这种组织方式在小组件里很清晰因为组件逻辑简单选项之间天然连贯。但当一个组件的功能复杂度上来之后同一个业务逻辑的代码会被拆散到不同的选项区域里你需要上下翻屏去拼凑完整逻辑。这就像把一道菜的所有食材按种类分开放买菜时方便做菜时要到处找。Composition API的组织方式是“按逻辑关注点划分”通过setup函数把某一块业务相关的响应式数据、计算属性、监听器、方法都放在一起。这样一来你阅读代码时的视角从“组件有什么选项”变成了“这个组件有哪些业务模块”。更重要的是Composition API把逻辑抽离成可复用的函数变得异常简单一个自定义hook就可以封装一个完整的业务逻辑单元比如useCountDown、useTableList、usePermission。不过这里也要说点实战体会Composition API不是银弹。我见过不少项目用了setup后把几百行代码全部堆在setup里可读性反而比Options API更差。核心原因是没有把逻辑拆分成独立的composable函数。所以在项目里我通常要求团队一个组件setup里超过一屏的内容就要考虑是否拆hook。2.4 响应式系统的两个辅助APIcomputed和watchcomputed和watch这两个API面试出现的频率也非常高而且经常是组合着问。computed在Vue3里有两个特征一定要记住惰性和缓存。惰性指的是只有当你真正访问computed的返回值时它内部的getter才执行缓存指的是只要依赖的响应式数据没有变化多次访问computed返回的都是同一个值不会重复计算。如果面试官让你手写一个computed的核心实现你可以这样回答内部创建一个ComputedRefImpl实例通过一个_dirty标记来表示脏状态初始为true访问时把它改为false当依赖被trigger时把_dirty重新设为true。这个设计本质上就是经典了脏检查机制在响应式系统里用起来非常优雅。watch的核心则是“当我监听的源发生变化时执行回调函数”。它内部需要区分几种不同的监听源一个ref、一个响应式对象、一个getter函数、还是一个数组。监听ref就直接触发监听reactive对象时会隐式地深度监听不需要手动加deep选项监听getter函数时会通过effect的runner去执行getter并比较返回值。这里有一个面试中容易踩的坑watch和watchEffect的区别是什么。watchEffect不需要显式声明依赖它会在执行过程中自动收集访问过的所有响应式数据并在任何依赖变化时重新执行。而watch需要你明确指定要监听的数据源。所以watchEffect适合执行一些副作用watch适合“精确监听某个数据后完成异步请求”这种场景。2.5 生命周期与组件通信的考点梳理Vue3的生命周期相对于Vue2有个明显的变化beforeDestroy和destroyed被重命名为beforeUnmount和unmounted语义更精准。同时Composition API形式的生命周期钩子onMounted、onBeforeUnmount等需要在setup阶段同步调用。面试里经常问的是setup生命周期和选项式生命周期谁先执行答案是setup先执行然后才依次执行beforeMount、mounted等钩子。原因是setup本身就是组件实例创建过程的一部分它里面定义的响应式数据和方法必须在模板编译成render函数之前就准备好。组件通信机制在Vue3里也发生了微妙变化。props和emit依然是最基础的通信方式但v-model变成了modelValue update:modelValue的语法糖这也让父子组件之间多次使用v-model绑定不同字段成为可能。provide/inject从2.x的不可响应变成了响应式可用但要注意如果传入reactive对象则子组件修改这个对象会影响父组件的数据正确的做法是provide一个修改函数。defineExpose和defineProps、defineEmits一起构成了script setup语法下的组件对外接口声明体系。还有一个容易被忽略但是面试常点的通信方式通过ref获取子组件实例。在script setup中子组件默认是关闭的必须用defineExpose显式导出才能被父组件访问到。这个变化对习惯了Vue2的开发者来说是个容易踩的坑。3. 高频手写题与源码级分析3.1 手写一个mini响应式系统面试中“手写一个响应式系统”这题已经成了Vue3岗位面试的保留节目。它考察的不是你能不能完整复刻Vue3的源码而是你有没有理解响应式的核心闭环。一个精简的响应式系统大概50行代码就能写完核心结构如下。let activeEffect null const targetMap new WeakMap() function effect(fn) { const effectFn () { activeEffect effectFn fn() activeEffect null } effectFn() } 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) deps deps.forEach(effectFn effectFn()) } function reactive(target) { return new Proxy(target, { get(target, key, receiver) { const result Reflect.get(target, key, receiver) track(target, key) return result }, set(target, key, value, receiver) { const oldValue target[key] const result Reflect.set(target, key, value, receiver) if (oldValue ! value) { trigger(target, key) } return result } }) }这个实现里很多人会漏掉一个关键设计为什么依赖收集时不直接存fn而要把fn包装成effectFn。原因是后续如果要支持effect的停止、调度器等功能你需要在effect自身标识上做文章。当前这个精简版不加额外的stop逻辑但面试时主动提一句“如果要做effect.stop可以在effectFn上添加一个deps属性来记录反向依赖”这个就显得你考虑得更全面。3.2 手写computed的完整思路computed手写题的要点在于实现惰性和缓存两者兼得。思路如下。function computed(getter) { let dirty true let value const effectFn effect(getter, { lazy: true, scheduler() { dirty true trigger(obj, value) } }) const obj { get value() { if (dirty) { value effectFn() dirty false } track(obj, value) return value } } return obj }这段代码的核心思维是computed内部创建了一个惰性的effect它不会立即求值只有访问.value时才执行。首次访问时dirty为true执行getter计算出值并缓存依赖变化时不会立即重新计算而是通过scheduler把dirty设置为true下次访问时发现dirty为true重新计算。这既保证了缓存又避免了无谓的计算。面试时如果能把这个小逻辑推演一遍基本就能证明你对computed的理解深度。你也可以顺手提到computed还有一个选项get和set当传入一个带set的对象时set会触发依赖的更新这在写v-model绑定时很有用。3.3 nextTick原理与事件循环的关系Vue3的nextTick实现要比Vue2简单一些核心逻辑就是利用Promise.then把回调放到微任务队列中。它的运行机制大概是当响应式数据变化时Vue不会立即更新DOM而是把需要更新的watcher放进一个队列然后通过nextTick异步批量处理。这个队列就是事件循环里的微任务队列。因此当你修改数据后立即访问DOM读到的依然是旧值。面试中常问的问题是nextTick和setTimeout的区别是什么答案是执行时机不同nextTick的回调会在当前同步代码执行完后、DOM更新前执行而setTimeout至少会延迟到宏任务阶段。这也意味着nextTick拿到的DOM一定是更新后的最新状态。如果再往深处聊Vue3内部还有一个调度器scheduler的概念。在组件更新时scheduler会通过控制执行的顺序和去重来避免不必要的重复渲染。理解这一点对后续做性能优化很有帮助。3.4 diff算法与key的作用Vue3的diff算法在绝大多数场景下性能优于Vue2但面试官更关心的往往是你有没有理解为什么需要diff以及key为什么那么重要。diff的核心目的是最小化DOM操作成本。当数据变化触发组件重新渲染时Vue需要对比新旧虚拟DOM树的差异决定哪些节点可以复用哪些节点需要移动、删除或新增。整个diff过程大致是先对首尾节点做预处理然后处理中间乱序的部分最后通过最长递增子序列来最小化节点移动次数。key的作用是给每个节点一个“身份证”。当节点带key时diff算法能精确识别出同一逻辑节点的复用和移动当key缺省时Vue会基于位置进行对比这在列表项顺序变化时会导致节点被错误复用产生状态错乱。面试时如果能提到Vue3的diff采用双端指针和最长递增子序列结合的方案并且能解释这个方案对比Vue2的好处更少的移动次数和更稳定的排序这基本就是满分回答。4. 工程化与项目场景题解析4.1 Vite为什么比Webpack快Vite在Vue3项目里的普及程度已经到了“默认构建工具”的程度所以面试里问Vite也是常事。核心问题是Vite为什么开发时启动快、热更新也快答案是开发模式和生产模式采用了不同的策略。开发模式下Vite基于浏览器原生ES Module不做整体打包而是直接启动一个静态服务器浏览器按需加载源文件。首次启动只需要做预构建用esbuild把部分依赖预编译成ESM启动速度自然快。热更新时Vite只要精确地重新编译变化的那个文件并利用WebSocket通知浏览器做局部替换。而Webpack在开发模式下也要经历完整的模块解析、编译、打包流程项目越大耗时越明显。生产模式下Vite使用的是Rollup进行打包代码拆分、tree-shaking等优化都很成熟。这里要注意Vite的生态兼容性是一个需要考量的点某些低版本插件可能还不支持Vite的插件模型面试时能主动提到这个权衡展示你的实战经经验。4.2 TypeScript在Vue3里的应用Vue3的源码本身是用TypeScript写的因此对TS的支持非常到位。现在很多Vue3项目也都是TS化的。面试中会问的通常是defineProps、defineEmits如何结合TS做类型推导。script setup langts interface Props { id: number title: string status?: active | inactive } const props definePropsProps() const emit defineEmits{ (e: change, value: number): void (e: submit, payload: { id: number; name: string }): void }() /script这里有个面试考点defineProps ()这种写法是纯类型的运行时不会产生任何开销这是“编译时类型推导”与“运行时校验”的区别。如果项目需要在运行时对props做复杂校验你可以选择withDefaults或者自定义校验器但绝大多数场景下纯类型版本已经够了。补充一个小知识点ref函数在TS中的类型推导建议习惯性写泛型。比如const count refnumber(0)。在使用async函数赋值时容易出现类型推导错误显式声明会省去很多排查麻烦。4.3 JSX与模板语法如何选择很多人不知道Vue3是可以写JSX的而且官方提供了专门的插件。这样在复杂渲染逻辑的项目里就有了另一种选择。使用JSX的场景通常是渲染逻辑特别复杂、需要大量条件分支嵌套、或者需要更灵活地处理函数式组件。模板语法则在绝大多数场景下更直观并且编译器可以做静态优化比如静态节点提升、缓存事件处理器等。面试里如果问你JSX和模板怎么选建议回答不要二选一而是说根据场景组合使用默认用模板遇到模板太臃肿的细粒度渲染用JSX。这样既展示了你的工具选型能力也不会显得偏离Vue3官方推荐的模板优先路线。4.4 动态路由和后台管理系统场景Vue3后台管理系统的面试题有一个出现率极高的场景根据后端返回的菜单权限动态添加路由。这个场景考察的不只是路由API还有权限模型设计。实现思路通常如下登录成功后前端根据用户角色请求后端获取可访问的路由表结构通过router.addRoute接口逐个添加动态路由并配合路由守卫控制访问权限。在Vue3里这里有几个关键API要知道router.addRoute可以添加单条路由记录也能嵌套添加子路由删除路由有两种方式addRoute返回的回调或者router.removeRoute。这个场景还有一个让人头疼的问题刷新页面时动态路由会丢失因为路由是在登录时加的刷新后内存里的路由表就没了。常规解决方案是把动态路由表保存到pinia或localStorage再在应用启动时重新添加。我实际排查过不少这类问题最常见的一个坑是在addRoute之后直接跳转路由偶尔会报“No match”错误。因为addRoute是异步添加的可能尚未完成匹配。解决方式是使用router.replace({ ...to, replace: true })或者在next()前等待一个nextTick。4.5 富文本、标签页等常见业务中的Vue3实践热搜词里出现了vue-quill-editor vue3说明这个库在迁移到Vue3时坑了不少人。实际情况确实是这样vue-quill-editor原本是Vue2时代的组件它直接依赖Vue2的响应式系统在Vue3里要用就得换包装比如使用vueup/vue-quill或者自己用Quill封装一层。这反映了Vue3迁移过程中一个共性问题第三方组件库可能跟不上框架版本更新的节奏。另一个类似的场景是el-tabs标签页样式定制。Vue3项目里大多数人用的是Element Plus如果遇到样式不生效的情况优先检查是否是scoped样式隔离的问题可以用:deep()穿透子组件样式。这在面试里可以作为一道考察工程经验的小题。5. 面试答题技巧与避坑实录5.1 从“背答案”到“讲原理”的思维切换看到这里你可能会发现前面我几乎没有让你直接背“标准答案”而是反复在强调原理和设计动机。这是因为在实际面试中单纯背答案的回答通常走不远。举一个我亲耳听到的例子。面试官问“Vue3的v-model和Vue2有什么区别”候选人回答“Vue2的v-model是value和input的语法糖Vue3换成了modelValue和update:modelValue。”这个答案本身不能说错但面试官马上追问了一句“那为什么非要换成modelValue”候选人就愣住了。会背答案和会讲原理的差距恰恰体现在这种追问里。更好回答是“Vue2的v-model只针对单个值绑定如果要绑定多个值需要写多个.sync修饰符。Vue3把命名从value扩展为modelValue并且支持组件上多个v-model同时绑定不同属性这样复用了同一个语法也保持了语义一致性。”你看当你能解释“为什么这样设计”时面试官对你的评价会完全不一样。我在准备面试题时有一个习惯每整理一个知识点就逼自己回答三个问题。这是什么它解决了什么问题如果不这样设计有什么替代方案这三个问题一过你对这个知识点的理解深度就会有一个肉眼可见的提升。5.2 面试中回答问题的结构化表达面试的时候回答技术问题的逻辑结构其实很重要。无脑背诵会让面试官觉得你这人只会记东西但散装式的表达也容易让人抓不住重点。我推荐一个四步回答模板。第一步一句话给出结论。比如“Vue3的响应式系统是基于Proxy实现的”。第二步补充核心概念和原理。这时候可以适当展开说清楚Proxy负责什么、Reflect负责什么、依赖收集和派发更新的流程是什么。第三步对比或说明设计动机比如和Object.defineProperty对比讲清楚为什么Proxy是更优解。第四步结合实际场景或注意事项收尾。比如在什么情况下响应式会失效或者性能上要注意什么。这个模板的好处是“由宏观到微观由定义到场景”面试官如果需要你深入某个细节他会顺着你的话继续追问而你已经通过前两步展示了自己的知识体系。这种表达方式在写技术博客和做技术分享时同样适用算是一个通用的表达框架。5.3 高频追问问题速查表我整理了一些面试中常见的追问和对应思路用表格列出来方便复习。追问场景回答思路得分点为什么用Proxy替代defineProperty对象增删属性监听、数组索引监听、性能更好提到Vue.set/Vue.delete的消失ref为什么需要.value而reactive不需要基本类型无法被Proxy代理能解释RefImpl包装类的设计reactive解构后为什么丢失响应式解构会把代理对象的属性赋给普通变量提到toRefs可以解决watch和watchEffect的区别是什么显式声明依赖vs自动收集依赖举例说明适用场景setup中为什么不能使用thissetup执行时组件实例还未创建完毕理解组件创建流程就顺理成章组件v-model多个值如何绑定modelValue之外的命名参数能展示一个自定义组件的写法5.4 容易踩坑的三个回答雷区第一个雷区是只背API不记版本。Vue3的“新特性”一定要说清楚它相对Vue2的变化是什么。有些API表面上看只是换了个名字但背后可能是完全不同的实现逻辑。如果你连版本都不提面试官会怀疑你是在写Vue2项目时“顺手”看了点Vue3文档。第二个雷区是对响应式原理停留在“Proxy监听get和set”这种表面陈述。你需要能说出依赖收集具体怎么做、副作用函数怎么触发、为什么需要Reflect保证this指向正确。这层逻辑如果说不清说明你没有真正深入过源码。第三个雷区是忘记script setup和普通setup的差异。2023年面试时默认情况下你应该使用script setup语法。如果你还在写export default { setup() {...} }这种老写法面试官会认为你的Vue3实战经验不足。实际项目中几乎都会用script setup因为它更简洁且默认支持类型推导。6. 面试备考路线与资源建议6.1 按优先级规划复习顺序很多人面对一堆Vue3面试题时不知道从哪里开始。我给的备考顺序建议是先打牢响应式原理基础再学习Composition API的使用规范然后过渡到Vite和TS工程化最后才是读源码和性能优化。为什么要这样排因为响应式原理是Vue3所有知识点的地基。你理解了reactive、ref、computed、watch这些底层是怎么运作的再去学使用层面的API会感觉很多语法都是“理所当然”的。而工程化则是你展示实战能力的地方面试官能通过你对Vite、TS的熟悉程度判断你是否有真实项目经验。源码阅读不要贪多。我建议只精读三个模块就够了响应式系统、组件渲染流程、diff算法。这三个模块覆盖了面试中80%以上的深入追问。6.2 实践出真知的三个小项目建议单纯看文档和面试题记不住也容易忘。我建议每个准备面试的人在复习期间同步做三个小实践。第一个是通过CDN方式快速搭一个Vue3单页面亲手验证ref自动解包的场景和局限性。第二个是写一个包含父子组件传值、多个v-model、provide/inject的示例把这些通信方式过一遍。第三个是手写一个mini响应式系统配合调试工具观察依赖收集和触发的过程。这三个实践都可以在半天内完成但它们带来的理解深度远超过你看十篇源码分析文章。6.3 一份精简的资料清单如果非要精简到三样我会推荐官方文档的进阶部分、Vue3源码的Readme和核心模块代码、再加一份你亲手整理的知识点思维导图。官方文档是答题语言的标准参照里面的语法描述用词相当精准能帮助你规范自己回答时的用词。源码阅读则是最硬核的一手资料不要只看第三方解读给targetMap打几个断点跟一轮执行过程胜过看十遍解析文章。最后自己画一遍知识导图画的过程中你会发现自己哪些地方是“我以为我会了”。最终的一点个人体会Vue3面试这件事说到底还不是一份工作机会的考题它更像是对你前端思维升级的一次检查。我在带实习生的时候常说你背会了100道面试题也不一定能做一个合格的Vue3开发但如果你理解了一个框架为什么这样设计那些API你根本不需要背用到时自然会想起来。这份八股集合整理出来的核心价值是帮你在面试前形成一个完整、自洽的知识体系。复习的时候反复问自己“为什么”然后尝试把答案讲清楚面试的时候不要急着背答案先梳理好回答的层次平时开发时带着面试官的心态去审视自己写的代码——你会发现准备面试本身其实就是一次很好的技术复盘。