简介面向准备前端求职面试的开发者资料包系统覆盖Html、Css、JavaScript、Vue、React、Node、TypeScript、Webpack、算法、网络与安全、浏览器等前端核心方向既适合刚入门的新人建立知识框架也适合有一定项目经验的开发者针对高频考点查漏补缺在冲刺阶段快速提升。包内共28个文件以20份Markdown笔记为主体按专题拆分为精选面试题完整版、框架与语言模块详解、算法与逻辑题、HTTP与架构、移动端等独立篇章读者可按需挑重点复习另有5个XML配置、2个gitignore以及1个工程描述文件保留了原始项目组织方式不影响笔记的阅读与迁移。整个压缩包仅110KB携带方便可直接解压后配合任意Markdown工具使用。目前已有249人学习下载内容并非简单罗列题目而是将高频面试题、易错点与答题思路融合在各专题笔记中尤其针对Vue、React、TypeScript等热门技术准备了较完整的解析能够帮助求职者梳理前端知识体系、提升面试应答的条理性与准确度。1. 前端面试刷题这份题库到底该怎么用才不变成收藏夹里的压缩包有个很典型的画面某开发者把题库刷了两轮面了四家公司最后卡在「Vue3 的 ref 和 reactive 到底怎么选」上。这个场景我最近见了不止一次它指向一个反直觉的结论——前端求职面试的终点不是「背完题」而是把一道题讲成一段有场景、有实现、有代价的话。这份前端面试题资源覆盖 HTML、CSS、JavaScript、Vue、React、Node、TypeScript 七个方向vue3 部分的面试题专项整理尤其适合想系统盘一遍响应式原理的人。它适合两类人一类是简历能写、一开口就露怯的求职者另一类是想在跳槽前把知识盲区补齐的从业者。用法对了它是一份可以直接照着复现的题库用法不对它只是收藏夹里一个再也不会解压的包。2. 把 JavaScript 与 TypeScript 考点拆成能复述的模块闭包、事件循环与类型收窄刷这套题库时我建议不要按文件顺序从头翻到尾而是先按知识点密度排序。JavaScript 的题目决定面试官对你的「下限判断」TypeScript 则是近几年能拉开差距的「上限题」。这一部分我用「概念 → 最小实现 → 追问链」三遍法带你把高频考点过成自己的话。2.1 闭包与作用域链从背定义到讲场景闭包的考察本质不是「函数套函数」的记法而是作用域和执行上下文的运行机制。题库里最常见的一道代码题也是每家面试都大概率出现的题for (var i 0; i 5; i) { setTimeout(function () { console.log(i) }, 1000) }这段代码在浏览器控制台输出的是 5 5 5 5 5不是 0 1 2 3 4。原因是 var 声明的 i 被提升到外层的函数作用域5 个定时器回调共享的是同一个变量绑定循环结束之后 i 已经变成 5回调在 1000ms 之后读取时自然全是 5。setTimeout 的第二个参数是延迟执行的毫秒数这里把延迟改成 0结果也不会变只是把「循环已经结束」这件事提前暴露出来。把 var 换成 let 之后再跑输出就变成 0 1 2 3 4。let 按块级作用域绑定变量for 循环的每一次迭代都会生成一个独立的词法环境回调拿到的 i 是各自那一轮的值。这是两种声明方式最典型的面试差异也是闭包题里最常见的起点。面试官到这里通常会追加三个追问第一如果要求必须保留 var你怎么改出 0 1 2 3 4第二闭包会不会造成内存泄漏第三setTimeout(fn, 0) 是不是立刻执行第一问的常见做法是用 IIFE 把每一轮的 i 作为实参传入并保存成形参for (var i 0; i 5; i) { (function (j) { // j 是当前 i 的快照回调闭包引用的是 j setTimeout(function () { console.log(j) }, 1000) })(i) }IIFE 在每次迭代时都执行一次形参 j 接收当前 i 的副本回调闭包引用的是这个副本所以输出恢复成 0 1 2 3 4。追问第二问时不要只回答「会」或「不会」而是说清楚闭包本身不泄漏泄漏来自被闭包引用的对象生命周期被意外拉长比如在全局缓存里长期持有 DOM 节点却不释放。第三问的回答要和第 2.2 节的事件循环绑定setTimeout 只是把回调放进宏任务队列不是立刻执行。用这份题库刷闭包题时建议每道题都走「解释题干 → 判断输出 → 说出机制 → 给出改法」四步而不是直接看答案。能把这个循环走完面试官问出的变体题基本都在射程内。2.2 事件循环从输出顺序题到 async/await 的执行细节事件循环是 JavaScript 部分区分「背题型选手」和「真懂型选手」的分水岭。题库里的起点通常是这样console.log(start) // 同步语句先执行 setTimeout(function () { console.log(timeout) // 宏任务最后执行 }, 0) Promise.resolve().then(function () { console.log(promise) // 微任务在同步代码之后执行 }) console.log(end) // 同步语句第二个打印正确输出是 start → end → promise → timeout。执行顺序可以这样拆console.log(start) 是同步语句先执行setTimeout 的回调被推进宏任务队列不立即执行Promise.resolve().then 的回调被推进微任务队列console.log(end) 同步执行。同步代码跑完后先清空微任务队列打印 promise然后再从宏任务队列取出 setTimeout 回调打印 timeout。这套规则在浏览器和 Node 环境里都适用只是 Node 在特定阶段还会插入 process.nextTick 和 setImmediate题库中一般会单独说明。真题里更常考的是 async/await 的变体比如下面这段是出现频率很高的组合async function async1() { console.log(async1 start) await async2() console.log(async1 end) } async function async2() { console.log(async2) } console.log(script start) setTimeout(function () { console.log(setTimeout) }, 0) async1() new Promise(function (resolve) { console.log(promise1) resolve() }).then(function () { console.log(promise2) }) console.log(script end)输出顺序是 script start → async1 start → async2 → promise1 → script end → async1 end → promise2 → setTimeout。这里最容易错的两处await async2() 会先同步执行 async2 内部然后把 await 后面的代码放进微任务new Promise 的 executor 是同步执行的所以 promise1 在 Promise 创建时就打印随后注册的 then 回调先进入微任务队列排到 async1 end 后面。按队列顺序推演一遍同步代码执行完 script end 之后微任务队列里已经存在两个任务先进入的是 async1 end后进入的是 promise2所以 async1 end 先打印微任务清空后才轮到宏任务的 setTimeout。把这一步写在纸上胜过刷十道同类题。Promise.then 的回调注册时机也值得注意executor 内部的 resolve() 调用只是把 Promise 状态置为 fulfilled真正的 .then 回调是注册阶段就排队不是 resolve 之后才排队这也是 async/await 与 Promise.then 在输出顺序题里表现一致的原因。2.3 TypeScript 类型题从 any 到类型收窄再到类型体操TypeScript 在面试题里通常考三件事类型收窄、内置工具类型的实现、以及泛型约束。题库里收窄的入门题长这样function formatId(id: number | string) { if (typeof id string) { return id.toUpperCase() } return id.toFixed(2) }typeof 检查在联合类型里把 id 从 number | string 收窄成 string 或 number分支里能安全调用对应方法。这道题看着简单追问点在于 Array.isArray、in 操作符、以及 instanceof 各自适合的场景。另一个更常被考到的是实现内置工具类型几乎每家都会让你手写一个 Pickinterface User { id: number name: string age?: number } // K 被约束为 T 的 key 的联合类型 type MyPickT, K extends keyof T { [P in K]: T[P] } type UserBasic MyPickUser, id | nameMyPick 第一层约束是 K extends keyof T把 K 限定在 T 的属性名范围内P in K 是对联合类型做映射遍历T[P] 是索引访问类型取出原类型里对应属性的类型。这三个语法各自对应一个面试追问keyof 拿到的联合类型怎么用、映射类型和普通对象类型有什么区别、索引访问和接口继承有什么差别。回答时用「从已有类型里派生新类型」来概括比背定义更容易让面试官认可。TypeScript 的题目在这份题库里占的比例不算最大但在求职面试中出现的频率近几年明显上升尤其会和你简历里写的框架版本绑定。比如写了 Vue3 TypeScript 的简历面试官很可能顺手问「ref 是什么类型」「reactive 返回的类型和入参类型是什么关系」。处理这类问题可以把现象落到代码上ref(0) 返回的是 Ref reactive({ count: 0 }) 返回的是 UnwrapNestedRefs{ count: number }后者会把嵌套的 ref 自动解包。这个点放到第 3 章再展开这里只需要建立「类型题是推导出来的不是背出来的」这个认知。3. 从 Vue3 到 React响应式原理、虚拟 DOM 与组合式 API 的追问链框架题库的核心不是「用没用过」而是「理解到什么层」。Vue 和 React 同时出现在一份题库里时最容易踩的坑是只背各自结论不对比着看。这两套框架解决的是同一类问题只是路径不同对比着刷效率会高很多。3.1 Vue3 响应式从 Object.defineProperty 到 Proxy 的演进Vue3 面试题里出场率最高的问题就是「为什么 Vue3 用 Proxy 替换 Object.defineProperty」。建议先能写一个最小实现哪怕只是拦截读写function reactive(target) { return new Proxy(target, { get(obj, key) { // 读取时做依赖收集的占位 console.log(读取, key) return obj[key] }, set(obj, key, value) { // 写入时做触发更新的占位 obj[key] value console.log(写入, key, value) return true // 严格模式下 set 必须返回 true }, }) } const state reactive({ count: 0 }) state.count 1 // 触发 set 钩子这段代码能跑通「属性读写被拦截」的最小闭环。需要强调两个参数Proxy 的 set 钩子必须返回 true否则严格模式下会抛 TypeErrorget 钩子里的 obj[key] 要读原对象而不是代理对象否则会递归触发 get 导致死循环。这里用 console.log 占位在实际框架里会替换成依赖收集和触发更新的函数。追问「为什么 Proxy 能处理新增属性而 defineProperty 不行」用 Vue2 的场景看Object.defineProperty 只能对已存在的属性做 getter/setter新增属性没有被定义过自然不会有拦截数组的 push、pop 这类方法改写的是索引也不是属性赋值所以 Vue2 只能通过重写数组方法或 $set 来补救。Proxy 拦截的是整个对象层面的操作包括 has、deleteProperty、ownKeys所以新增属性、删除属性都在能力范围内。这不是优化问题是能力边界问题。更深一层会问到响应式开销。Proxy 也有代价访问路径每次都要经过 handler且嵌套对象在读取时才惰性代理。所以 Vue3 在框架层配合了标记、缓存和浅响应式shallowReactive来控开销。面试中能把这层说出来就把「背新特性」变成「理解实现约束」差别很明显。题库里还有一个分支考点是 ref 与 reactive 怎么选我的建议是原始类型用 ref、对象用 reactive但要能解释为什么ref 内部其实是一个对象包裹通过类的 getter/setter 实现对 .value 的依赖追踪对象用 ref 会带来额外的 .value 包一层而 reactive 直接作用于对象本身。3.2 虚拟 DOM 与 diff你不需要背源码但要会对比虚拟 DOM 几乎是框架类必问题但面试官真正想听的不是「虚拟 DOM 比真实 DOM 快」而是「虚拟 DOM 保证了什么」。题库里会给一张对比表我建议你也自己整理一张把 Vue3 和 React 的边界差异写清楚维度Vue3React比较粒度组件级配合静态标记组件级fiber 可打断diff 策略同层比较 双端比较同层比较 单向递归静态优化patchFlag 标记动态节点无静态标记靠 memo 等key 作用复用节点最小化移动协调子节点避免重建更新机制依赖触发精确到组件渲染函数重新执行后 diff关键要能答出「diff 是为了把更新代价控制在最小范围」这个目的。真实 DOM 操作当然可以很快但页面状态和 DOM 之间的同步成本会随页面增大快速上升。虚拟 DOM 的做法是先算出最小差异集合再统一交给真实 DOM 操作。key 是高频追问点列表渲染时 key 的作用不是「防止报错」而是帮助 diff 识别节点身份没有 key 时框架按顺序对比插入一个节点可能导致后续节点全部重建有 key 时框架可以复用对应节点只做插入或移动。在 Vue 中用 index 做 key 是个典型反例当列表头部插入一项时index 全部变化diff 会把每个节点当作新节点处理副作用是 DOM 更新异常和状态错位。React 里 key 的协调逻辑和 Vue 有差异但「用稳定 id 不用 index」这个结论是一致的。刷这组题时我一般会在题库里的题目旁标注「这个结论在 Vue 和 React 里是否一致」面试被追问时不会慌。3.3 组合式 API 与 Hooks为什么 React Hooks 不能写在条件里这题属于「懂原理才答得好」的代表。React 的 useEffect、useState 依赖调用顺序和调用次数函数组件每次渲染都会重新执行内部代码Hooks 必须在固定的顺序中被调用写在 if 或循环里会破坏顺序导致状态与上一次渲染对不上。Vue3 的 setup 只在组件实例创建时执行一次组合式 API 的调用是把逻辑函数注册到实例上不依赖每次渲染的调用顺序所以没有这个限制。顺带一个追问Vue 为什么能完成依赖收集而 React 不能Vue 的响应式是在 setup 执行阶段读取数据时收集依赖数据变化触发组件更新React 没有响应式代理每次状态变化都会重新执行整个函数组件体配合 fiber 才能可中断地处理更新任务。这两个框架都取消了「手动找该更新哪里」的成本但实现路径不同。面试官问到这里基本就能判断你是不是真的写过代码。题库里还有一个常见场景题封装一个请求数据的组合式函数。Vue 写法如下import { ref, onMounted } from vue export function useUserList() { // ref 包裹的数据是响应式的 const list ref([]) const loading ref(true) onMounted(async () { // fetch 请求完成后替换响应式数据 const data await fetch(/api/users).then((res) res.json()) list.value data loading.value false }) return { list, loading } }需要说明的是ref 定义的 list 和 loading 是响应式数据组件里解构出来可以直接用onMounted 注册生命周期返回的对象不失去响应性因为 list 和 loading 本身是 ref 对象.value 的读取才会触发依赖收集。React 版写成 useEffect useState语义等价但执行模型不同能对比着说出来就是加分项。4. 浏览器渲染、HTTP 缓存与 Node 中间层拉开差距的非框架题框架题处理得好只能保证面试的「下限」浏览器和网络决定了你在团队里能解决多深的问题。这部分题库通常被低估但面试官特别喜欢拿它来区分「用过框架」和「理解 Web 运行」的人。4.1 浏览器渲染关键渲染路径与重排重绘浏览器面试题容易让人觉得玄学其实可以按「主流程 性能抓手」两条线准备。主流程是HTML 解析成 DOM 树CSS 解析成 CSSOM两者合成渲染树然后经历布局、绘制、合成三步。要能说出「布局计算几何信息绘制生成像素合成交给 GPU」这个分工。重排和重绘的差异是必考点重排是布局阶段重新计算几何位置代价最大常见触发点是改变 width、height、margin、top 等尺寸相关属性重绘只是恢复像素外观代价略小常见触发点是改 color、background。优化结论是优先用 transform 和 opacity 这类合成层属性因为它们跳过布局和绘制。这里有一个高频陷阱叫强制同步布局Forced Synchronous Layout题库里通常会单独列一条const height element.offsetHeight // 读取 element.style.height height 10 px // 写入这段代码单看是「先读后写」但它在一次 JS 任务里同时触发了读和写浏览器会强制把前面的样式的回流结果返回这种读写交错越多性能损耗越大。常见做法是把读和写分开const heights items.map((item) item.offsetHeight) // 批量读取 items.forEach((item, index) { item.style.height heights[index] 10 px // 批量写入 })第一个代码块的 offsetHeight 在写入前读取会强制同步布局第二个块先批量读再批量写浏览器可以合并布局。heights 数组保存的是读取到的旧值用于后续统一写入这样写完之后浏览器只需要做一次布局计算。面试时能说出「读写分离」这个结论比单纯背重排重绘定义有用得多。4.2 HTTP 缓存强缓存与协商缓存的选择逻辑缓存是前端面试必问题考的是你对「headers 优先级」和「失效场景」的理解。题库里一般给这样一张表我建议你也按这张表去记忆缓存类型相关头典型场景强缓存Cache-Control、Expires静态资源、图片协商缓存ETag / If-None-Match接口响应、易变文件协商缓存Last-Modified / If-Modified-Since时间戳监控需要背的优先级Cache-Control 的 max-age 优先于 ExpiresETag 优先于 Last-Modified因为 ETag 是内容指纹比时间更精确。面试官问「为什么有了 Cache-Control 还要 ETag」回答是强缓存失效后需要确认资源是否真的变化ETag 用指纹匹配避免了 Last-Modified 的秒级粒度不够的问题。Node 里设置静态资源缓存头比较直观res.setHeader(Cache-Control, public, max-age31536000)public 表示允许 CDN 和浏览器缓存max-age31536000 秒约等于一年适合文件名带 hash 的静态资源不带 hash 的 HTML 页面通常用 no-cache让它每次都要回源校验但 304 的响应体很小。参数说明里注意区分 no-cache 和 no-storeno-cache 是每次使用前都校验no-store 是完全不缓存后者才适合账号信息等敏感接口。4.3 Node 中间层与工程化前端面试里的 Node 到底考什么Node 在面试里出现的位置一般在两道题一道是工程化工具的原理vite/webpack 都依赖 Node 运行时另一道是 BFF 中间层。对于求职面试能讲清「中间层解决什么问题」比会写表述更重要。中间层的典型价值是聚合接口浏览器直连多个后端服务要么并发数高要么面临跨域和鉴权分散的问题中间层可以把用户信息、订单列表、权益状态聚合成一个接口前端一次请求拿完app.get(/api/user-center, async (req, res) { const token req.headers.authorization // 前端带上来的鉴权凭证 const user await fetchUser(token) // 下游服务调用 const orders await fetchOrders(user.id) const rights await fetchRights(user.id) res.json({ user, orders, rights }) })这段伪代码展示的是 Node 中间层的聚合模式fetchUser 等函数代表下游服务调用authorization 头里的 token 由前端带上中间层用它透传鉴权三个下游请求在 Node 侧是并行发送的浏览器只看到一次网络往返。如果面试官问「为什么不直接让前端发三个请求」答案是并发连接数、鉴权逻辑收敛、以及响应结构统一这三个角度。工程化方向则更常问「vite 为什么比 webpack 快」回答核心是预构建依赖 原生 ESM 按需编译而不是把所有模块一次性打包。5. 前端面试刷题避坑四类现象、四个原因与一套排查顺序刷完题库不是结束真正的坑都在「你以为会了」后面。以下四条是我见过的最高频踩坑记录每条都按现象、原因、解决三步拆开讲。5.1 现象Vue3 响应式说得头头是道追问「Proxy 的代价」就卡住很多人在题库里背熟了「Proxy 比 defineProperty 强」的结论面到「Proxy 有没有性能问题」就停住了。原因是只背了结论没有看实现和约束。解决思路是把响应式题拆成「一句话结论 最小实现 延伸追问」三层每道框架题都按这个结构复述一遍。延伸追问至少准备两个为什么需要 shallowReactive为什么依赖收集要在读取时发生。能答出「Proxy 是懒代理嵌套对象在访问时才代理」这一层基本就过关了。5.2 现象手写事件循环能写对开口讲题却语无伦次有人写输出顺序题能全对但被要求「讲一下事件循环」就只会蹦几个名词。原因是刷输出顺序题时直接看答案练习的是「匹配答案」而不是「推导过程」。解决方法是把每道输出题写成口述稿先给结论再说微任务/宏任务队列规则最后逐行走代码保证每道题都能用一分钟讲完。对着镜子讲或者录音回放都行重点是强迫自己把「输出正确」变成「能解释正确」。5.3 现象项目题被追问就露怯简历里的 axios 封装、权限路由没准备过题库是按知识点组织的和你简历里的代码没有映射。很多人在项目题上翻车不是因为没做过而是因为没提前把做过的东西翻译成面试语言。解决方法是每刷一类题就在项目里找一个对应点准备一个 30 秒的「我在项目里这么用」的案例。比如刷到请求缓存就讲你的 axios 封装里怎么处理 token 刷新和重复请求取消刷到路由守卫就讲你的权限路由怎么控制菜单和页面。题库负责把知识点打碎你负责把它们重新缝回项目里。5.4 现象资料包太大第一天就焦虑到放弃这份题库横跨七个方向打开压缩包看到几十个 Markdown 文件的第一反应是「从哪开始」。原因是把题库当成了一本书试图从头顺序读到尾。解决方法是按优先级分层刷JS 基础 → 框架原理 → 手写题 → 浏览器和网络 → 工程化 → 冷门题。每天只刷两层先把自己最有把握的语言层刷透再进框架层。别指望一次刷完面试前能把前四层过两遍已经足够覆盖绝大多数考察点。注意如果你发现某道题反复刷还是记不住大概率不是记忆力问题而是前置知识缺了。比如事件循环记不住往往是不知道微任务和宏任务的定义先回头补队列模型再回来刷题。6. 把题库变成面试模拟器追问链设计和一分钟讲题训练题库本身是静态的面试是动态的。最后这一步是教你把它变成模拟器核心是两个方法追问链设计和一分钟讲题。追问链设计的做法是每做完一道题不要停在答案上而是顺着它长出两到三层追问并准备回答。举例说明原题第一层追问第二层追问Vue3 ref 与 reactive 区别ref 内部如何实现.value 依赖收集在哪发生事件循环输出题微任务和宏任务谁先async/await 底层是什么虚拟 DOM 是什么key 的作用为什么 index 做 key 有副作用HTTP 强缓存和协商缓存优先级怎么判断ETag 和 Last-Modified 选谁每一层追问都要能用「结论 一行代码 代价」回答。举个例子Vue3 响应式的一分钟讲题模板先给结论——Vue3 用 Proxy 拦截对象读写配合依赖收集实现视图更新再给实现——写一个 reactive 只需要 10 行代码核心是 get 里收集、set 里触发最后说代价——嵌套对象需要惰性代理所以框架层做了分层缓存和浅响应式。这样讲下来面试官听到的不只是一个答案而是一个能展开讨论的结构。第二个方法是一分钟讲题。给自己计 60 秒把一道题讲完前 10 秒给结论中间 40 秒讲实现和验证方式最后 10 秒说局限或边界。这个训练能暴露出两类问题一类是讲得太散抓不住重点另一类是只讲结论说不出实现。这两类问题在真正面试里都是致命伤但在模拟阶段发现还来得及补救。从那以后我每次刷题都强制走一遍「三问」能不能复述、能不能写最小实现、能不能说代价。能扛过这三问的题才算真正属于你扛不过去的就回到题库里把前置知识补齐。希望帮到你。本文还有配套的精品资源点击获取