这是2025年10月份我离职,重新找工作时将每次面试中的八股文记录收集起来并让AI梳理成文档。方便自己以后查看,也希望能帮到正在找工作的jym,并祝你们早日找到工作~~~
1. CSS 定位有哪些
概念与行为
position: static:默认,不脱离文档流,top/left无效。position: relative:仍在文档流中,但可以相对自身偏移(视为“视觉偏移”),不影响兄弟元素布局占位。position: absolute:脱离文档流,相对于最近的已定位祖先(非static)进行定位;若无定位祖先则相对于根(<html>/viewport)。position: fixed:脱离文档流,相对于视口固定(即滚动不影响),常用于回到顶部/固定头部。position: sticky:粘性定位,在其父容器内依赖滚动位置:在指定阈值前表现为 normal(跟随文档流),超过阈值后变为固定(类似 fixed),需要设置阈值(如top:0)并且父容器不能 overflow:hidden(可能会影响)。
面试精炼回答
“CSS 有 static、relative、absolute、fixed、sticky 五种常用定位。relative 不脱流只是偏移自身,absolute/fixed 脱离文档流;absolute 相对最近的定位祖先,fixed 相对视口,sticky 在滚动临界点切换成固定。”
注意点
absolute元素定位参考链重要(定位上下文是最近的非static祖先)。fixed在移动端与 transform/contain 的上下文结合时可能不是相对于视口(含有 transform 的父级会创建新的层),小心测试。
2. CSS 中的选择器有哪些,并说明它们的优先级(权重)
常见选择器(举例)
- 基础:元素(
div)、通配符(*) - 类:
.class - ID:
#id - 属性:
input[type="text"] - 伪类:
:hover,:nth-child() - 伪元素:
::after,::before - 组合选择器:后代(
a b)、子(a > b)、相邻兄弟(a + b)、通用兄弟(a ~ b) - 复杂:链式类、伪类组合等
优先级规则(权重计算)
通常用四元组表示:(a, b, c, d)
a— 是否为!important(特殊处理,优先级最高)b— 行内样式(style="...")计 1(相当于 1000)c— ID 选择器个数(每个计 100)d— 类、伪类、属性选择器个数(每个计 10)e— 元素、伪元素选择器个数(每个计 1)
示例:#app .card div => 100 + 10 + 1 权重
!important
!important会覆盖正常权重规则(但多个!important比较时仍按优先级比较)。尽量避免使用。
面试精炼回答
“选择器按 specificity 排序:内联 > ID > 类/伪类/属性 > 标签/伪元素;!important 覆盖常规规则。权重通常表示为 1000/100/10/1 的组合。”
3. CSS 中水平垂直居中方式;说说 BFC(块级格式化上下文)
水平 & 垂直居中常用方法
1) Flex(推荐)
.parent {
display: flex;
justify-content: center; /* 水平 */
align-items: center; /* 垂直 */
}
优点:简洁、支持动态尺寸、常用于单个子项或行列布局。
2) Grid(简洁)
.parent {
display: grid;
place-items: center; /* 同时水平垂直 */
}
3) 绝对定位 + transform(脱流情形)
.child {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
适用于固定定位元素或 modal。
4) 行高(单行文本)
.line {
height: 40px;
line-height: 40px;
text-align: center;
}
仅限单行文本居中。
5) table/table-cell 技术(古老)
.parent { display: table; }
.child { display: table-cell; vertical-align: middle; text-align: center; }
4. BFC(块级格式化上下文,Block Formatting Context)
触发 BFC 的常见方式
overflow不为visible(如hidden/auto/scroll)display: flow-rootposition: absolute/fixed(绝对或固定定位的块)display: inline-block/flex/grid(某些情况下触发)float(浮动元素创建 BFC 的子集效果)
BFC 的特性 / 作用
- BFC 内的盒子不会与外部盒子发生 margin 折叠(避免 margin 重叠)。
- BFC 会包含其内部的浮动元素(父容器高度包住浮动子元素),常用于“清除浮动”替代 clearfix。
- BFC 可限制内部元素不影响外部布局(例如不对外部产生浮动影响、避免外部元素重叠)。
使用场景
- 清除浮动:父元素设置
overflow: hidden或display: flow-root。 - 避免 margin 重叠:设置 BFC 可阻止相邻块级元素的 margin 折叠。
- 处理包含浮动子元素导致父高度塌陷问题。
面试精炼回答
“常用居中方式有 flex、grid、绝对定位+transform、行高等。BFC 是块级格式化上下文,一旦触发,内部布局与外部隔离,能解决清除浮动、margin 重叠等问题,常用 overflow:auto 或 display:flow-root 来触发。”
5. CSS 中的盒子模型
两种盒模型
content-box(默认)
元素的 width 表示内容宽度,padding 和 border 在外侧会增加元素总宽度。
border-box
width 包含 padding 和 border(更易于布局)。
通常在项目中统一使用:
* { box-sizing: border-box; }
组成(从内到外)
- content(内容区)
- padding(内边距)
- border(边框)
- margin(外边距,不影响元素的背景/边框)
回流 & 重绘
- 改变 box 大小、位置会触发回流(reflow/layout),开销大。
- 改变颜色等样式触发重绘(repaint),比回流小。
面试精炼回答
“盒模型有 content-box 和 border-box 两种。border-box 把 padding 和 border 包含在 width/height 内,更便于控制元素总尺寸。改动尺寸会触发回流,需尽量避免频繁引起回流的操作。”
6. JavaScript 数据类型
基本类型(Primitive,不可再分)
string、number、boolean、undefined、null、symbol、bigint
注意:typeof null === 'object' 是历史遗留问题。
引用类型(Object)
Object、Array、Function、Date、RegExp、Map、Set、WeakMap、WeakSet等。
判断类型常用方法
typeof:判断基本类型(返回字符串),但对数组/对象区分不足。Array.isArray():判断数组。instanceof:判断构造函数的实例(受原型链影响)。Object.prototype.toString.call(x):[object Type],通用且靠谱。
面试精炼回答
“JS 类型分为基本类型(七种)和引用类型。常见判断用 typeof、Array.isArray、instanceof、Object.toString 等。注意 null 判定与 NaN 等特殊值。”
7. JS / 浏览器 / 前端性能优化(要点与实践)
将优化分为:网络、资源、渲染与代码层面
网络层
- 使用 CDN 分发静态资源。
- 启用压缩:Gzip / Brotli。
- 合理设置缓存策略:
Cache-Control(强缓存)与ETag/Last-Modified(协商缓存)。 - 减少请求数:合并、雪碧图(图标尽量用 iconfont/SVG)。
- 使用 HTTP/2 或 HTTP/3 多路复用。
资源层
- 图片优化:WebP/AVIF,按需尺寸、懒加载(
loading="lazy")。 - 静态资源离线缓存(Service Worker)用于 PWA。
- 代码分割(动态 import、route-level split),减少首屏体积。
渲染层(避免卡顿)
- 减少回流(layout)与重绘(repaint):合并读写 DOM、使用
transform&opacity做动画。 - 使用
requestAnimationFrame做动画帧控制。 - 使用虚拟滚动(large lists)或分片渲染(windowing)。
- 将昂贵计算移到 Web Worker。
JS 代码层
- Tree shaking(移除未用代码)。
- 使用现代构建(esbuild/rollup/webpack5)和压缩(Terser)。
- 减少 polyfills,仅按需引入。
- 减少依赖体积(按需引入 lodash 模块而非整个库)。
加载策略
defer与async的区别:defer保证顺序且在 DOMContentLoaded 前执行;async无序,谁先加载先执行。- 关键资源预加载:
<link rel="preload">、prefetch。
性能监控
- 使用 Lighthouse、WebPageTest、Chrome DevTools Coverage、RUM(Real User Monitoring)收集真实用户数据。
面试精炼回答
“性能优化从网络到渲染多方面入手:压缩与缓存、CDN、代码分割、按需加载、图片优化;渲染层避免回流重绘、使用 rAF、Web Worker,结合监控工具持续优化。”
8. v-if 和 v-show
区别
v-if:条件渲染,满足条件时创建 DOM,不满足则销毁 DOM —— 首次渲染成本高但切换成本高(因为每次都 mount/unmount)。v-show:不移除 DOM,仅控制display样式显示/隐藏 —— 首次渲染就存在元素(开销),切换显示隐藏成本低。
何时用
- 频繁切换显示:用
v-show(比如切换 tab)。 - 低频或条件少:用
v-if(如权限控制、表单切换)。
面试精炼回答
“v-if 是销毁/创建元素,v-show 用 display 控制。频繁切换用 v-show、不常切换用 v-if。”
9. Vue 中 key 的作用
作用
- 提供虚拟 DOM diff 的稳定标识,帮助框架在列表更新时正确复用或移动节点。
- 防止错误复用组件实例(例如带内部状态的子组件在重排时会复用错误状态)。
典型问题
列表重排(如 1,2,3 → 3,2,1)若没有 key,渲染器可能做最小 DOM 操作但造成组件实例与数据错位;加上唯一 key 后能正确重新挂载/移动。
面试精炼回答
“key 是给虚拟 DOM 一个稳定唯一标识,优化 diff 并保证组件实例正确映射到数据上,避免状态混淆。”
10. 浏览器跨域,如何处理跨域(本地 & 服务器端)
原因
浏览器同源策略(协议、域名、端口三者任一不同就被视为跨域)为安全限制。
常见解决方案
服务器端(推荐)
-
CORS(跨域资源共享) :在服务器端设置响应头,例如:
Access-Control-Allow-Origin: https://yourdomain.com Access-Control-Allow-Methods: GET,POST,OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization若需要携带 cookie:
Access-Control-Allow-Credentials: true,且Access-Control-Allow-Origin不能为*。 -
代理 (Reverse Proxy) :通过 Nginx 将
/api代理到后端域,使浏览器认为同源。适合线上与本地统一处理。 -
JSONP(仅 GET) :利用
<script>标签不受同源限制,适用于老旧场景,不推荐新项目。
本地开发
- 开发服务器代理(如 webpack-dev-server、Vite 的
proxy):在本地 devServer 上配置代理把请求转发到后端,解决跨域开发问题。 - Mock 后端 / 使用 CORS:在后端临时允许本地 origin 或开启 CORS。
其他技术
- postMessage(跨 window/frame 安全通信)
- WebSocket(不是受同源限制的常规 HTTP 请求)
- 服务器端中转:后端自身向第三方请求数据,再返回给前端(后端同源)。
面试精炼回答
“跨域源于浏览器同源策略。最佳做法是在服务器端通过 CORS 或反向代理解决;开发时使用 devServer 代理。JSONP 仅限 GET,安全性与功能有限。”
11. JS 和 TS 的区别
核心差异
- 类型系统:JS 是动态弱类型,TS 是静态强类型(编译期类型检查)。
- 开发体验:TS 提供更强的 IDE 智能提示、提前捕获错误、接口与泛型等。
- 编译/运行:TS 在编译阶段会把代码转成 JS,再执行(不会在运行时强制类型检查)。
- 学习成本:TS 增加类型学习曲线,但在大型项目能显著减少错误、提高可维护性。
面试精炼回答
“TS 是 JS 的超集,添加了静态类型、接口、泛型,能在编译期捕获错误、提高协作效率和代码可维护性。运行时仍是 JS。”
12. React 中常用的 Hooks(汇总)
useState:本地状态useEffect:副作用/生命周期useRef:持久化引用或 DOM 节点useMemo:缓存计算结果,避免重复计算useCallback:缓存函数引用,优化子组件重渲染useReducer:复杂状态管理(类似 Redux 的 reducer 模式)useContext:跨组件传递状态(结合 Context)useLayoutEffect:同步副作用,比useEffect早(在 DOM 绘制前执行)- 并发/性能相关:
useTransition、useDeferredValue、useId、useSyncExternalStore(React 18+)
面试精炼回答
“常用 hooks 包括 useState/useEffect/useRef/useMemo/useCallback/useReducer/useContext 等,分别用于状态、副作用、引用、性能与复杂状态管理。”
13. useState 和 useReducer
核心区别
useState:适合局部、简单状态(数值、布尔、少量字段)。setState更新直接传新值或函数式更新(prev => new)。useReducer:适合复杂状态与多字段更新,将更新逻辑抽离到reducer(state, action)中,通过dispatch(action)调用,便于维护与测试;当多个子状态更新耦合或需要基于 action 做复杂业务时更易管理。
示例
useState(简单)
const [count, setCount] = useState(0);
setCount(prev => prev + 1);
useReducer(复杂)
function reducer(state, action) {
switch(action.type) {
case 'increment': return { ...state, count: state.count + 1 };
case 'setName': return { ...state, name: action.payload };
default: return state;
}
}
const [state, dispatch] = useReducer(reducer, { count: 0, name: ''});
dispatch({ type: 'increment' });
何时用
- 复杂表单、多个交互状态、多人协作代码库:
useReducer。 - 简单局部状态:
useState。
面试精炼回答
“useState 更适合简单场景,useReducer 把更新逻辑集中到 reducer,便于管理复杂状态和测试。组件复杂或多人协作时,我倾向用 useReducer。”
14. React 中的虚拟 DOM(Virtual DOM)理解
概念
虚拟 DOM 是框架维护的一棵 JS 对象树(VNode),用于描述 UI 的结构与属性。每次状态改变时,React 生成新的虚拟 DOM,和旧的进行 diff,找出最小修改集(patch),然后把变更批量应用到真实 DOM,减少直接 DOM 操作开销。
优点
- 抽象真实 DOM,减少直接操作开销
- 能做跨平台渲染(Web、Native、canvas)
- 更容易实现批量更新与优化(如合并更新、优先级调度)
限制与误解
- 虚拟 DOM 并非“万能加速器”,错误使用(频繁创建大量复杂对象、无必要的重新渲染)仍会导致性能问题;合理优化(shouldComponentUpdate / memo / useMemo / useCallback)仍很重要。
面试精炼回答
“虚拟 DOM 用 JS 对象描述 UI,diff 找差异并最小化对真实 DOM 的操作,从而提升性能并支持跨平台。但仍需合理避免频繁重渲染。”
15. React 16 推出的 Fiber,谈谈你的理解
为什么要引入 Fiber
React 15 的协调(reconciliation)是递归同步执行的:更新大树会占用主线程较长时间,造成页面卡顿、掉帧、输入延迟等问题。Fiber 重写了解构与调度机制,让渲染过程可中断、可恢复、可分片。
核心能力
- 可中断/分片(time-slicing) :把大任务拆为小片段,在空闲时间执行,避免阻塞主线程。
- 优先级调度:不同更新可设不同优先级(交互优先、动画优先),高优先级任务可中断低优先级任务。
- 增量渲染:能够在多帧内完成一次更新。
- Fiber 节点结构:每个 fiber 表示组件/元素,包含必要的调度元数据(优先级/副作用等)。
影响
- 提升 UI 响应性(输入、动画流畅)。
- 为并发特性(Concurrent Mode / Suspense)奠定基础(React 18 的并发处于其演进方向)。
面试精炼回答
“Fiber 是 React 16 重写的协调引擎,使渲染可中断、可分片并支持优先级调度,从而提高主线程响应性和用户体验。”
16. TypeScript 中断言是什么
定义
类型断言(Type Assertion)是告诉 TypeScript 编译器“相信我,这个值的类型是 X”,不会在运行时做任何转换,仅影响编译期类型检查。
语法
// 推荐(在 TSX 文件中也安全)
const el = document.getElementById('id') as HTMLInputElement;
// 旧语法(在 TSX 中可能冲突)
const el2 = <HTMLInputElement>document.getElementById('id');
常见用途
- DOM 操作(
getElementById返回HTMLElement | null) - JSON.parse 后指定类型(需谨慎)
- 联合类型收窄(当你比编译器更确定类型)
风险
- 乱用断言会掩盖真实错误(破坏类型安全)——尽量用类型保护或显式校验代替盲目断言。
面试精炼回答
“断言是编译期的类型告诉编译器使用者知道类型,但不做运行时检查。用时需谨慎,避免滥用掩盖错误。”
17. Vue2 与 Vue3 响应式原理、Vue2 的缺点、Vue3 的解决方式
Vue2(基于 Object.defineProperty)
-
对对象属性通过
Object.defineProperty的 getter/setter 做依赖收集与派发。 -
对数组通过劫持数组方法(
push/pop/shift/unshift/splice/sort/reverse)来触发更新。 -
缺点:
- 无法检测新增 / 删除属性(需
Vue.set)。 - 无法检测数组索引(
arr[1] = x不触发)。 - 响应式需要遍历嵌套对象导致初始化开销(深度递归)。
- 一些边界场景复杂性高(例如 proxy 化后的 API 更难与 defineProperty 混合)。
- 无法检测新增 / 删除属性(需
Vue3(基于 Proxy)
-
使用
Proxy拦截get/set/has/deleteProperty等所有操作,实现对属性访问与变更的侦测(包括新增/删除)。 -
优点:
- 自动支持新增/删除属性与数组索引变化。
- 更少的初始化开销(按需拦截)。
- 更丰富的操作拦截(如
Reflect结合使用)。 - 性能与内存更优,API 更统一(
ref/reactive)。
源码层面(总结)
- Vue3 的响应式核心仍是
effect -> track -> trigger,但利用 Proxy 让实现更完整、更高效。 ref与reactive在使用层表现不同:ref包装基本类型(需要.value访问),reactive直接代理对象;模板自动 unwrapref。
面试精炼回答
“Vue2 用 Object.defineProperty 做响应式,无法监听新增属性和数组索引,且初始化时需递归处理。Vue3 用 Proxy,能完整拦截对象/数组操作,性能与功能上都有显著改善。”
18. 说说 v-model
本质
v-model 是语法糖:双向绑定的简写,底层用 prop + 事件通信实现(Vue2 用 value + input,Vue3 用 modelValue + update:modelValue 支持多 v-model)。
Vue2(默认行为)
- 组件上使用
v-model等价于:props: ['value']+ 触发this.$emit('input', newValue)。
Vue3(更灵活)
- 默认 prop 名称
modelValue,事件update:modelValue - 支持自定义
v-model:foo来映射到fooprop 与update:foo事件(从而允许多 v-model)。
使用注意
- 在组件内部不要直接修改 prop,应通过派发事件告诉父组件更新。
- 在 Composition API 中结合
toRef/toRefs/emits使用更安全。
面试精炼回答
“v-model 是双向绑定语法糖,Vue2 用 value+input,Vue3 用 modelValue+update:modelValue 并支持多 v-model,组件内部应通过 emit 更新父值。”
19. React 常见的 Hooks(补充)
重复提醒与扩展:
useLayoutEffect:在 DOM 更改但在浏览器绘制之前运行,适合测量 DOM 或同步更新。useDeferredValue/useTransition:用于并发模式下延迟低优先级更新,提升交互流畅性。useId:生成稳定的 id,用于服务端渲染/无冲突 id。useSyncExternalStore:用于外部可订阅存储(React 18+)实现可预测的订阅更新。
20. 说说 useEffect
用途
- 执行副作用:数据请求、订阅、事件监听、DOM 操作、计时器等。
执行时机
- 初次渲染后执行(在浏览器绘制后),依赖变化时再次执行。具体是将回调放到微任务/宏任务队列中于渲染后执行(实现细节与 React 版本有关)。
清理函数
useEffect可返回一个清理函数(用于移除订阅、清除定时器等),该函数在下一次 effect 执行前或组件卸载时调用。
useEffect(() => {
const id = setInterval(() => { ... }, 1000);
return () => clearInterval(id); // 清理
}, [dep]);
常见陷阱
- 遗漏依赖:导致闭包引用旧值或内存泄露。应把 effect 中用到的外部变量(函数/值)列入依赖,或用 eslint-plugin-react-hooks 辅助。
- 函数式更新 vs 直接使用:当需要依赖旧 state 更新时,使用
setState(prev => prev + 1)避免闭包过期问题。 - useEffect 与 useLayoutEffect 的选择:若需要在浏览器绘制前读写布局(测量 DOM),用
useLayoutEffect;一般副作用用useEffect。
面试精炼回答
“useEffect 用于处理副作用,依赖数组控制何时触发,返回清理函数用于卸载或 effect 重新执行前的清理。注意依赖完整性与闭包问题。”
21. React 中你使用的状态管理工具 & 实际用途(示例模板)
面试时把自己实际使用经验替换模板内容即可。
示例回答:
“在项目中我用过 Redux(含 redux-toolkit)、Zustand 与 Recoil。
- Redux/RTK:在大型 app 中负责用户信息、权限、路由状态、持久化(localStorage)、复杂异步流程(thunks/sagas)。使用 RTK 减少样板代码并改善类型推导。
- Zustand:在中小型项目或局部状态共享场景(侧边栏、模态、主题切换)快速搭建,API 简洁易测。
- Recoil:尝试过用作复杂依赖的状态派生(selector),便于组件级原子化状态管理。
使用这些状态管理工具后,主要作用是:减少 props drilling、统一权限控制、实现跨页面共享筛选条件和请求缓存、便于中间件插入(日志/持久化/恢复)。”
22. ES6 新特性(重点)
列举并示例(面试常问)
let/const(块级作用域)- 箭头函数(
=>,无自身this) - 模板字符串(
`Hello ${name}`) - 解构赋值(对象/数组)
- 扩展运算符(
...) - Promise /
Promise.all/Promise.race - Class 语法(
class与extends) - 模块化
import/export Map/SetSymbol(唯一标识)async/await(基于 Promise 的语法糖)Proxy(拦截对象操作)Reflect(反射 API)
面试精炼回答
“ES6 引入块级作用域、箭头函数、类、模块、Promise、解构、扩展运算符、Map/Set、Symbol、Proxy 等,极大改善了可读性和模块化能力。”
23. 说说 Promise,以及它有哪些状态
三种状态
pending(进行中)fulfilled(已成功/已完成)rejected(已拒绝/出错)
状态一旦变更为 fulfilled/rejected 就不可逆。
相关 API
then(onFulfilled, onRejected)catch(onRejected)(then(null, onRejected)的语法糖)finally(() => {})(无论成功失败都会调用)- 静态方法:
Promise.resolve、Promise.reject、Promise.all、Promise.race、Promise.allSettled、Promise.any
与微任务(microtask)关系
- Promise 的
.then回调是微任务,会在当前 macrotask 同步代码执行完毕后、下一个 macrotask 执行前执行(优先于 setTimeout 的回调)。
面试精炼回答
“Promise 有 pending、fulfilled、rejected 三种状态,.then/.catch/.finally 用于处理链式调用。Promise 回调运行在微任务队列,优先于宏任务(如 setTimeout)。”
24. Vue 中的插槽(slots)
类型
- 默认插槽:不带 name 的
<slot />,用于父组件直接插入内容。 - 具名插槽:
<slot name="header" />,父组件使用<template v-slot:header>或#header填充。 - 作用域插槽 / 具名作用域插槽(scoped slot) :子组件通过 slot 传回一组数据(slot props),父组件通过接收这些数据渲染内容(类似反向数据流)。
使用示例(Vue 3 语法)
子组件:
<template>
<div>
<slot name="header"></slot>
<slot :row="rowData"></slot> <!-- 作用域插槽 -->
</div>
</template>
<script> export default { setup(){ const rowData = { id:1 }; return { rowData } } }</script>
父组件:
<MyComp>
<template #header>
<h1>标题</h1>
</template>
<template #default="{ row }">
<div>{{ row.id }}</div>
</template>
</MyComp>
实现原理(简述)
- 编译阶段:插槽被转换为函数(slot functions),运行时在渲染时调用这些函数生成 vnode。
- 作用域插槽是惰性执行,只有渲染需要时才会执行,能结合 patchFlags 优化性能。
面试精炼回答
“插槽分默认、具名和作用域插槽。作用域插槽允许子组件向父组件暴露数据(slot props),父组件用这些数据渲染内容,适合可插拔且需要子组件数据的场景。”