2026 届前端校招冲刺:2 万字高频面试题库(含详解、追问与评分标准)
写给正在准备前端实习、秋招、春招和社招初级岗位的同学。
如果你已经刷过很多零散面试题,大概率会有一种感觉:每一道题看起来都懂,但真正坐到面试官面前,很难把答案讲成一条清晰的线。前端面试并不是单纯背概念,它更像一次"现场建模":面试官问一个问题,你需要快速判断它考的是基础、原理、工程经验,还是解决问题的思路。
这份题库的目标不是堆满所有知识点,而是帮你建立一套能在面试现场稳定输出的回答框架。每道题都包含四部分:
- 考察点:面试官真正想确认什么。
- 建议你在面试里怎么讲。
- 追问方向:常见的二连问、三连问。
- 评分参考:从 1 分到 4 分,判断自己答到什么层次。
适用人群:2026 届前端应届生、准备实习的在校生、0-2 年前端工程师。
建议用法:第一遍快速过目录,确认薄弱模块;第二遍按模块精读;第三遍把每题答案压缩成 60 秒口述版本;最后一遍拿项目经历串联技术点。
目录
- HTML 与 CSS 基础
- JavaScript 核心
- TypeScript
- React
- Vue
- 浏览器与网络
- 工程化与构建
- 性能优化
- 项目设计与前端架构
- 编码题与算法
- 移动端与跨端
- AI、前沿技术与综合潜力
难度说明:
- 简单:应届生必须答出来。
- 中等:能区分"背过"和"理解过"。
- 偏难:主要考原理深度、工程经验和表达能力。
一、HTML 与 CSS 基础
Q1:如何理解语义化 HTML?它在真实项目里有什么价值?【简单】
考察点:HTML 基础、可访问性意识、SEO 和工程可维护性。
参考答案:
语义化 HTML 指根据内容含义选择合适标签,而不是只用 div 和 span 搭页面。例如页面头部用 header,导航用 nav,主体内容用 main,文章用 article,侧边内容用 aside,表单项使用 label 关联控件。
它的价值主要有三点:
- 结构更清晰,团队维护时能直接从标签看出页面层级。
- 对搜索引擎和阅读器更友好,能帮助机器理解页面内容。
- 可访问性更好,键盘导航、屏幕阅读器、表单聚焦等体验会更稳定。
面试建议:不要只说"有利于 SEO"。更好的回答是:语义化不是为了让页面看起来不同,而是让页面结构对人、浏览器、搜索引擎和辅助设备都有意义。
追问方向:
section、article、div如何选择?label的for属性有什么作用?- 如何让一个自定义按钮具备可访问性?
评分参考:
- 1 分:知道语义化就是用合适标签。
- 2 分:能举出常见语义标签。
- 3 分:能说出 SEO、可访问性、维护性价值。
- 4 分:能结合表单、键盘操作、阅读器体验说明工程场景。
Q2:标准盒模型和 IE 盒模型有什么区别?为什么项目里常用 border-box?【简单】
考察点:CSS 盒模型、布局尺寸计算、工程习惯。
参考答案:
- 标准盒模型 中,
width和height只表示内容区域,元素最终宽度还要加上padding和border。 - IE 盒模型 中,
width和height包含内容、内边距和边框。
现代项目常用:
css
*,
*::before,
*::after {
box-sizing: border-box;
}
原因是 border-box 更符合布局直觉。比如一个卡片宽度设置为 300px,再加 padding: 16px 后,最终仍然占据 300px,不会因为 padding 导致布局溢出。组件化开发里,如果每个组件都要额外计算 padding 和 border,会增加很多不必要的心智负担。
补充 :
box-sizing不会影响margin。外边距始终在盒子外部,仍然会影响元素之间的距离。
追问方向:
box-sizing会继承吗?width: 100%加padding为什么会溢出?margin折叠发生在什么情况下?
评分参考:
- 1 分:知道两种盒模型存在差异。
- 2 分:能说清
width是否包含padding和border。 - 3 分:能解释为什么全局设置
border-box。 - 4 分:能结合组件布局、溢出问题和
margin折叠一起分析。
Q3:BFC 是什么?它能解决哪些 CSS 布局问题?【中等】
考察点 :格式化上下文、浮动、margin 折叠、布局隔离。
参考答案:
BFC 是 Block Formatting Context,块级格式化上下文。可以把它理解为一个独立的布局区域:内部元素的布局不会轻易影响外部,外部浮动元素也不会侵入它的布局计算。
常见触发方式包括:
overflow不为visibledisplay: flow-rootdisplay: inline-blockposition: absolute/fixedfloat不为noneflex/grid容器的子项等
实际用途有三个高频场景:
- 清除浮动,让父元素包住浮动子元素。
- 避免相邻块级元素 margin 折叠。
- 实现左侧浮动、右侧自适应的两栏布局,因为 BFC 区域不会和浮动元素重叠。
更推荐的现代写法是 display: flow-root,它语义明确,表示这个元素创建一个新的块格式化上下文,而不是用 overflow: hidden 顺带触发 BFC。
追问方向:
- 为什么
overflow: hidden可以清除浮动? display: flow-root相比clearfix有什么优点?- BFC 能解决所有
margin折叠吗?
评分参考:
- 1 分:知道 BFC 是块级格式化上下文。
- 2 分:能说出几个触发条件。
- 3 分:能结合清浮动、阻止
margin折叠说明用途。 - 4 分:能指出现代方案
flow-root,并理解布局隔离的本质。
Q4:flex: 1 到底是什么简写?和 flex: auto 有什么区别?【简单】
考察点:Flex 布局细节、主轴尺寸分配。
参考答案:
flex 是 flex-grow、flex-shrink、flex-basis 的简写。
通常情况下,flex: 1 会被解析为:
css
flex-grow: 1;
flex-shrink: 1;
flex-basis: 0%;
含义是:项目可以放大,也可以缩小,初始主轴尺寸按 0 计算,然后根据剩余空间分配。
flex: auto 通常等价于 1 1 auto。它和 flex: 1 最大区别在于 flex-basis。auto 会先考虑元素自身内容或设置的宽高,再参与空间分配;0% 则更像从零开始按比例分配。因此多个元素都写 flex: 1 时更容易等分,而 flex: auto 会受内容宽度影响。
一句话总结 :
flex: 1强调按比例分空间,flex: auto强调先尊重内容尺寸再弹性伸缩。
追问方向:
flex-basis和width同时存在时谁优先?align-items和align-content的区别是什么?flex子项为什么有时无法被压缩?
评分参考:
- 1 分:知道
flex是简写。 - 2 分:能说出三个属性。
- 3 分:能区分
flex: 1和flex: auto。 - 4 分:能解释内容尺寸、最小宽度、溢出之间的关系。
Q5:CSS 选择器优先级如何计算?为什么不建议滥用 !important?【简单】
考察点:级联规则、样式覆盖、工程维护。
参考答案:
CSS 优先级可以粗略按四类权重理解:
- 内联样式最高。
- ID 选择器高于类、属性和伪类。
- 类、属性和伪类高于标签和伪元素。
- 通配符和继承样式权重最低。
更准确的说法是按选择器元组比较,而不是简单相加。比如 ID 数量多的一方优先;ID 相同再比较类、属性、伪类数量;最后比较标签和伪元素数量。如果优先级相同,后声明的样式覆盖先声明的样式。
!important 会提升声明优先级,但滥用后会破坏正常级联关系。项目里一旦大量使用,后续样式只能用更强选择器或更多 !important 覆盖,调试成本会很高。
工程上更建议通过合理的 CSS 组织方式解决冲突,例如 CSS Modules、BEM、组件作用域样式、设计系统 token,而不是直接堆优先级。
追问方向:
:is()和:where()的优先级有什么差异?- CSS Modules 如何解决样式冲突?
scopedCSS 是否能完全避免样式污染?
评分参考:
- 1 分:知道选择器有优先级。
- 2 分:能比较 ID、类、标签的权重。
- 3 分:能解释
!important的维护问题。 - 4 分:能从工程规范角度给出样式隔离方案。
Q6:如何实现水平垂直居中?不同方案适合什么场景?【简单】
考察点:布局基本功、定位、flex/grid。
参考答案:
最常用方案是 Flex:
css
.parent {
display: flex;
align-items: center;
justify-content: center;
}
如果是单个子元素,也可以用 Grid:
css
.parent {
display: grid;
place-items: center;
}
如果需要兼容较旧布局,绝对定位也常见:
css
.child {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
面试建议 :要说清场景。Flex 适合容器内部整体对齐,Grid 写法简洁,绝对定位适合弹窗、浮层、未知尺寸元素。如果元素宽高固定,还可以用负
margin,但现在不推荐作为首选。
追问方向:
transform会不会影响层叠上下文?margin: auto在 flex 中为什么能居中?- 多行文本如何垂直居中?
评分参考:
- 1 分:能写出一种居中方案。
- 2 分:能写出 Flex 或绝对定位方案。
- 3 分:能比较多个方案适用场景。
- 4 分:能结合弹窗、浮层、响应式布局说明取舍。
二、JavaScript 核心
Q7:var、let、const 有什么区别?什么是暂时性死区?【简单】
考察点:作用域、变量提升、ES6 基础。
参考答案:
var:函数作用域,会变量提升,可以重复声明。let和const:块级作用域,不允许在同一作用域重复声明,并且存在暂时性死区。const:声明后不能重新赋值,但如果值是对象,对象内部属性仍然可以修改,因为const约束的是绑定关系,不是深层不可变。
暂时性死区 指从块级作用域开始到变量声明语句执行之前,这段区域内不能访问该变量。虽然变量在语义上已经存在于作用域中,但还没有完成初始化,访问会抛出 ReferenceError。
实践原则 :默认使用
const,需要重新赋值时用let,尽量避免var,这样能减少作用域和提升带来的隐藏问题。
追问方向:
const obj = {}后能否修改obj.a?- 为什么
typeof x有时也会报错? for循环中用var和let绑定事件有什么区别?
评分参考:
- 1 分:知道三者基本区别。
- 2 分:能说出块级作用域和变量提升。
- 3 分:能解释暂时性死区。
- 4 分:能结合闭包、循环、工程规范说明使用原则。
Q8:JavaScript 的原型链是什么?new 的过程发生了什么?【中等】
考察点:对象模型、继承机制、构造函数。
参考答案:
JavaScript 中每个对象都有一个内部原型,可以通过原型链向上查找属性。当访问对象属性时,如果对象自身没有,就会沿着原型继续查找,直到 null 为止。
- 函数有
prototype属性,用来作为通过该函数构造出的实例对象的原型。 - 实例对象可以通过
__proto__或Object.getPrototypeOf找到自己的原型。
new 大致做了四件事:
- 创建一个新对象。
- 将新对象的原型指向构造函数的
prototype。 - 以新对象作为
this执行构造函数。 - 如果构造函数返回对象,则返回该对象;否则返回新创建的对象。
追问方向:
Function.prototype和Object.prototype的关系是什么?- 箭头函数能不能被
new? - 如何手写一个
new?
评分参考:
- 1 分:知道属性会沿原型链查找。
- 2 分:能解释
prototype和实例原型关系。 - 3 分:能完整说明
new的四步。 - 4 分:能手写
new并解释构造函数返回值规则。
Q9:闭包是什么?它有哪些典型应用和风险?【中等】
考察点:词法作用域、函数生命周期、内存意识。
参考答案:
闭包是指函数能够访问其词法作用域中的变量,即使外层函数已经执行结束。它的本质不是"函数套函数"这么简单,而是内部函数持有了对外部作用域变量的引用。
典型应用包括:
- 封装私有变量
- 函数柯里化
- 缓存中间结果
- 防抖节流
- 模块化封装
- 事件回调保留状态
闭包的风险主要是变量生命周期被延长。如果闭包持有大量对象、DOM 节点或定时器引用,又没有及时释放,就可能导致内存无法回收。实际项目里要注意在组件卸载时解绑事件、清除定时器、断开无用引用。
好的回答要同时讲清优点和边界:闭包让状态可以被安全保留,但不应该被当成随意存储全局状态的工具。
追问方向:
- 循环里创建闭包为什么容易出错?
- 闭包一定会造成内存泄漏吗?
- React Hooks 和闭包有什么关系?
评分参考:
- 1 分:知道闭包能访问外层变量。
- 2 分:能举出一两个应用。
- 3 分:能解释变量生命周期延长。
- 4 分:能结合事件、组件卸载、Hooks stale closure 分析。
Q10:事件循环是什么?宏任务和微任务的执行顺序是怎样的?【中等】
考察点:异步模型、Promise、任务调度。
参考答案:
JavaScript 主线程一次只能执行一个任务。事件循环负责协调同步代码、异步回调、渲染和任务队列。
一般执行顺序:
- 先执行当前调用栈中的同步代码。
- 同步代码执行完后,清空微任务队列。
- 然后浏览器可能进行渲染。
- 再取出下一个宏任务执行。
- 常见宏任务 :
setTimeout、setInterval、DOM 事件回调、网络回调等。 - 常见微任务 :
Promise.then/catch/finally、queueMicrotask、MutationObserver。
示例:
javascript
console.log(1);
setTimeout(() => console.log(2));
Promise.resolve().then(() => console.log(3));
console.log(4);
输出是 1 4 3 2。因为同步代码先执行,Promise 回调作为微任务优先于定时器宏任务。
追问方向:
async/await和Promise的执行顺序如何理解?- Node.js 事件循环和浏览器有什么不同?
- 微任务过多会造成什么问题?
评分参考:
- 1 分:知道同步先于异步。
- 2 分:能区分宏任务和微任务。
- 3 分:能判断常见输出顺序。
- 4 分:能联系渲染时机、微任务饥饿、Node 环境差异。
Q11:this 的绑定规则有哪些?箭头函数的 this 有什么特殊之处?【中等】
考察点:调用方式、上下文绑定、函数语义。
参考答案:
this 的值不是由函数定义位置单独决定的,而是由调用方式决定。常见规则包括:
- 默认调用 时,非严格模式指向全局对象,严格模式为
undefined。 - 作为对象方法调用时指向调用者。
- 通过
call、apply、bind显式绑定。 - 通过
new调用时指向新创建的实例。
箭头函数 没有自己的 this,它会捕获定义时外层词法作用域中的 this。因此箭头函数适合写回调,避免手动保存 const self = this,但不适合用作对象原型方法、构造函数或需要动态绑定 this 的场景。
优先级 :
new绑定通常高于显式绑定,显式绑定高于隐式绑定,隐式绑定高于默认绑定。但箭头函数不参与这些规则,因为它的this已经由词法环境决定。
追问方向:
bind返回的函数还能被call改变this吗?- 箭头函数能不能作为构造函数?
- DOM 事件回调中普通函数和箭头函数的
this差异是什么?
评分参考:
- 1 分:知道
this和调用方式有关。 - 2 分:能说出默认、隐式、显式、
new绑定。 - 3 分:能解释箭头函数词法
this。 - 4 分:能分析优先级和真实代码输出。
Q12:如何理解 Promise?then 链式调用为什么可以连续执行?【中等】
考察点:异步抽象、状态机、链式调用。
参考答案:
Promise 是对异步结果的封装,它有三种状态:pending、fulfilled、rejected。状态只能从 pending 变成 fulfilled 或 rejected,且一旦改变就不可逆。
then 之所以能链式调用,是因为它会返回一个新的 Promise。回调函数的返回值会决定新 Promise 的状态:
- 如果返回普通值,新
Promise变为fulfilled。 - 如果抛出错误,新
Promise变为rejected。 - 如果返回另一个
Promise,新Promise会跟随它的最终状态。
这也是为什么可以写:
javascript
fetchUser()
.then(user => fetchOrders(user.id))
.then(orders => render(orders))
.catch(error => report(error));
catch 本质上也是 then(null, onRejected) 的语法糖。finally 不接收成功值或失败原因用于改变主流程,常用于关闭 loading、释放资源等收尾逻辑。
追问方向:
Promise状态为什么不可逆?Promise.all和Promise.allSettled的区别?- 如何限制并发请求数量?
评分参考:
- 1 分:知道
Promise处理异步。 - 2 分:能说出三种状态。
- 3 分:能解释
then返回新Promise。 - 4 分:能分析错误传播、并发控制和状态吸收。
Q13:深拷贝和浅拷贝有什么区别?如何实现一个可靠的深拷贝?【中等】
考察点:引用类型、对象复制、边界情况。
参考答案:
浅拷贝 只复制对象第一层属性。如果属性值是引用类型,新对象和旧对象仍然共享同一个引用。常见浅拷贝方式包括扩展运算符、Object.assign、数组的 slice 等。
深拷贝 会递归复制嵌套对象,使新旧对象在结构上独立。JSON.parse(JSON.stringify(obj)) 是最常见但不可靠的方案,它无法处理 undefined、函数、Symbol、Date、RegExp、Map、Set、循环引用等情况。
现代浏览器和 Node 环境可以考虑 structuredClone,它支持更多内置类型,也能处理循环引用,但不能克隆函数和 DOM 节点。
如果手写深拷贝,需要处理基本类型、数组、普通对象、日期、正则、Map、Set,并用 WeakMap 记录已拷贝对象,避免循环引用导致递归爆栈。
追问方向:
structuredClone有哪些限制?- 为什么循环引用会导致栈溢出?
- Immer 是如何避免大规模深拷贝成本的?
评分参考:
- 1 分:知道浅拷贝共享引用。
- 2 分:能说出 JSON 深拷贝的问题。
- 3 分:能提到
structuredClone或WeakMap。 - 4 分:能从性能和不可变数据结构角度分析。
Q14:防抖和节流有什么区别?分别适合什么业务场景?【简单】
考察点:高频事件优化、定时器、用户体验。
参考答案:
- 防抖 是指事件频繁触发时,只在最后一次触发后等待一段时间再执行。如果等待期间又触发,就重新计时。适合搜索输入联想、窗口尺寸变化后重新计算布局、表单校验等场景。
- 节流 是指事件频繁触发时,固定时间间隔内最多执行一次。适合滚动加载、拖拽移动、按钮连点保护、页面滚动位置上报等场景。
一句话区分:防抖关注"最后一次",节流关注"固定频率"。如果用户持续输入搜索词,用防抖可以避免每个字符都发请求;如果用户持续滚动页面,用节流可以保证逻辑按稳定频率执行。
追问方向:
- 防抖如何支持立即执行?
- 节流用时间戳和定时器实现有什么区别?
- React 中如何避免每次渲染都重新创建防抖函数?
评分参考:
- 1 分:知道二者用于限制高频执行。
- 2 分:能区分最后一次和固定频率。
- 3 分:能结合业务场景说明选择。
- 4 分:能写出实现并处理立即执行、取消、闭包问题。
三、TypeScript
Q15:TypeScript 相比 JavaScript 解决了什么问题?【简单】
考察点:类型系统价值、工程化意识。
参考答案:
TypeScript 是 JavaScript 的超集,它主要通过静态类型系统提升大型项目的可维护性。它不能让运行时错误完全消失,但能把很多低级错误提前到开发阶段暴露出来。
它解决的问题包括:
- 接口字段不明确
- 函数入参和返回值不清晰
- 重构风险高
- 多人协作时依赖口头约定
- IDE 智能提示弱
对于中大型前端项目,类型本身也是一种文档,能帮助开发者快速理解模块边界。
注意:TypeScript 的类型只存在于编译阶段,最终运行的仍然是 JavaScript。因此接口返回数据仍然需要运行时校验,不能因为写了类型就默认后端数据一定可靠。
追问方向:
- TypeScript 能否保证运行时类型安全?
any、unknown、never的区别?- 类型声明文件
.d.ts的作用是什么?
评分参考:
- 1 分:知道 TS 增加类型。
- 2 分:能说出类型检查和提示价值。
- 3 分:能结合重构、协作、接口约束说明。
- 4 分:能指出编译期类型和运行时数据之间的边界。
Q16:interface 和 type 有什么区别?如何选择?【中等】
考察点:类型建模、声明合并、扩展能力。
参考答案:
interface主要用于描述对象结构,可以通过extends扩展,并支持声明合并 。多个同名interface会自动合并,这在扩展第三方库类型时很有用。type可以给任意类型起别名,不仅能描述对象,还能描述联合类型、交叉类型、元组、条件类型、映射类型等。它表达能力更强,但不支持同名声明合并。
选择原则 :描述公开对象结构或类实现约束时优先用
interface;需要联合、交叉、工具类型组合时用type。团队内部更重要的是风格统一。
追问方向:
- 什么是声明合并?
interface A extends B和type A = B & C有什么差异?- 如何给第三方库补充类型?
评分参考:
- 1 分:知道二者都能描述对象。
- 2 分:能说出
type支持联合类型。 - 3 分:能说出
interface声明合并。 - 4 分:能结合库类型扩展和团队规范说明选择。
Q17:any 和 unknown 有什么区别?为什么更推荐 unknown?【简单】
考察点:类型安全、边界处理。
参考答案:
any会绕过类型检查,相当于告诉 TypeScript"不要管这个变量"。一旦使用any,后续属性访问、函数调用、赋值都不会被检查,类型系统的保护会被削弱。unknown表示"我现在不知道它是什么类型"。它可以接收任何值,但使用之前必须做类型缩小 ,例如typeof、Array.isArray、自定义类型守卫等。
因此在处理外部输入时,比如接口响应、JSON.parse、第三方 SDK 回调,更推荐先用 unknown 表达不确定,再通过校验收窄成可信类型。这样既承认数据来源不可靠,又能保留类型检查。
追问方向:
never表示什么?- 如何写自定义类型守卫?
JSON.parse的返回值该如何建模?
评分参考:
- 1 分:知道
any放弃检查。 - 2 分:知道
unknown使用前要收窄。 - 3 分:能写出类型收窄例子。
- 4 分:能结合接口数据、错误处理、运行时校验说明。
Q18:泛型是什么?它解决了什么问题?【中等】
考察点:类型复用、约束、推导。
参考答案:
泛型可以把类型作为参数传入,让函数、接口、类在保持类型安全的同时具备复用能力。它解决的是"逻辑相同但类型不同"的问题。
例如:
typescript
function identity<T>(value: T): T {
return value;
}
这里 T 会根据传入值自动推导。传入字符串返回字符串,传入数字返回数字,既不需要写多个函数,也不会丢失类型信息。
泛型还可以通过 extends 加约束:
typescript
function getLength<T extends { length: number }>(value: T) {
return value.length;
}
这表示传入的类型必须有 length 属性。
面试要点:泛型不是为了写复杂类型,而是为了在抽象和类型安全之间取得平衡。
追问方向:
- 泛型默认类型怎么写?
keyof和泛型如何配合?- 如何实现一个类型安全的
getProperty?
评分参考:
- 1 分:知道泛型是类型参数。
- 2 分:能写出简单泛型函数。
- 3 分:能使用泛型约束。
- 4 分:能结合
keyof、条件类型、工具类型完成类型建模。
Q19:TypeScript 常见工具类型有哪些?它们底层思路是什么?【中等】
考察点:映射类型、条件类型、类型变换。
参考答案:
常见工具类型包括 Partial<T>、Required<T>、Readonly<T>、Pick<T, K>、Omit<T, K>、Record<K, T>、ReturnType<T>、Parameters<T> 等。
它们的底层思路主要是映射类型 、索引访问类型 、条件类型 和 infer。
例如 Partial<T> 会遍历对象类型的所有 key,并把每个属性变成可选:
typescript
type MyPartial<T> = {
[K in keyof T]?: T[K];
};
Pick<T, K> 是从对象中选择一部分 key,Omit<T, K> 可以理解为先排除 key 再 Pick。ReturnType 则通过条件类型和 infer 推断函数返回值。
面试建议:不用死背所有实现,但要能说明工具类型是在类型层面对对象结构做转换,提高类型复用能力。
追问方向:
- 如何手写
Pick、Omit、ReturnType? Record<string, unknown>和普通对象类型有什么区别?- 深层
Partial如何实现?
评分参考:
- 1 分:能说出几个工具类型名称。
- 2 分:知道它们用于类型变换。
- 3 分:能手写
Partial或Pick。 - 4 分:能理解映射类型、条件类型和
infer的组合。
四、React
Q20:React 的核心思想是什么?为什么说 UI 是状态的函数?【简单】
考察点:声明式 UI、组件化、状态驱动。
参考答案:
React 的核心思想可以概括为:组件化、声明式、状态驱动。开发者不需要直接命令 DOM 一步步怎么变,而是描述某个状态下 UI 应该长什么样。状态变化后,React 负责计算新旧 UI 的差异并更新视图。
"UI 是状态的函数"可以理解为:
UI = f(state)
同样的状态应该渲染出同样的界面。当状态变化时,组件重新渲染,得到新的 UI 描述。这种模型让复杂界面更容易推理,因为你关注的是状态和视图之间的映射关系,而不是手动维护 DOM 的每一步变化。
实践要求 :尽量减少派生状态和重复状态。例如列表长度可以从数组计算出来,就不应该再单独维护一个
count,否则容易出现数据不一致。
追问方向:
- 受控组件和非受控组件的区别?
- 为什么不要直接修改
state? - React 重新渲染是否等于真实 DOM 一定更新?
评分参考:
- 1 分:知道 React 是组件化框架。
- 2 分:能说出声明式和状态驱动。
- 3 分:能解释 UI 与
state的关系。 - 4 分:能结合派生状态、不可变更新、渲染优化说明实践。
Q21:React Fiber 是什么?它解决了什么问题?【偏难】
考察点:React 渲染架构、调度、可中断渲染。
参考答案:
Fiber 是 React 的一种内部数据结构和协调架构。早期 React 更新过程接近递归同步执行,一旦组件树很大,主线程会长时间被占用,导致页面卡顿、输入不响应。
Fiber 把渲染工作拆分成一个个可执行单元,每个组件对应一个 Fiber 节点。这样 React 可以在浏览器有空闲时间时分片执行渲染任务,并根据优先级决定哪些更新更紧急。比如用户输入优先级高,普通列表渲染优先级低。
Fiber 架构让 React 具备了可中断、可恢复、可优先级调度的能力,是并发特性的基础。
注意 :Fiber 主要影响
render阶段;commit阶段涉及真实 DOM 变更,需要保持同步执行,避免界面处于不一致状态。
追问方向:
render阶段和commit阶段有什么区别?- 为什么
commit阶段不能随意中断? - React 18 并发渲染解决了什么体验问题?
评分参考:
- 1 分:知道 Fiber 是 React 内部机制。
- 2 分:能说出它把任务拆成小单元。
- 3 分:能解释可中断和优先级调度。
- 4 分:能区分 render/commit,并联系并发渲染。
Q22:useEffect 和 useLayoutEffect 有什么区别?【中等】
考察点:副作用时机、渲染流程、闪烁问题。
参考答案:
useEffect:在浏览器完成绘制之后异步执行,不会阻塞页面显示,适合请求数据、订阅事件、记录日志、操作非布局相关副作用。useLayoutEffect:在 DOM 更新之后、浏览器绘制之前同步执行,会阻塞绘制。它适合读取布局并同步修改 DOM 的场景,比如测量元素尺寸后立即调整位置,避免用户看到闪烁。
选择原则:
- 默认使用
useEffect。 - 只有当副作用必须在绘制前完成,否则会出现明显视觉跳动时,才使用
useLayoutEffect。滥用useLayoutEffect会延长页面阻塞时间,影响性能。
共同点:两者都可以返回清理函数,用于解绑事件、取消订阅、清除定时器。依赖数组要准确,否则容易出现重复执行或闭包读取旧值的问题。
追问方向:
- 依赖数组为空表示什么?
- 如何处理 effect 中的异步请求竞态?
- 为什么服务端渲染中
useLayoutEffect会有警告?
评分参考:
- 1 分:知道二者都是处理副作用。
- 2 分:能说出执行时机不同。
- 3 分:能解释绘制前后和闪烁问题。
- 4 分:能结合清理函数、竞态、SSR 场景分析。
Q23:React Hooks 为什么不能写在条件语句里?【中等】
考察点:Hooks 调用顺序、状态保存机制。
参考答案:
Hooks 依赖稳定的调用顺序 来关联每次渲染中的状态。React 并不是通过变量名识别某个 useState,而是按照 Hooks 调用顺序依次保存和读取状态。
如果把 Hook 写在条件语句、循环或嵌套函数中,某次渲染调用了,下一次渲染没调用,后续 Hooks 的顺序就会错位,React 可能把 A 状态读成 B 状态,导致不可预测问题。
因此 Hooks 有两条基本规则:
- 只能在函数组件或自定义 Hook 顶层调用。
- 不能在条件、循环、普通函数里调用。
如果确实有条件逻辑,应把条件写到 Hook 内部:
javascript
useEffect(() => {
if (!enabled) return;
subscribe();
return unsubscribe;
}, [enabled]);
追问方向:
- 自定义 Hook 本质是什么?
- Hooks 如何保存多个
state? - 为什么 ESLint 能检查 Hooks 规则?
评分参考:
- 1 分:知道不能在条件里写 Hook。
- 2 分:能说出调用顺序必须稳定。
- 3 分:能解释状态错位问题。
- 4 分:能结合自定义 Hook 和 lint 规则说明机制。
Q24:useMemo 和 useCallback 的区别是什么?什么时候不该用?【中等】
考察点:渲染优化、引用稳定、过度优化。
参考答案:
useMemo:用于缓存计算结果。useCallback:用于缓存函数引用。
可以粗略理解为:
javascript
useCallback(fn, deps)
// 类似于
useMemo(() => fn, deps)
它们常见用途有两个:
- 避免昂贵计算在每次渲染时重复执行。
- 保持对象或函数引用稳定,配合
React.memo避免子组件无意义重渲染。
但它们不是越多越好 。每次使用都需要维护依赖数组,也会增加代码复杂度。对于普通计算、很轻量的组件、没有传给 memo 子组件的函数,使用它们可能收益很低,甚至让代码更难维护。
工程判断 :先确认性能问题,再用 profiler 或实际场景定位,而不是凭感觉给所有函数包
useCallback。
追问方向:
React.memo是什么?- 依赖数组写错会有什么问题?
- 为什么对象字面量会导致子组件重新渲染?
评分参考:
- 1 分:知道二者用于缓存。
- 2 分:能区分缓存值和缓存函数。
- 3 分:能说明与
React.memo的配合。 - 4 分:能讲清过度优化成本和定位方法。
Q25:React 中状态管理如何选型?Context、Redux、Zustand 分别适合什么场景?【中等】
考察点:状态分层、工程选型、复杂度控制。
参考答案:
状态管理先要区分状态类型:
- 组件局部状态
- 跨组件共享状态
- 服务端缓存状态
- URL 状态
- 表单状态
不同状态不一定要放到同一个全局仓库。
- Context:适合低频更新的全局配置,比如主题、语言、登录用户信息。它不是完整状态管理库,更新时可能导致消费该 Context 的组件重新渲染,需要谨慎拆分。
- Redux:适合状态流复杂、需要严格可预测、需要中间件、调试工具和团队规范的大型项目。它样板代码较多,但约束强。
- Zustand:更轻量,API 简洁,适合中小型项目或局部复杂状态。
服务端数据缓存 通常更推荐
React Query、SWR这类工具,因为它们处理缓存、重试、失效、加载状态等问题更专业。
追问方向:
- Context 为什么可能导致性能问题?
- Redux Toolkit 解决了 Redux 哪些痛点?
- 服务端状态和客户端状态有什么区别?
评分参考:
- 1 分:知道几种状态管理方案。
- 2 分:能说出基本使用场景。
- 3 分:能区分客户端状态和服务端缓存。
- 4 分:能从团队规模、调试、性能、复杂度角度选型。
Q26:React 组件 key 的作用是什么?为什么不建议用数组下标作为 key?【简单】
考察点:列表 diff、组件身份、渲染正确性。
参考答案:
key 用来帮助 React 在列表更新时识别每个元素的稳定身份 。React diff 时会根据 key 判断哪些元素是新增、删除、移动或复用。
不建议用数组下标作为 key,是因为当列表发生插入、删除、排序时,下标会变化。React 可能错误复用组件实例,导致输入框内容错位、动画异常、组件内部状态跟着错误项移动。
安全情况 :只有在列表完全静态、不排序、不插入删除时,下标
key才相对安全。实际项目中更推荐使用数据本身的唯一 ID。
追问方向:
key是否会作为 props 传给组件?- 为什么随机数
key也不好? - React diff 为什么不做完整树编辑距离比较?
评分参考:
- 1 分:知道
key用于列表。 - 2 分:能说出帮助 React 识别元素。
- 3 分:能解释下标
key的错位问题。 - 4 分:能联系组件状态复用和 diff 性能。
五、Vue
Q27:Vue 2 和 Vue 3 响应式原理有什么区别?【中等】
考察点 :响应式系统、Object.defineProperty、Proxy。
参考答案:
- Vue 2 :主要基于
Object.defineProperty对对象属性做 getter/setter 劫持。在读取属性时收集依赖,在修改属性时通知依赖更新。它的问题是无法直接监听新增属性和删除属性,数组的一些变更也需要特殊处理。 - Vue 3 :使用
Proxy代理整个对象,可以拦截读取、设置、删除、in、遍历等操作,对新增属性、删除属性、数组索引变化支持更自然。配合Reflect可以更规范地执行默认行为。
Vue 3 的响应式系统也更独立,可以通过 reactive、ref、computed、watch 等 API 在组件外使用。这让组合式 API 更适合逻辑复用。
追问方向:
ref和reactive如何选择?- 为什么解构
reactive对象会丢失响应式? toRefs的作用是什么?
评分参考:
- 1 分:知道 Vue 2 和 Vue 3 响应式实现不同。
- 2 分:能说出
defineProperty和Proxy。 - 3 分:能说明 Vue 2 的新增属性限制。
- 4 分:能结合 Composition API、解构丢失响应式分析。
Q28:computed 和 watch 有什么区别?【简单】
考察点:派生状态、副作用、缓存。
参考答案:
computed:用于根据已有响应式数据计算派生值,具有缓存能力。只要依赖不变,多次访问不会重复计算。它适合模板展示、格式化、过滤列表、拼接字段等场景。watch:用于监听响应式数据变化并执行副作用,例如发请求、写本地存储、手动操作 DOM、上报日志等。它关注的是"变化发生后要做什么"。
选择原则:
- 能用计算结果表达的,用
computed。 - 需要执行异步或副作用的,用
watch。 - 不要用
watch去维护一个本可以由computed得出的状态,否则容易造成重复状态和同步问题。
追问方向:
computed为什么有缓存?watch和watchEffect的区别?watch如何处理异步竞态?
评分参考:
- 1 分:知道
computed计算值,watch监听变化。 - 2 分:能说出
computed有缓存。 - 3 分:能结合副作用场景选择。
- 4 分:能指出避免重复派生状态的设计原则。
Q29:Vue 的 nextTick 是什么?为什么修改数据后不能立即拿到最新 DOM?【中等】
考察点:异步更新队列、DOM 渲染时机。
参考答案:
Vue 修改响应式数据后,并不会同步立刻更新 DOM,而是把更新任务放入队列,在当前同步代码执行完后批量刷新。这样可以合并多次数据变化,避免频繁操作 DOM。
nextTick 用于在下一次 DOM 更新完成后执行回调 。如果你修改数据后立刻读取 DOM,读到的可能还是旧 DOM;把读取逻辑放到 nextTick 中,就能拿到更新后的结果。
例如弹窗打开后需要聚焦输入框、列表渲染后需要滚动到底部、数据变化后测量元素高度,这些都适合使用 nextTick。
追问方向:
- Vue 为什么要批量异步更新?
nextTick底层可能用哪些异步 API?- React 中有没有类似概念?
评分参考:
- 1 分:知道
nextTick和 DOM 更新有关。 - 2 分:能说出 Vue DOM 异步更新。
- 3 分:能举出读取 DOM 的业务场景。
- 4 分:能联系批处理、微任务、性能优化说明。
Q30:Vue 组件通信有哪些方式?如何选择?【简单】
考察点:组件设计、数据流、状态管理。
参考答案:
- 父子组件通信 :最常用的是
props和emit。父组件通过props传数据,子组件通过emit通知父组件事件,符合单向数据流。 - 跨层级通信 :可以使用
provide/inject,适合主题、表单上下文、组件库内部上下文等场景。 - 兄弟组件通信:可以提升状态到共同父组件,也可以使用全局状态管理,如 Pinia。
避免滥用事件总线:事件总线短期方便,但数据来源不清晰,调试困难。Pinia 更适合管理跨页面、跨模块共享状态;而服务端缓存数据仍然可以交给专门的数据请求库或封装层处理。
追问方向:
props为什么不建议子组件直接修改?provide/inject是否响应式?- Pinia 相比 Vuex 有什么变化?
评分参考:
- 1 分:能说出
props和emit。 - 2 分:能说出
provide/inject或 Pinia。 - 3 分:能按组件关系选择方案。
- 4 分:能从数据流清晰度和可维护性角度分析。
六、浏览器与网络
Q31:从输入 URL 到页面展示,浏览器发生了什么?【偏难】
考察点:网络链路、浏览器渲染、整体知识面。
参考答案:
可以按几个阶段回答:
- 浏览器解析 URL,检查缓存、HSTS、协议等信息。
- 进行 DNS 解析,把域名解析成 IP。
- 建立连接,HTTPS 还需要 TLS 握手。
- 浏览器发送 HTTP 请求,服务器处理后返回响应。
- 浏览器接收 HTML,开始解析并构建 DOM;遇到 CSS 构建 CSSOM;两者结合生成渲染树。
- 进行布局、绘制、合成,最终展示页面。
- 加载 JS、图片、字体等资源,并可能触发后续渲染更新。
回答要点:不要讲成死记硬背流水账。要突出关键点:
- DNS 可能缓存
- TCP/TLS 有握手成本
- HTTP 缓存会影响是否真正请求服务器
- CSS 会影响渲染
- JS 可能阻塞 HTML 解析
- 现代浏览器会做预解析和资源优先级调度
追问方向:
- DNS 解析有哪些缓存?
- CSS 和 JS 分别如何阻塞渲染?
- HTTP/2 对这个过程有哪些优化?
评分参考:
- 1 分:能说出 DNS、请求、渲染几个阶段。
- 2 分:能按顺序描述主要流程。
- 3 分:能解释缓存、TLS、DOM/CSSOM。
- 4 分:能联系资源优先级、阻塞渲染和性能优化。
Q32:浏览器缓存有哪些类型?强缓存和协商缓存如何工作?【中等】
考察点:HTTP 缓存、性能优化、部署策略。
参考答案:
浏览器缓存主要包括 HTTP 缓存、内存缓存、磁盘缓存、Service Worker 缓存等。面试最常问的是 HTTP 强缓存和协商缓存。
- 强缓存 :浏览器在缓存有效期内直接使用本地资源,不向服务器发送请求。相关响应头主要是
Cache-Control,例如max-age、public、private、no-cache、no-store。Expires是较老的字段,依赖客户端时间。 - 协商缓存 :缓存过期或需要确认时,浏览器向服务器发送条件请求。常见字段包括
ETag/If-None-Match和Last-Modified/If-Modified-Since。如果资源未变化,服务器返回 304,浏览器继续使用本地缓存。
工程实践:通常对带 hash 的静态资源设置长强缓存,对 HTML 设置不缓存或短缓存,确保发版后入口文件能及时更新。
追问方向:
no-cache和no-store的区别?ETag和Last-Modified谁优先?- 前端发版后用户白屏可能和缓存有什么关系?
评分参考:
- 1 分:知道缓存能减少请求。
- 2 分:能区分强缓存和协商缓存。
- 3 分:能说出关键响应头。
- 4 分:能结合 hash 静态资源和 HTML 缓存部署策略。
Q33:HTTP 状态码 301、302、304、401、403、500 分别表示什么?【简单】
考察点:HTTP 基础、接口排查能力。
参考答案:
- 301:永久重定向,资源地址长期变更。
- 302:临时重定向,客户端下次仍可以请求原地址。
- 304:资源未修改,配合协商缓存使用,响应体通常为空。
- 401:未认证,通常需要登录或 token 无效。
- 403:已认证但没有权限。
- 500:服务器内部错误。
分类归纳:2xx 成功,3xx 重定向或缓存,4xx 客户端问题,5xx 服务端问题。这样比零散背诵更清晰。
实际排查接口时,401 更关注登录态、token、cookie;403 更关注权限、角色、资源归属;500 则要看服务端日志、请求参数和链路追踪。
追问方向:
- 301 和 302 对 SEO、缓存有什么影响?
- 401 和 403 的根本区别?
- 502、503、504 常见于什么场景?
评分参考:
- 1 分:能说出几个常见状态码。
- 2 分:能按 2xx/3xx/4xx/5xx 分类。
- 3 分:能区分 401 和 403。
- 4 分:能结合接口排查和缓存机制说明。
Q34:跨域是什么?CORS 的关键响应头有哪些?【中等】
考察点:同源策略、前后端联调、安全边界。
参考答案:
跨域源于浏览器同源策略。协议、域名、端口任意一个不同,就属于不同源。浏览器限制脚本读取跨源响应内容,以保护用户数据安全。
CORS 是浏览器支持的跨域资源共享机制。服务端通过响应头声明允许哪些来源访问资源。常见响应头包括:
Access-Control-Allow-OriginAccess-Control-Allow-MethodsAccess-Control-Allow-HeadersAccess-Control-Allow-Credentials
对于复杂请求,浏览器会先发送 OPTIONS 预检请求,确认服务器允许后再发送真实请求。
注意:跨域限制主要发生在浏览器环境。服务器之间请求不受浏览器同源策略限制。开发环境代理能解决本地联调问题,但生产环境仍需要服务端正确配置 CORS 或通过同域网关转发。
追问方向:
- 什么是简单请求和复杂请求?
- 带 cookie 的跨域请求需要哪些配置?
- JSONP 为什么能跨域?有什么缺点?
评分参考:
- 1 分:知道跨域和同源策略有关。
- 2 分:能说出协议、域名、端口。
- 3 分:能说明 CORS 响应头和预检。
- 4 分:能结合凭证、代理、生产部署安全分析。
Q35:Cookie、LocalStorage、SessionStorage 有什么区别?【简单】
考察点:浏览器存储、安全、登录态。
参考答案:
- Cookie :会随同域请求自动发送给服务器,容量较小,常用于登录态、会话标识、服务端识别用户。它可以设置过期时间、域、路径、
HttpOnly、Secure、SameSite等属性。 - LocalStorage:容量相对更大,默认长期保存,除非主动删除。
- SessionStorage:只在当前标签页会话内有效,页面关闭后清除,同源但不同标签页通常不共享。
安全建议 :不建议把敏感 token 随意放入 LocalStorage,因为一旦发生 XSS,脚本可以读取。Cookie 如果设置
HttpOnly,前端 JS 无法读取,能降低 XSS 窃取风险,但仍要考虑 CSRF,可通过SameSite、CSRF Token 等方式防护。
追问方向:
HttpOnly、Secure、SameSite分别做什么?- XSS 和 CSRF 有什么区别?
- IndexedDB 适合什么场景?
评分参考:
- 1 分:知道三者都能存数据。
- 2 分:能区分生命周期和是否随请求发送。
- 3 分:能说出 Cookie 安全属性。
- 4 分:能结合 XSS、CSRF 和登录态设计分析。
七、工程化与构建
Q36:Webpack 和 Vite 的核心区别是什么?【中等】
考察点:构建工具、开发体验、模块机制。
参考答案:
- Webpack:传统打包器,开发时通常会先分析依赖图,把模块打包后再交给浏览器。它生态成熟、插件丰富、适合复杂项目,但冷启动和大项目增量编译可能较慢。
- Vite:开发环境基于浏览器原生 ES Modules。启动时不需要先把整个项目打包,而是按需编译浏览器请求到的模块。依赖预构建通常使用 esbuild,速度很快。生产构建默认基于 Rollup,产物优化能力强。
一句话总结:Webpack 更像"先打包再运行",Vite 开发模式更像"先启动,按需转换"。但这不代表 Vite 永远优于 Webpack。老项目、复杂 loader/plugin、特殊构建链路、微前端遗留系统,Webpack 仍然常见。
追问方向:
- Vite 为什么冷启动快?
- 依赖预构建解决了什么问题?
- 生产环境 Vite 为什么还要打包?
评分参考:
- 1 分:知道二者都是构建工具。
- 2 分:能说出 Vite 开发环境快。
- 3 分:能解释 ESM 按需加载和预构建。
- 4 分:能结合项目复杂度和生态成熟度做选型。
Q37:Babel 是做什么的?和 Polyfill 有什么区别?【中等】
考察点:JS 编译、兼容性、运行时 API。
参考答案:
- Babel :主要负责语法转换,把新版 JavaScript 语法转换成目标环境能理解的旧语法。例如把箭头函数、可选链、类语法等转换成兼容写法。
- Polyfill :负责补齐运行时缺失的 API 。例如旧浏览器没有
Promise、Array.prototype.includes、Object.assign,只靠 Babel 转语法是不够的,需要引入对应 polyfill。
区分方式 :Babel 解决"语法能不能解析",Polyfill 解决"API 存不存在"。实际项目里通常结合
@babel/preset-env、browserslist、core-js按目标浏览器自动决定转换和注入策略。
追问方向:
browserslist的作用是什么?useBuiltIns: usage和entry有什么区别?- Babel 插件和预设的区别?
评分参考:
- 1 分:知道 Babel 转换 JS。
- 2 分:能区分语法转换和 API 补齐。
- 3 分:能举出 Promise 需要 polyfill。
- 4 分:能结合目标浏览器、core-js、按需注入说明。
Q38:Tree Shaking 是什么?为什么有时不生效?【中等】
考察点:打包优化、ESM、模块副作用。
参考答案:
Tree Shaking 用于移除未使用代码,减少最终包体积。它依赖静态分析,因此 ES Modules 的 import/export 更适合 Tree Shaking,因为依赖关系在编译阶段就比较明确。
不生效的常见原因:
- 使用 CommonJS 动态导入导出。
- 模块存在副作用,打包器不敢删除。
- 引入方式不正确,例如从大包入口整体引入。
- 代码被动态访问。
- 库没有正确声明
sideEffects。
注意:Tree Shaking 删除的是"未被引用且可证明无副作用"的代码,不是简单按文件删除。
追问方向:
- ESM 为什么比 CommonJS 更容易 Tree Shaking?
package.json中sideEffects是什么?- 如何分析打包产物体积?
评分参考:
- 1 分:知道 Tree Shaking 删除无用代码。
- 2 分:能说出依赖 ESM 静态分析。
- 3 分:能说明副作用导致不生效。
- 4 分:能结合组件库按需引入和产物分析排查。
Q39:前端项目为什么需要 ESLint、Prettier、Husky、Commitlint?【简单】
考察点:工程规范、团队协作、质量门禁。
参考答案:
- ESLint:检查代码质量和潜在问题,比如未使用变量、Hooks 规则、危险写法等。
- Prettier:统一格式,减少团队在缩进、换行、引号上的争论。
- Husky:接入 Git hooks,在提交前执行检查、测试或格式化。
- Commitlint:规范提交信息,让提交历史更清晰,也方便后续自动生成 changelog 或做版本发布。
本质:这些工具不是为了"显得专业",而是把团队约定自动化。规则如果只写在文档里,很容易被忘记;放到工具链里,才能稳定执行。
追问方向:
- ESLint 和 Prettier 冲突如何处理?
pre-commit和pre-push适合分别做什么?- 为什么提交阶段不建议跑过慢任务?
评分参考:
- 1 分:知道这些是规范工具。
- 2 分:能说出各自基本作用。
- 3 分:能理解自动化质量门禁价值。
- 4 分:能从速度、体验、CI 分层角度设计规范流程。
Q40:Monorepo 是什么?适合什么团队和项目?【中等】
考察点:仓库管理、依赖复用、工程规模。
参考答案:
Monorepo 指把多个相关项目或包放在同一个仓库中管理。它适合多个应用共享组件库、工具库、类型定义、配置和发布流程的场景。
优点:
- 跨包修改更容易
- 依赖版本统一
- 代码复用方便
- 规范集中管理
缺点:
- 仓库规模变大后,构建、测试、权限、发布流程都需要更强工具支持,否则会变慢、变乱。
常见工具包括 pnpm workspace、Turborepo、Nx、Rush 等。一个成熟 Monorepo 需要处理任务编排、缓存、增量构建、依赖拓扑、版本发布和 CI 优化。
选型判断:当多个项目共享大量基础能力,并且团队有能力建设工具链时,Monorepo 能提高协作效率;否则可能只是把复杂度集中到一个仓库里。
追问方向:
pnpm workspace如何管理依赖?- Monorepo 和多仓库如何取舍?
- 什么是增量构建和远程缓存?
评分参考:
- 1 分:知道 Monorepo 是单仓多项目。
- 2 分:能说出代码复用和统一规范。
- 3 分:能指出构建和发布复杂度。
- 4 分:能结合团队规模、CI、缓存、版本策略选型。
八、性能优化
Q41:前端性能优化可以从哪些方向入手?【中等】
考察点:系统性思考、指标意识、优化闭环。
参考答案:
性能优化可以按链路拆解:网络加载、资源体积、渲染执行、交互响应、缓存策略、监控反馈。
- 网络层面:CDN、HTTP/2/3、缓存、预加载、减少请求、压缩。
- 资源层面:代码分割、Tree Shaking、图片压缩、现代格式、字体子集化。
- 渲染层面:减少重排重绘、虚拟列表、懒加载、避免长任务。
- 交互层面:关注输入延迟、主线程占用、防抖节流、Web Worker。
更专业的回答要带指标。常见 Web Vitals 包括 LCP、INP、CLS:
- LCP:最大内容绘制
- INP:交互响应
- CLS:布局稳定性
优化原则:不是凭感觉做,而是先采集指标,再定位瓶颈,最后验证效果。
追问方向:
- LCP、INP、CLS 分别代表什么?
- 如何定位首屏慢?
- 性能优化如何避免只优化实验室分数?
评分参考:
- 1 分:能说出压缩、懒加载等点。
- 2 分:能按加载、渲染、交互分类。
- 3 分:能提到 Web Vitals 指标。
- 4 分:能建立监控、定位、优化、验证闭环。
Q42:什么是重排和重绘?如何减少它们?【简单】
考察点:浏览器渲染、DOM 操作优化。
参考答案:
- 重排(回流):元素几何信息发生变化后,浏览器需要重新计算布局。例如修改宽高、位置、字体大小、插入删除 DOM、读取某些布局属性等。
- 重绘:元素外观变化但布局不变,例如颜色、背景、阴影变化。
重排通常比重绘成本更高,因为它可能影响后续节点甚至整个页面布局。
减少方式:
- 批量修改 DOM,避免频繁读写布局属性交替发生。
- 使用
class一次性改变样式。 - 对复杂动画使用
transform和opacity。 - 避免在循环中反复读取
offsetWidth后又写样式。 - 长列表使用虚拟滚动。
定位工具:Performance 面板、Layout Shift、长任务、渲染帧率等。
追问方向:
- 哪些 CSS 属性更容易触发布局?
- 为什么
transform动画更流畅? - 什么是强制同步布局?
评分参考:
- 1 分:知道重排比重绘影响大。
- 2 分:能区分布局变化和外观变化。
- 3 分:能说出批量 DOM、transform 优化。
- 4 分:能结合浏览器性能面板定位问题。
Q43:首屏加载慢如何排查和优化?【中等】
考察点:问题拆解、性能工具、工程经验。
参考答案:
排查首屏慢要先看指标和瀑布图,而不是直接猜。可以用 Lighthouse、Performance、Network、线上 RUM 监控观察 TTFB、资源加载、JS 执行、LCP 元素、长任务等。
优化方向:
- 服务端降低 TTFB,开启 CDN 和缓存。
- 压缩 HTML/CSS/JS,开启 gzip 或 br。
- 首屏 JS 代码分割,非关键模块懒加载。
- 关键 CSS 内联或优先加载。
- 图片使用 WebP/AVIF、响应式尺寸、懒加载。
- 字体使用
font-display。 - 减少首屏接口数量,必要时做 SSR、SSG 或接口聚合。
针对性优化:如果 LCP 元素是大图,要重点优化图片加载优先级、尺寸和格式。如果瓶颈是 JS 执行长任务,要拆包、延迟非关键脚本、减少同步计算。
追问方向:
- 如何确认 LCP 元素是什么?
- SSR 一定能提升首屏吗?
preload、prefetch、preconnect的区别?
评分参考:
- 1 分:能说出压缩、懒加载。
- 2 分:能使用 Network 看资源加载。
- 3 分:能按 TTFB、资源、执行、渲染拆解。
- 4 分:能针对 LCP 元素和长任务给出具体策略。
Q44:虚拟列表是什么?适合解决什么问题?【中等】
考察点:长列表渲染、滚动性能、DOM 数量控制。
参考答案:
虚拟列表用于渲染超长列表时,只渲染可视区域附近的少量 DOM,而不是一次性渲染全部数据。用户滚动时,根据滚动位置动态计算应该显示哪些项,并用占位高度维持滚动条长度。
适用场景:几千、几万条数据的列表、日志、表格、聊天记录等。
核心收益:
- 减少 DOM 节点数量
- 降低初始渲染成本
- 减少滚动时布局压力
实现要点:
- 固定高度:通过
scrollTop / itemHeight快速算出起始索引。 - 动态高度:需要记录已测量高度、估算未测量高度,复杂度更高。
代价:SEO 不友好、浏览器查找只能查已渲染内容、动态高度处理复杂、无障碍和键盘导航需要额外处理。
追问方向:
- 固定高度和动态高度虚拟列表有什么区别?
- 如何避免滚动白屏?
- 表格虚拟滚动有哪些额外难点?
评分参考:
- 1 分:知道只渲染可视区域。
- 2 分:能说出减少 DOM 数量。
- 3 分:能解释滚动位置和索引计算。
- 4 分:能分析动态高度、缓冲区、可访问性等问题。
九、项目设计与前端架构
Q45:如何设计一个通用请求库?【中等】
考察点:接口封装、错误处理、可维护性。
参考答案:
通用请求库通常需要封装:
- 基础配置 :
baseURL、timeout、headers。 - 请求拦截:注入 token、traceId、语言环境等。
- 响应拦截:统一解析业务状态码、处理未登录、权限不足、服务端错误。
- 错误处理:区分网络错误、超时、HTTP 错误、业务错误,不能全部简单弹一个"请求失败"。
- 并发场景:token 过期后多个请求同时返回 401,应该避免同时刷新 token,可以做刷新队列。
- 取消请求 :使用
AbortController取消组件卸载后的请求,避免无效setState。
TypeScript 支持:请求库应支持泛型返回值,让调用方获得类型提示,但同时不能忽略运行时数据校验。
追问方向:
- 如何处理 token 无感刷新?
- 请求取消和防重复提交怎么做?
- 如何统一处理文件下载接口?
评分参考:
- 1 分:能封装 baseURL 和 token。
- 2 分:能说出请求/响应拦截。
- 3 分:能区分错误类型和登录失效。
- 4 分:能处理并发刷新、取消请求、类型和监控。
Q46:后台管理系统的权限设计怎么做?【中等】
考察点:业务建模、路由权限、按钮权限。
参考答案:
后台权限通常包括用户、角色、权限点、菜单、路由、按钮、数据范围。常见模型是 RBAC:用户绑定角色,角色绑定权限。
前端要处理三层权限:
- 路由权限:根据用户权限生成可访问路由,未授权访问跳转 403 或登录页。
- 菜单权限:只展示用户可见菜单。
- 按钮/操作权限:例如新增、编辑、删除、导出。
数据权限:通常应由后端控制,前端只能做展示层辅助,不能把安全完全交给前端。即使前端隐藏了按钮,用户仍可能手动请求接口,所以接口层必须校验权限。
工程实现:
- 登录后获取用户信息和权限列表。
- 前端将权限点映射为指令、Hook 或组件,用于控制按钮展示。
- 保留兜底路由和异常页。
追问方向:
- 动态路由和静态路由过滤各有什么优缺点?
- 按钮权限如何封装?
- 为什么数据权限不能只在前端做?
评分参考:
- 1 分:知道根据角色控制页面。
- 2 分:能说出菜单和路由权限。
- 3 分:能补充按钮权限和后端校验。
- 4 分:能设计完整 RBAC、异常页、权限点封装。
Q47:如何设计一个组件库?【偏难】
考察点:设计系统、组件 API、工程体系。
参考答案:
组件库不是把项目里的组件复制出来就结束,它需要稳定的设计规范、API 设计、主题能力、文档、测试和发布流程。
设计层面:
- 定义颜色、字号、间距、圆角、阴影、动效等 Design Token,并和设计稿保持一致。
- 组件层面关注受控/非受控、插槽或 render props、事件命名、可访问性、国际化、暗色模式、表单联动等。
工程层面:
- 支持按需引入、Tree Shaking、类型声明、样式隔离、版本管理、变更日志、单元测试、视觉回归测试和文档站。
- 复杂组件还要考虑键盘操作、焦点管理、弹层层级、滚动锁定等细节。
目标:降低业务开发重复成本,同时保持一致体验,而不是制造一套难以维护的新框架。
追问方向:
- Button 组件 API 如何设计?
- 组件库如何支持主题定制?
- 如何保证组件升级不破坏业务?
评分参考:
- 1 分:知道组件库用于复用。
- 2 分:能说出基础组件和文档。
- 3 分:能考虑主题、按需引入、类型。
- 4 分:能覆盖设计系统、测试、发布、兼容性和可访问性。
Q48:项目中如何做错误监控和日志上报?【中等】
考察点:稳定性建设、线上排查、监控体系。
参考答案:
前端错误监控一般包括:
- JS 运行时错误
- Promise 未捕获错误
- 资源加载错误
- 接口错误
- 框架错误边界
- 白屏检测
- 性能指标和用户行为链路
浏览器层面 :监听 window.onerror、unhandledrejection、资源 error 事件。
框架层面:
- React:使用
Error Boundary捕获渲染阶段错误。 - Vue:配置
errorHandler。
上报内容:
- 错误信息、堆栈、页面 URL、用户标识、设备信息、浏览器版本、版本号、source map 映射、最近操作轨迹等。
重要:没有版本号和 source map,线上压缩代码的堆栈很难定位。同时要注意采样、去重、隐私脱敏和上报失败兜底。
追问方向:
- 如何捕获 Promise 未处理错误?
- source map 上传后如何保护源码?
- 白屏检测可以怎么做?
评分参考:
- 1 分:知道需要捕获错误。
- 2 分:能说出
onerror和接口错误。 - 3 分:能补充版本号、source map、用户环境。
- 4 分:能考虑采样、去重、隐私和稳定性闭环。
十、编码题与算法
Q49:手写防抖函数。【简单】
考察点:闭包、定时器、参数和 this。
基础实现:
javascript
function debounce(fn, delay = 300) {
let timer = null;
return function (...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
这里通过闭包保存 timer。每次触发时清除上一次定时器,再重新设置。只有停止触发超过指定时间后,函数才会执行。
扩展 :可以补充立即执行、取消方法、返回值处理。注意
fn.apply(this, args)是为了保留调用时的this和参数。
追问方向:
- 如何支持第一次立即执行?
- 如何给防抖函数增加
cancel方法? - React 中使用防抖为什么要注意函数引用稳定?
评分参考:
- 1 分:能写出定时器延迟。
- 2 分:能清除上一次定时器。
- 3 分:能保留参数和 this。
- 4 分:能扩展立即执行、取消和框架场景。
Q50:手写节流函数。【简单】
考察点:高频事件控制、时间戳、定时器。
时间戳版本:
javascript
function throttle(fn, delay = 300) {
let lastTime = 0;
return function (...args) {
const now = Date.now();
if (now - lastTime >= delay) {
lastTime = now;
fn.apply(this, args);
}
};
}
这个版本第一次会立即执行,之后在间隔时间内忽略触发。定时器版本可以保证最后一次触发也有机会执行,更适合需要尾调用的场景。
两种实现差异:时间戳节流响应更直接,但可能丢掉最后一次;定时器节流能控制尾部执行,但实现稍复杂。真实业务里要根据交互选择 leading 和 trailing。
追问方向:
- 如何同时支持 leading 和 trailing?
- 滚动事件里节流间隔设置多少合适?
requestAnimationFrame能否用于节流?
评分参考:
- 1 分:能限制执行频率。
- 2 分:能写出时间戳实现。
- 3 分:能说明时间戳和定时器差异。
- 4 分:能处理首尾执行和动画场景。
Q51:手写 Promise.all。【中等】
考察点:Promise、并发、顺序保持、错误处理。
javascript
function promiseAll(iterable) {
return new Promise((resolve, reject) => {
const list = Array.from(iterable);
const result = [];
let count = 0;
if (list.length === 0) {
resolve([]);
return;
}
list.forEach((item, index) => {
Promise.resolve(item).then(
value => {
result[index] = value;
count += 1;
if (count === list.length) {
resolve(result);
}
},
reject
);
});
});
}
关键点:
- 入参可能不是 Promise,所以要用
Promise.resolve包装。 - 结果顺序要和输入顺序一致,不能按完成顺序 push。
- 只要任意一个失败,整体就失败。
追问方向:
Promise.allSettled和Promise.all有什么区别?- 如何实现并发数限制?
- 空数组传给
Promise.all返回什么?
评分参考:
- 1 分:知道全部成功才 resolve。
- 2 分:能处理失败 reject。
- 3 分:能保持结果顺序。
- 4 分:能处理非 Promise 值、空数组、并发限制延伸。
Q52:手写深拷贝,要求处理循环引用。【中等】
考察点:递归、引用缓存、边界类型。
javascript
function deepClone(value, cache = new WeakMap()) {
if (value === null || typeof value !== 'object') return value;
if (cache.has(value)) return cache.get(value);
if (value instanceof Date) return new Date(value);
if (value instanceof RegExp) return new RegExp(value);
const result = Array.isArray(value) ? [] : {};
cache.set(value, result);
Reflect.ownKeys(value).forEach(key => {
result[key] = deepClone(value[key], cache);
});
return result;
}
WeakMap用于记录已经克隆过的对象,解决循环引用问题。Reflect.ownKeys可以拿到字符串 key 和 Symbol key,比Object.keys覆盖范围更大。
注意:这仍然不是工业级完整实现,因为还没有处理 Map、Set、函数、属性描述符、原型链等复杂情况。面试里能讲清边界,比假装"完整实现"更重要。
追问方向:
- 为什么用
WeakMap而不是Map? - 如何处理
Map和Set? - 如何保留对象原型和属性描述符?
评分参考:
- 1 分:能递归复制对象。
- 2 分:能区分数组和对象。
- 3 分:能用 WeakMap 处理循环引用。
- 4 分:能主动说明边界类型和工业实现复杂度。
Q53:实现数组扁平化。【简单】
考察点:递归、数组方法、边界处理。
递归版本:
javascript
function flatten(arr) {
const result = [];
arr.forEach(item => {
if (Array.isArray(item)) {
result.push(...flatten(item));
} else {
result.push(item);
}
});
return result;
}
也可以使用原生 flat(Infinity):
javascript
const result = arr.flat(Infinity);
面试建议:先写递归,再补充原生 API。递归版本简单直接,但极深嵌套可能造成调用栈溢出,可以用栈迭代方式优化。
追问方向:
- 如何按指定深度扁平化?
- 如何用
reduce实现? - 极深数组如何避免递归爆栈?
评分参考:
- 1 分:知道
flat。 - 2 分:能写递归实现。
- 3 分:能支持指定深度。
- 4 分:能考虑极深嵌套和性能。
Q54:如何实现 LRU 缓存?【偏难】
考察点:数据结构、Map、缓存淘汰策略。
LRU 是 Least Recently Used,最近最少使用缓存。容量满时,淘汰最久没有被访问的元素。
JavaScript 中可以借助 Map 的插入顺序实现:
javascript
class LRUCache {
constructor(capacity) {
this.capacity = capacity;
this.map = new Map();
}
get(key) {
if (!this.map.has(key)) return -1;
const value = this.map.get(key);
this.map.delete(key);
this.map.set(key, value);
return value;
}
put(key, value) {
if (this.map.has(key)) {
this.map.delete(key);
}
this.map.set(key, value);
if (this.map.size > this.capacity) {
const oldestKey = this.map.keys().next().value;
this.map.delete(oldestKey);
}
}
}
访问或更新某个 key 后,把它移动到 Map 末尾,表示最近使用。容量超出时删除 Map 的第一个 key。
追问方向:
- 为什么 Map 能保持插入顺序?
- 如果不用 Map,如何用哈希表加双向链表实现?
- LRU 在前端有哪些应用?
评分参考:
- 1 分:知道淘汰最近最少使用。
- 2 分:能用 Map 实现基本功能。
- 3 分:能保证 get/put 后更新顺序。
- 4 分:能解释哈希表加双向链表的 O(1) 思路。
十一、移动端与跨端
Q55:移动端 1px 边框问题是什么?如何解决?【中等】
考察点:DPR、CSS 像素、移动端经验。
问题原因 :移动端 1px 问题源于设备像素比 DPR。CSS 中的 1px 是 CSS 像素,在 DPR 为 2 或 3 的高清屏上,会对应多个物理像素,视觉上可能显得比设计稿更粗。
常见方案 :使用伪元素加 transform 缩放:
css
.hairline {
position: relative;
}
.hairline::after {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 1px;
background: #ddd;
transform: scaleY(0.5);
transform-origin: 0 100%;
}
其他方案:viewport 缩放、border-image、box-shadow、SVG 背景等。实际项目中伪元素方案较常见,兼容性和可控性都比较好。
追问方向:
- CSS 像素和物理像素有什么区别?
- DPR 如何获取?
- 安全区域
env(safe-area-inset-bottom)用于什么场景?
评分参考:
- 1 分:知道高清屏边框会粗。
- 2 分:能说出 DPR 原因。
- 3 分:能写出伪元素缩放方案。
- 4 分:能比较多种方案并结合安全区域处理。
Q56:移动端适配有哪些方案?rem、vw、响应式分别怎么选?【中等】
考察点:移动端布局、设计稿适配、响应式思维。
常见方案:
- rem 方案 :根据设计稿宽度动态设置根元素
font-size,再把px转成rem。优点是兼容性好,适合传统 H5 活动页;缺点是全局依赖根字号,和第三方组件混用时要小心。 - vw 方案 :直接基于视口宽度计算,写法简单,适合按屏幕比例缩放的页面。但在超宽、折叠屏、横屏场景要配合
max-width或断点限制。 - 响应式布局 :更关注不同设备下的信息结构变化,不只是等比缩放。对于后台系统、内容站、复杂表单,通常需要断点布局、弹性容器和组件级适配,而不是单纯
rem。
追问方向:
- 为什么不能所有页面都等比缩放?
- 横屏和折叠屏如何处理?
- CSS Container Query 能解决什么问题?
评分参考:
- 1 分:知道 rem 或 vw。
- 2 分:能说明按视口适配。
- 3 分:能比较 rem 和 vw。
- 4 分:能从内容结构、断点、容器查询角度设计响应式。
Q57:小程序、H5、App 跨端开发的主要差异是什么?【中等】
考察点:跨端认知、运行环境、技术选型。
- H5:运行在浏览器或 WebView 中,能力受浏览器和容器限制。
- 小程序:运行在平台提供的双线程架构中,逻辑层和渲染层分离,通过桥通信,API、包体积、路由、组件和样式都有平台约束。
- App 原生:能力最强,但开发成本也更高。
跨端框架如 Taro、uni-app、React Native、Flutter 试图通过一套代码覆盖多个端,但没有银弹。多端差异包括组件能力、生命周期、路由、样式兼容、性能、原生 API、审核发布、调试工具等。
选型要看业务目标:营销页或轻交互,H5 足够;依赖微信生态能力,小程序更合适;追求复杂交互、稳定性能和深度原生能力,可能需要 RN、Flutter 或原生开发。
追问方向:
- 小程序为什么有包体积限制?
- RN 和 Flutter 的渲染机制有什么差异?
- 跨端项目如何处理平台差异代码?
评分参考:
- 1 分:知道不同端运行环境不同。
- 2 分:能说出 H5 和小程序限制。
- 3 分:能比较跨端框架优缺点。
- 4 分:能基于业务、性能、生态和团队能力选型。
十二、AI、前沿技术与综合潜力
Q58:AI 工具会如何改变前端开发?前端工程师该保留什么核心能力?【简单】
考察点:技术视野、职业判断、学习能力。
参考答案:
AI 工具已经能帮助前端完成代码生成、组件样板、测试用例、文档总结、错误排查、正则和脚本编写等任务。它提升的是执行效率,但不能替代工程判断。
前端工程师仍然需要保留几类核心能力:
- 需求理解和产品判断:知道用户真正要解决什么问题。
- 基础能力:理解浏览器、网络、JS、框架和工程化,不被生成代码牵着走。
- 架构和质量意识:能评估代码是否可维护、可测试、可演进。
- 审美和体验判断:能发现交互细节和性能问题。
面试表达:不要简单回答"AI 不会替代人"。更好的表达是:AI 会替代一部分重复编码,但会放大工程师之间在问题定义、系统设计、代码审查和落地负责上的差距。
追问方向:
- 你平时如何使用 AI 辅助开发?
- AI 生成代码如何做质量检查?
- 你认为前端未来三年会有哪些变化?
评分参考:
- 1 分:知道 AI 能辅助写代码。
- 2 分:能说出提效场景。
- 3 分:能指出仍需工程判断。
- 4 分:能结合质量、架构、体验和职业发展表达独立观点。
Q59:Server Components、SSR、SSG、CSR 有什么区别?【偏难】
考察点:渲染模式、首屏、SEO、架构趋势。
参考答案:
- CSR(客户端渲染):浏览器下载 JS 后在客户端生成页面。优点是交互灵活、部署简单,缺点是首屏和 SEO 可能受影响。
- SSR(服务端渲染):每次请求时服务端生成 HTML 返回给浏览器,首屏和 SEO 更好,但服务端压力更大,也要处理 hydration 成本。
- SSG(静态生成):在构建时生成 HTML,适合内容相对稳定的页面,如文档、博客、营销页。它性能好、部署简单,但实时性较弱。
- React Server Components(RSC):更细粒度的服务端组件模型。部分组件只在服务端执行,不把对应 JS 发送到客户端,可以减少包体积,并更直接访问服务端资源。但它改变了组件边界和数据获取方式,需要框架支持和团队理解成本。
追问方向:
- hydration 是什么?为什么会有成本?
- SSR 一定比 CSR 快吗?
- RSC 中客户端组件和服务端组件如何划分?
评分参考:
- 1 分:知道 CSR、SSR 基本区别。
- 2 分:能说出 SSG 适合静态内容。
- 3 分:能解释 hydration 成本。
- 4 分:能理解 RSC 的包体积、数据访问和组件边界变化。
Q60:如果让你介绍一个最有代表性的项目,你该怎么讲?【简单】
考察点:项目表达、问题意识、个人贡献。
参考答案:
项目介绍不要只背技术栈。一个清晰结构可以是:
- 项目背景:这是什么项目?
- 业务目标:为了解决什么问题?
- 你的角色:你负责什么?
- 核心难点:技术或业务上最难的点是什么?
- 技术方案:你是怎么解决的?为什么这样设计?
- 落地结果:上线后带来了什么变化?如何衡量?
- 复盘总结:如果重做一次,你会怎么改进?
示例:"这个项目是一个面向运营人员的配置平台,目标是把原来需要研发改代码的活动配置流程改成可视化搭建。我负责前端编辑器和预览链路。难点是组件拖拽、配置实时预览、表单校验和发布前校验。方案上我把页面抽象成 schema,用组件注册表渲染,编辑区和预览区共享一份状态。上线后活动配置从半天缩短到几十分钟,也减少了重复研发投入。"
核心:面试官更关心你有没有真正参与解决问题,而不是项目用了多少热门技术。你要主动讲清楚为什么这样设计、遇到什么坑、如何权衡、结果如何衡量。
追问方向:
- 项目里最难的问题是什么?
- 如果重做一次,你会怎么改?
- 这个项目如何保证质量和稳定性?
评分参考:
- 1 分:能说明项目做什么。
- 2 分:能说出自己负责的模块。
- 3 分:能讲清难点和方案。
- 4 分:能讲出权衡、数据结果和复盘思考。
最后:应届生面试最容易丢分的 8 件事
- 只背结论,不讲原因。比如只说"用防抖",但讲不出为什么用、怎么实现、有什么边界。
- 只讲技术名词,不讲业务场景。面试官更想知道你能不能把技术用到项目里。
- 项目介绍像流水账。项目经历要突出你的角色、难点、方案和结果。
- 不承认边界。不会的问题可以说"我目前理解到这里",再给出推理方向,比硬编更好。
- 忽视浏览器和网络基础。前端不是只会框架,HTML、CSS、JS、HTTP 仍然是底盘。
- 过度包装项目。面试官连续追问三层,很容易判断你是不是真做过。
- 代码题不处理边界。空数组、异常输入、this、参数、返回顺序、异步失败都可能是加分点。
- 没有复盘意识。优秀候选人会主动讲"当时为什么这么做,现在看还有什么可以改"。
一套 7 天冲刺复习节奏
- 第 1 天:HTML、CSS、浏览器渲染。目标是把布局、盒模型、BFC、缓存、跨域讲清楚。
- 第 2 天:JavaScript 核心。重点是原型链、闭包、事件循环、Promise、this、深浅拷贝。
- 第 3 天:TypeScript。重点是泛型、工具类型、unknown、接口建模和项目类型实践。
- 第 4 天:React 或 Vue 主框架。不要两个都平均用力,优先把自己简历里写的框架讲深。
- 第 5 天:工程化与性能。准备构建工具、规范工具、缓存策略、首屏优化、监控上报。
- 第 6 天:代码题。防抖、节流、Promise.all、深拷贝、数组扁平化、LRU 至少手写一遍。
- 第 7 天:项目复盘和模拟面试。把每个项目整理成 3 分钟版本和 8 分钟版本,准备追问。
结语
对 2026 届应届生来说,面试官通常不会要求你像资深工程师一样经历过大型系统的所有坑,但会非常关注三件事:基础是否扎实,表达是否清楚,遇到未知问题时是否能继续推理。
真正有效的准备,不是把题库背到一字不差,而是把每个知识点都变成你能解释、能举例、能联系项目、能承认边界的表达。
如果你现在还有很多不会的题,不用慌。把问题拆开,按模块补齐,每天口述几道题,再回到项目里找对应场景。面试竞争到最后,拼的不是谁收藏的资料最多,而是谁能在 40 分钟里稳定展示自己的基本功、思考方式和成长速度。
祝你 2026 届校招顺利,拿到真正适合自己的前端 offer。