以下是更贴合企业面试实际、分维度且附带答题思路 + 避坑点的前端面试题,覆盖基础、进阶、框架、工程化等核心模块,适配不同面试场景:
一、HTML/CSS 高频题(基础必答)
-
viewport 视口的核心属性有哪些?如何适配移动端?
- 答题思路:先说明 viewport 是移动端适配核心,核心属性(width=device-width、initial-scale=1.0、user-scalable=no 等);再讲适配方案(rem/vw/vh、媒体查询、flex 布局),结合实际项目举例(如用 postcss-px-to-viewport 自动转 px 为 vw)。
- 避坑点:不要只说属性,要讲清 "为什么这些属性能解决移动端适配"。
-
CSS 实现一个 1:1 自适应正方形?至少两种方法。
- 答题思路:① 利用 padding-top/bottom(百分比基于父元素宽度):
width: 100%; padding-top: 100%; height: 0;;② 利用 aspect-ratio 属性(CSS3 新特性):width: 100%; aspect-ratio: 1/1;; - 避坑点:不要忽略 "自适应",避免写固定宽高的方案。
- 答题思路:① 利用 padding-top/bottom(百分比基于父元素宽度):
-
CSS 优先级冲突时,!important 一定生效吗?为什么?
- 答题思路:不一定;优先级规则:!important > 内联样式 > ID > 类 / 伪类 / 属性 > 元素 / 伪元素;但如果两个样式都加了!important,仍按 "后定义覆盖前定义""选择器优先级高的生效",且内联样式 +!important 优先级高于外部样式 +!important。
- 避坑点:不要说 "!important 是最高优先级,一定生效",要讲清例外场景。
二、JavaScript 核心题(进阶重点)
-
手写实现 Promise.all,要求处理异常(失败一个全部失败)。
-
答题思路: javascript
运行
function myPromiseAll(promises) { return new Promise((resolve, reject) => { if (!Array.isArray(promises)) return reject(new TypeError('参数必须是数组')); const result = []; let count = 0; promises.forEach((p, index) => { Promise.resolve(p).then(res => { result[index] = res; count++; if (count === promises.length) resolve(result); }).catch(err => reject(err)); // 一个失败直接reject }); }); } -
避坑点:要处理 "非 Promise 类型的参数"(用 Promise.resolve 包装),且结果数组的顺序要和入参数组一致。
-
-
解释什么是防抖节流?分别用在什么场景?手写防抖函数(带立即执行版)。
-
答题思路:
-
防抖:触发后延迟 n 秒执行,期间再次触发则重置延迟(场景:搜索框输入、窗口 resize);
-
节流:触发后 n 秒内只能执行一次(场景:滚动加载、按钮防连点);
-
手写立即执行版防抖: javascript
运行
function debounce(fn, delay, immediate = false) { let timer = null; return function(...args) { if (timer) clearTimeout(timer); if (immediate && !timer) fn.apply(this, args); timer = setTimeout(() => { if (!immediate) fn.apply(this, args); timer = null; }, delay); }; }
-
-
避坑点:不要混淆防抖节流的场景,且要处理 this 指向和参数传递。
-
-
为什么 0.1 + 0.2 !== 0.3?如何解决这个问题?
- 答题思路:JS 采用 IEEE 754 双精度浮点数存储,0.1 和 0.2 二进制是无限循环小数,存储时精度丢失;解决方法:① 转整数计算((0.110 + 0.210)/10);② 用 toFixed 保留小数(注意 toFixed 是四舍五入);③ 用第三方库(如 decimal.js)。
- 避坑点:不要只说 "精度丢失",要讲清底层存储原理。
三、框架题(Vue/React 二选一)
Vue 方向
-
Vue3 的 setup 语法糖中,defineProps/defineEmits 为什么不需要导入?
- 答题思路:Vue3 对
<script setup>做了语法糖优化,defineProps/defineEmits 是编译器宏(compiler macros),在编译阶段会被处理,无需手动导入;且这些宏只能在 setup 语法糖中使用,作用域仅限于当前组件。 - 避坑点:不要说 "是全局变量",要强调 "编译器层面的特殊处理"。
- 答题思路:Vue3 对
-
Vue 的 keep-alive 原理是什么?如何缓存指定组件 / 排除指定组件?
- 答题思路:keep-alive 是内置组件,会缓存不活动的组件实例(而非销毁),原理是将组件实例存入 cache 对象,再次渲染时直接取缓存;通过 include(缓存指定组件)、exclude(排除指定组件)、max(最大缓存数)控制,属性值支持字符串、数组、正则。
- 避坑点:不要忽略 "max 属性的作用"(超过数量会按 LRU 策略删除最久未使用的缓存)。
React 方向
-
React 的 useMemo 和 useCallback 的区别?分别解决什么问题?
- 答题思路:
- useMemo:缓存计算结果(避免每次渲染重复计算),返回值是计算结果;
- useCallback:缓存函数引用(避免子组件因函数引用变化重复渲染),返回值是函数;
- 避坑点:不要滥用(比如简单计算 / 函数无需缓存,会增加内存开销),要讲清 "依赖数组为空时,仅首次渲染缓存"。
- 答题思路:
-
React 中什么是虚拟 DOM Diff 算法?key 的作用是什么?
- 答题思路:Diff 算法是对比新旧虚拟 DOM 树,找出差异部分只更新真实 DOM;key 是列表渲染时的唯一标识,帮助 React 识别哪些元素被添加 / 删除 / 移动,避免错误复用节点;若不用 key,React 会采用 "就地更新" 策略,可能导致状态错乱。
- 避坑点:不要说 "key 用 index 更好",要说明 index 作为 key 在列表排序 / 增删时的问题。
四、工程化 & 性能优化题(中高级必答)
-
Vite 比 Webpack 快的核心原因是什么?
- 答题思路:① 构建原理不同:Vite 基于 ES Module 原生支持,开发时无需打包(按需编译),Webpack 开发时需打包成 bundle;② 依赖预构建:Vite 用 esbuild 预构建第三方依赖(esbuild 是 Go 编写,比 JS 快 10-100 倍),Webpack 用 JS 处理依赖;③ 热更新:Vite 热更新只更新修改的模块,Webpack 热更新可能重新打包整个模块。
- 避坑点:不要只说 "Vite 不用打包",要区分 "开发环境" 和 "生产环境"(Vite 生产环境仍用 Rollup 打包)。
-
前端性能优化中,如何诊断首屏加载慢的问题?至少 3 种方法。
- 答题思路:① 用 Chrome DevTools 的 Performance 面板录制加载过程,分析长任务、资源加载耗时;② 用 Lighthouse 生成性能报告,查看 FCP(首次内容绘制)、LCP(最大内容绘制)等指标;③ 查看 Network 面板,分析资源大小、加载顺序、是否有冗余请求;
- 避坑点:不要只说优化方案,要先讲 "诊断方法",再对应优化(比如 Network 看到大图片,就做图片压缩 / 懒加载)。
五、场景题(考察实战能力)
-
项目中遇到 "白屏问题",你会如何排查和解决?
- 答题思路:① 排查步骤:看控制台报错(JS 报错 / 资源加载失败)→ 检查网络(接口返回异常 / CDN 资源失效)→ 检查渲染逻辑(首屏数据未加载完成就渲染)→ 检查兼容性(语法不兼容 / API 不支持);② 解决方法:JS 报错(修复代码 / 增加容错)、资源加载失败(降级方案 / 备用 CDN)、数据问题(骨架屏 / 加载态)、兼容性(Babel 转译 / Polyfill)。
-
如何实现一个前端权限控制系统?(按钮级)
- 答题思路:① 权限设计:分角色(admin / 普通用户)、分权限点(按钮 / 接口),后端返回当前用户权限列表;② 实现方式:Vue 中用自定义指令(v-permission),React 中用高阶组件 / Hooks,判断当前权限列表是否包含按钮权限,不包含则隐藏 / 禁用按钮;③ 补充:路由层面增加守卫,避免无权限访问页面。