前端基础与浏览器机制 (Front-End Fundamentals & Browser Mechanics)
这些问题涵盖了Web工作的基本原理,包括网络、渲染和浏览器特性。
1. 浏览器渲染与性能 (Browser Rendering & Performance)
- URL输入发生什么?(What happens when you type a URL?)
- 这是一个经典问题。它涉及DNS解析、TCP三次握手、HTTP请求、服务器处理、HTTP响应、浏览器解析(HTML, CSS, JS)、DOM/CSSOM构建、布局、绘制和合成。
- DOM树和CSSOM树的构建是并行的还是串行的?(Are DOM and CSSOM tree construction parallel or serial?)
- DOM 构建通常与网络请求其他资源(如图片)是并行的。然而,CSSOM 构建是渲染阻塞的,因为浏览器需要CSSOM来正确渲染页面。JavaScript如果需要访问或修改DOM或CSSOM,也可能阻塞DOM构建。
- 讲一下URL到渲染页面过程 (Explain the process from URL to page rendering)
- 这是“URL输入发生什么”问题的扩展,更侧重于浏览器端:DNS查找、TCP连接、HTTP请求/响应、解析HTML、构建DOM树、解析CSS、构建CSSOM树、结合生成渲染树、布局(回流)、绘制(重绘)和合成。
- div改变尺寸和颜色(应该是想问回流和重绘) (Div changing size and color - likely asking about Reflow and Repaint)
- 回流 (Reflow):当页面布局发生变化时(例如,元素尺寸、位置、内容变化)。这是一个昂贵的操作,因为它需要重新计算受影响元素及其后代和祖先的布局。
- 重绘 (Repaint):当元素的视觉属性发生变化但不影响其布局时(例如,
color
、background-color
、visibility
)。这比回流的开销小。
- 回流重绘具体是怎么实现的 (How are reflow and repaint specifically implemented?)
- 这深入到浏览器渲染引擎。渲染树构建完成后,浏览器执行“布局”(回流)阶段以确定所有元素的精确位置和大小。然后,“绘制”(重绘)阶段填充像素。这些过程由浏览器的渲染引擎管理。
2. 网络与协议 (Networking & Protocols)
- DNS解析具体是怎么个解析过程呢 (How does DNS resolution specifically work?)
- 涉及检查浏览器缓存、操作系统缓存、路由器缓存、本地DNS服务器、根DNS服务器、TLD(顶级域名)DNS服务器和权威DNS服务器,最终获取IP地址。它是一个分层和分布式的系统。
- 三次握手、四次挥手讲一下 (Explain TCP Three-way Handshake and Four-way Wave)
- 三次握手 (Three-way Handshake):建立TCP连接(SYN, SYN-ACK, ACK)。
- 四次挥手 (Four-way Wave):终止TCP连接(FIN, ACK, FIN, ACK)。
- 301, 302, 304 (HTTP Status Codes)
- 301 Moved Permanently (永久重定向):资源已永久移动到新URL。浏览器应更新其书签/缓存。
- 302 Found (临时重定向):资源暂时移动。浏览器不应更新其书签/缓存。
- 304 Not Modified (未修改):客户端的缓存版本仍然有效。服务器告诉浏览器使用其缓存副本,节省带宽。
- HTTPS加密 (HTTPS encryption)
- 在HTTP之上使用 SSL/TLS 协议。它结合使用非对称加密(公钥/私钥)进行初始握手和密钥交换,以及对称加密进行连接建立后的数据传输。这确保了机密性、完整性和身份验证。
- HTTP/2有什么新功能?多路复用具体表现 (What’s new in HTTP/2? Explain multiplexing)
- 多路复用 (Multiplexing):允许在单个TCP连接上同时发送多个请求和响应。这消除了HTTP/1.x中的“队头阻塞”问题,从而提高性能并减少往返次数。
- 其他特性:服务器推送、头部压缩(HPACK)、流优先级。
- 那你刚才说的图片压缩,如果要更快,你用CDN,dns解析是怎么跟CDN关联起来的?(Regarding image compression, if you want it faster using CDN, how does DNS resolution relate to CDN?)
- 当用户请求CDN上的资源时,DNS解析会根据用户的地理位置和网络状况,将请求解析到离用户最近的CDN节点(边缘服务器)的IP地址。这通常通过DNS的CNAME记录和CDN服务商的智能DNS解析实现,从而实现内容分发和加速。
3. 浏览器缓存 (Browser Caching)
- 浏览器缓存有哪些 (What types of browser caches are there?)
- 主要有 HTTP 缓存(包括强缓存和协商缓存)。其他类型包括 Service Worker 缓存、PWA 缓存(IndexedDB, LocalStorage)和内存缓存。
- 强缓存和协商缓存,怎么设置 (Strong cache and negotiated cache, how to set them up?)
- 强缓存 (Strong Cache):浏览器不向服务器发送请求。检查
Expires
(HTTP/1.0) 或Cache-Control
(HTTP/1.1) 头部。Cache-Control: max-age=<seconds>
:资源在此秒数内是新鲜的。Cache-Control: no-cache
(仍发送请求但重新验证)、no-store
(从不缓存)。
- 协商缓存 (Negotiated Cache):浏览器向服务器发送请求,服务器决定缓存版本是否仍然有效。
Last-Modified
/If-Modified-Since
:服务器发送Last-Modified
头部。在后续请求中,浏览器发送If-Modified-Since
带有该日期。服务器进行比较。ETag
/If-None-Match
:服务器发送ETag
(资源内容的标识符)。在后续请求中,浏览器发送If-None-Match
带有该ETag。服务器进行比较。ETag
比Last-Modified
更健壮。
- 强缓存 (Strong Cache):浏览器不向服务器发送请求。检查
- 浏览器缓存,能详细说说吗,相关的属性和标识呢?缓存过程大概是什么样的?(Can you elaborate on browser caching, relevant attributes and identifiers? What’s the caching process like?)
- 这是一个要求详细解释的问题,涵盖上述所有要点,强调使用的头部以及浏览器和服务器经过的决策过程。
JavaScript 与 Web API (JavaScript & Web APIs)
这些问题侧重于JavaScript的核心机制以及它如何与浏览器交互。
1. JavaScript 基础 (JavaScript Fundamentals)
- JS事件循环 (JavaScript Event Loop)
- 解释了JavaScript如何处理异步操作,尽管它是单线程的。它涉及调用栈 (Call Stack)、堆 (Heap)、Web API(定时器、DOM事件、HTTP请求)、回调队列 (Callback Queue/Macro-task Queue) 和微任务队列 (Micro-task Queue)(用于Promise、
queueMicrotask
)。事件循环不断检查调用栈是否为空,然后将任务从微任务队列移动到调用栈,再从回调队列移动到调用栈。
- 解释了JavaScript如何处理异步操作,尽管它是单线程的。它涉及调用栈 (Call Stack)、堆 (Heap)、Web API(定时器、DOM事件、HTTP请求)、回调队列 (Callback Queue/Macro-task Queue) 和微任务队列 (Micro-task Queue)(用于Promise、
- 深拷贝,为什么要深拷贝 (Deep copy, why deep copy?)
- 深拷贝 (Deep copy):创建一个对象的完全独立副本,包括所有嵌套的对象和数组。对复制对象的更改不会影响原始对象。
- 为什么 (Why):为了避免在修改复杂数据结构时产生意外的副作用。如果只执行浅拷贝,嵌套的对象/数组仍将引用相同的内存位置,导致意外行为。
- 垂直居中的方法 (Methods for vertical centering)
- 多种方法:
- Flexbox:
display: flex; align-items: center; justify-content: center;
- Grid:
display: grid; place-items: center;
- 绝对定位 (Absolute Positioning):
position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
- Table-cell:
display: table-cell; vertical-align: middle;
- Line-height(用于单行文本)。
- Flexbox:
- 多种方法:
- 发布订阅者模式,能不能手写 (Publish-Subscribe pattern, can you hand-code it?)
- 需要实现一个类似
EventEmitter
的类,包含on
(订阅)、emit
(发布) 和off
方法。
- 需要实现一个类似
- Promise并发,Promise嵌套,Promise底层原理 (Promise concurrency, nesting, underlying principles)
- 并发 (Concurrency):使用
Promise.all()
、Promise.race()
、Promise.allSettled()
、Promise.any()
来同时管理多个 Promise。 - 嵌套 (Nesting):通过链式调用
.then()
来处理顺序的异步操作。 - 底层原理 (Underlying Principle):Promise 代表异步操作的最终完成(或失败)及其结果值。它们基于 A+ Promises 规范,并利用微任务队列执行回调。
- 并发 (Concurrency):使用
- new过程发生了什么 (What happens during the
new
process?)- 创建一个新的空对象。
- 将这个新对象的
[[Prototype]]
(即__proto__
)链接到构造函数的prototype
对象。 - 将构造函数的作用域赋给新对象(
this
指向新对象)。 - 执行构造函数中的代码。
- 如果构造函数没有明确返回一个对象,则返回这个新对象。
2. 模块化 (Modularity)
- CommonJS和ES6模块化有什么区别 (What are the differences between CommonJS and ES6 Modules?)
- CommonJS (Node.js 默认):
- 同步加载模块。
require()
导入,module.exports
或exports
导出。- 导出的是值的拷贝。
- ES Modules (ESM) (浏览器和现代Node.js):
- 异步加载模块(静态分析)。
import
导入,export
导出。- 导出的是值的引用(绑定)。
- 支持顶层
await
。
- CommonJS (Node.js 默认):
3. 跨域 (Cross-Origin)
- 跨域 (Cross-Origin)
- 指浏览器出于安全原因,限制从一个源(协议、域名、端口三者之一不同)加载的文档或脚本与另一个源的资源进行交互。
- 解决方案:CORS (跨域资源共享)、JSONP、代理、WebSocket、Nginx反向代理等。
框架与库 (Frameworks & Libraries)
这些问题深入到React和Vue等流行前端框架的具体实现和优化。
1. React
- react生命周期 (React Lifecycle)
- 旧版 (Class Components):
componentDidMount
、componentDidUpdate
、componentWillUnmount
等。 - 新版 (Hooks):使用
useEffect
模拟大部分生命周期行为。
- 旧版 (Class Components):
- 如何用hook实现componentWillUnmount (How to implement
componentWillUnmount
with Hooks)- 在
useEffect
的回调函数中返回一个清理函数。这个清理函数会在组件卸载时执行。
useEffect(() => { // 相当于 componentDidMount 和 componentDidUpdate return () => { // 相当于 componentWillUnmount }; }, []); // 空数组表示只在挂载和卸载时执行
- 在
- 在class组件能用hooks吗?在函数式组件用class语法呢?(Can you use Hooks in class components? Can you use class syntax in functional components?)
- Hooks不能在class组件中使用。Hooks 是为函数式组件设计的。
- 函数式组件不能直接使用class语法。函数式组件就是纯粹的JavaScript函数。
- class组件跟函数式组件优化的点在哪?使用函数式组件好处是什么?(Where are the optimization points for class components vs. functional components? What are the benefits of using functional components?)
- 优化点:
- Class Components:
shouldComponentUpdate
、PureComponent
。 - Functional Components:
React.memo
(用于性能优化,类似PureComponent
)、useCallback
、useMemo
。
- Class Components:
- 函数式组件好处:
- 更简洁、可读性更高。
- 更易于测试。
- 使用 Hooks 能够更好地复用状态逻辑。
- 更容易理解和推理组件行为。
- 未来可能获得更好的性能优化(例如 Concurrent Mode)。
- 优化点:
- React会吗,Vue2和Vue3说说你的看法和理解 (Do you know React? Talk about your views and understanding of Vue2 and Vue3)
- 这需要你结合自己的经验,比较它们的设计理念、API风格、性能优化、生态系统等。
- Vue2: 选项式API,响应式基于
Object.defineProperty
。 - Vue3: 组合式API (Composition API) 和 选项式API 共存,响应式基于
Proxy
,性能更好,Tree-shaking 优化。 - React: JSX,函数式组件 + Hooks,强调函数式编程和不可变性。
- Vue2: 选项式API,响应式基于
- 这需要你结合自己的经验,比较它们的设计理念、API风格、性能优化、生态系统等。
2. Vue
- Vue的双向绑定怎么做的?(How does Vue’s two-way binding work?)
- Vue2: 主要通过
Object.defineProperty()
劫持对象的 getter 和 setter,结合发布-订阅模式(Dep 收集依赖,Watcher 订阅更新)。当数据变化时,setter 通知所有依赖的 Watcher,Watcher 触发组件重新渲染。 - Vue3: 使用
Proxy
对象劫持整个对象,可以监听属性的增删改查以及数组操作,性能更好,并且解决了Object.defineProperty
无法监听新增属性和删除属性的问题。
- Vue2: 主要通过
- Vue $nextTick原理 (Vue $nextTick principle)
$nextTick
的原理是利用 JavaScript 的事件循环机制。Vue 在更新 DOM 后,会将$nextTick
的回调函数推入微任务队列(如果支持 Promise)或宏任务队列(如setTimeout
)。这样可以确保回调函数在 DOM 更新完成后执行,从而获取到最新的 DOM 状态。
- emit原理(emit原理 (emit原理(emit principle)
$emit
是 Vue 组件通信的一种方式,用于子组件向父组件发送事件。它基于 Vue 实例的事件系统。子组件通过$emit('eventName', payload)
触发一个事件,父组件通过@eventName="handler"
监听并处理这个事件。
- computed和watch的区别 (Differences between computed and watch)
- Computed (计算属性):
- 基于它们的响应式依赖进行缓存。只有当依赖变化时,才会重新求值。
- 默认是同步的。
- 用于处理模板中需要复杂逻辑计算的数据,通常用于派生状态。
- 必须有返回值。
- Watch (侦听器):
- 用于观察和响应 Vue 实例上的数据变化。
- 当数据变化时,执行一个副作用函数。
- 可以执行异步操作。
- 通常用于执行数据变化后的复杂操作,如网络请求、DOM 操作等。
- Computed (计算属性):
- Vue那个生命周期可以操作dom (Which Vue lifecycle hook can operate on DOM?)
mounted
:在实例挂载到 DOM 后调用。此时组件的模板已经渲染到 DOM 中,可以进行 DOM 操作。updated
:在组件因数据更改而重新渲染后调用。此时 DOM 已经更新,也可以进行 DOM 操作,但应避免在此钩子中修改状态,可能导致无限循环。
其他通用问题 (Other General Questions)
这些问题涵盖了工具、兼容性、安全和个人职业选择。
1. 工具与构建 (Tools & Build)
- webpack你了解吗?(Do you know webpack?)
- Webpack 是一个静态模块打包器。当 Webpack 处理应用程序时,它会递归地构建一个依赖关系图,其中包含应用程序所需的每个模块,然后将所有这些模块打包成一个或多个 bundle。
- 核心概念:Entry(入口)、Output(输出)、Loader(加载器)、Plugin(插件)、Mode(模式)。
- 你的图片压缩怎么做的?(How do you do image compression?)
- 可以是在构建时(如使用 Webpack 插件
image-webpack-loader
)、上传时(服务器端处理)、或运行时(如使用<picture>
标签、srcset
属性、WebP 格式)。
- 可以是在构建时(如使用 Webpack 插件
- 如果要做兼容性,你会怎么做?(How would you handle compatibility issues?)
- Polyfill:用于弥补新旧浏览器之间API的差异。
- Babel:将ES6+代码转换为ES5,以兼容旧浏览器。
- Autoprefixer:自动添加CSS供应商前缀。
- CSS Reset/Normalize.css:统一不同浏览器默认样式。
- 渐进增强/优雅降级:设计策略。
- 浏览器兼容性测试:使用工具或手动测试。
2. 安全 (Security)
- 登陆校验方法 (Login validation methods)
- Session-Cookie: 服务器创建Session,将Session ID存在Cookie中返回给客户端,客户端每次请求带上Cookie,服务器根据Session ID识别用户。
- Token-based (JWT): 客户端登录成功后,服务器返回一个JWT (JSON Web Token),客户端将Token存储(如LocalStorage),每次请求在Header中携带Token,服务器验证Token有效性。
- OAuth2 / OpenID Connect: 用于第三方登录授权。
- 说到了nodejs,问nodejs怎么实现token?(应该是想问底层原理吧)(Mentioned Node.js, how does Node.js implement tokens? (Likely asking about underlying principles))
- 在 Node.js 中实现 Token 通常指 JWT (JSON Web Token)。
- 原理:使用
jsonwebtoken
等库。当用户登录时,服务器使用一个密钥对用户ID、角色等信息进行签名,生成一个JWT。这个JWT包含三部分:Header(头部)、Payload(载荷)和Signature(签名)。客户端收到JWT后存储起来。每次请求时,客户端将JWT放在HTTP请求的Authorization
头部。服务器收到请求后,使用相同的密钥验证JWT的签名,如果签名有效且未过期,则认为用户已认证。
- SSL加密细节 (SSL encryption details)
- SSL/TLS 握手过程:
- ClientHello: 客户端发送支持的SSL/TLS版本、加密套件、随机数等。
- ServerHello: 服务器选择一个加密套件,发送证书(包含公钥)、随机数。
- Certificate: 客户端验证服务器证书的合法性(通过CA)。
- ClientKeyExchange: 客户端生成一个预主密钥,用服务器公钥加密后发送给服务器。
- ChangeCipherSpec: 客户端和服务器分别使用预主密钥生成会话密钥(对称密钥)。
- Finished: 双方使用会话密钥发送加密的握手消息,验证握手是否成功。
- 之后的数据传输都使用对称密钥进行加密。
- SSL/TLS 握手过程:
3. 个人与职业 (Personal & Career)
- 为啥想做前端 (Why do you want to be a front-end developer?)
- 这是一个开放性问题,需要你表达对前端的热情、兴趣点(如用户体验、可视化、技术发展快等)、个人优势与前端的契合度。
- 学习前端的过程是怎么样的 (What was your front-end learning process like?)
- 描述你的学习路径,例如从HTML/CSS/JS基础开始,到框架(React/Vue),再到构建工具(Webpack),以及如何解决问题、参与项目等。
- 有学过算法和数据结构吗?(Have you studied algorithms and data structures?)
- 前端开发中,算法和数据结构虽然不像后端那样核心,但在优化性能、解决复杂问题(如虚拟列表、树形结构操作)时非常重要。诚实回答并说明自己在前端场景下的应用或学习意愿。
- 我们这个工作可能三四天都会花在兼容性问题上,你能接受吗 (Our work might involve spending three or four days on compatibility issues, can you accept that?)
- 这是一个考察你对工作内容预期和抗压能力的问题。通常应该表示理解和接受,并强调自己有解决兼容性问题的能力和耐心。
希望这份详细的总结和归纳对你有所帮助!祝你面试顺利!
我将根据面试问题分类详细解答,并提供扩展知识:
一、Web存储相关
localStorage和cookie的区别:
- 存储容量:cookie约4KB,localStorage约5MB
- 生命周期:cookie可设置过期时间,localStorage永久存储(除非手动清除)
- 通信:cookie会随HTTP请求自动发送,localStorage不会
- 访问限制:cookie可设置domain/path限制,localStorage遵循同源策略
设置cookie示例:
document.cookie = "username=John; expires=Thu, 18 Dec 2025 12:00:00 UTC; path=/";
document.cookie = "useremail=john@example.com; max-age=3600; secure";
二、跨域问题
跨域解决方案:
- CORS(服务端设置
Access-Control-Allow-Origin
) - JSONP(利用
- CORS(服务端设置
三、JavaScript核心
变量存储:
- 基本类型:Undefined、Null、Boolean、Number、String、Symbol(栈存储)
- 引用类型:Object(堆存储,栈存指针)
let/const/var区别:
// 作用域 var a = 1; // 函数作用域 let b = 2; // 块级作用域 const c = 3; // 块级作用域 // 变量提升 console.log(d); // undefined(var会提升) var d = 4; // 重复声明 var e = 5; var e = 6; // 允许 let f = 7; let f = 8; // 报错
const的可变性:
- 不可变:基本类型(Number, String等)
- 可变:引用类型(Object, Array等)的属性可修改
四、ES6+特性
Set和Map:
- Set:值唯一的集合,常用方法add/delete/has
- Map:键值对集合,键可以是任意类型,保持插入顺序
Promise进阶:
- Promise.all:全部成功则返回结果数组,一个失败立即reject
- Promise.race:第一个settled的promise决定结果
- 新增方法:Promise.allSettled(等所有promise完成)
五、Vue框架
虚拟DOM:
- 本质是JS对象(轻量级DOM描述)
- 更新流程:数据变化 → 新vnode → diff算法 → 补丁更新
- 递归处理:确实会递归到最深层,但通过diff算法优化
Vuex核心:
// mutation必须是同步函数 mutations: { increment(state) { state.count++ } } // action可以包含异步操作 actions: { async fetchData({ commit }) { const res = await api.getData(); commit('SET_DATA', res.data); } }
六、手撕代码
- 深拷贝实现:
function deepClone(obj, map = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (map.has(obj)) return map.get(obj);
const clone = Array.isArray(obj) ? [] : {};
map.set(obj, clone);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key], map);
}
}
return clone;
}
- 防抖与节流:
// 防抖(延迟执行)
function debounce(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
// 节流(固定频率执行)
function throttle(fn, interval) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= interval) {
fn.apply(this, args);
lastTime = now;
}
};
}
- 三数之和(LeetCode 15):
function threeSum(nums) {
nums.sort((a, b) => a - b);
const res = [];
for (let i = 0; i < nums.length - 2; i++) {
if (i > 0 && nums[i] === nums[i - 1]) continue;
let left = i + 1, right = nums.length - 1;
while (left < right) {
const sum = nums[i] + nums[left] + nums[right];
if (sum === 0) {
res.push([nums[i], nums[left], nums[right]]);
while (left < right && nums[left] === nums[left + 1]) left++;
while (left < right && nums[right] === nums[right - 1]) right--;
left++;
right--;
} else if (sum < 0) {
left++;
} else {
right--;
}
}
}
return res;
}
七、Webpack相关
Loader与Plugin:
- Loader:文件转换器(如babel-loader转译JS,css-loader处理CSS)
- Plugin:扩展功能(如HtmlWebpackPlugin生成HTML文件)
Vue3打包:
- 主流方案:Vite(基于ESM的极速开发体验)
- 传统方案:webpack + vue-loader
建议结合具体项目经验补充回答,例如提到localStorage时可以举例说明在项目中如何用于保存用户偏好设置,讨论跨域时可以说实际项目中如何配置代理等。对于框架问题,最好能对比React的类似机制(如虚拟DOM的diff算法差异)。
1. React会吗?Vue2和Vue3说说你的看法和理解
React
React 是一个基于 组件化 和 虚拟DOM 的前端框架,核心特点:
- 单向数据流(数据自上而下传递,状态管理依赖Redux/MobX)
- JSX语法(JavaScript + XML,增强代码可读性)
- 函数式编程(Hooks API,如
useState
、useEffect
) - 高性能虚拟DOM(Diff算法优化渲染)
Vue2 vs Vue3
特性 | Vue2 | Vue3 |
---|---|---|
响应式原理 | Object.defineProperty (无法监听新增属性) |
Proxy (全量监听,性能更好) |
Composition API | 无(Options API) | 支持(逻辑复用更灵活) |
性能优化 | 虚拟DOM全量Diff | PatchFlag(静态标记,减少Diff计算) |
TypeScript支持 | 一般 | 深度集成 |
打包体积 | 较大 | Tree-shaking优化,更小 |
生命周期 | beforeCreate 、created 等 |
setup() 替代部分生命周期 |
Vue3优势:
- 更好的性能(Proxy响应式、静态提升)
- 更好的代码组织(Composition API)
- 更好的TS支持
- 更小的包体积
2. SSL加密细节
SSL(Secure Sockets Layer)现已被TLS(Transport Layer Security)取代,但习惯仍称SSL。
SSL/TLS加密流程(HTTPS)
- Client Hello:客户端发送支持的加密算法、随机数(Client Random)
- Server Hello:服务端返回选择的加密算法、随机数(Server Random)、证书
- 证书验证:客户端验证证书(CA机构签发,防止中间人攻击)
- 密钥交换:
- RSA:客户端用公钥加密Pre-Master Key,服务端私钥解密
- ECDHE(更安全):基于椭圆曲线动态生成会话密钥
- 会话密钥生成:Client Random + Server Random + Pre-Master Key → 生成对称加密密钥
- 加密通信:后续数据使用对称加密(AES)传输
加密算法:
- 非对称加密(密钥交换):RSA、ECDHE
- 对称加密(数据传输):AES、ChaCha20
- 哈希校验:SHA-256
3. 浏览器缓存
浏览器缓存分为 强缓存 和 协商缓存。
强缓存(无需请求服务器)
- HTTP Header:
Cache-Control: max-age=3600
(优先级高,单位秒)Expires: Wed, 21 Oct 2025 07:28:00 GMT
(HTTP/1.0,受本地时间影响)
- 缓存位置:
Memory Cache
(内存,快速但容量小)Disk Cache
(硬盘,持久但较慢)
协商缓存(需请求服务器验证)
- Last-Modified / If-Modified-Since(基于时间)
- 服务器返回
Last-Modified
,客户端下次带If-Modified-Since
- 缺点:1秒内修改无法检测
- 服务器返回
- ETag / If-None-Match(基于内容哈希,更精准)
- 服务器返回
ETag
,客户端下次带If-None-Match
- 服务器返回
缓存过程:
- 检查
Cache-Control
/Expires
→ 命中强缓存直接返回 - 未命中则发送请求,服务器检查
If-Modified-Since
/If-None-Match
- 资源未变更 → 304 Not Modified(用缓存)
- 资源变更 → 200 OK(返回新资源)
4. 手写发布订阅模式(Pub-Sub)及优化
基础实现
class EventEmitter {
constructor() {
this.events = {};
}
on(event, callback) {
if (!this.events[event]) this.events[event] = [];
this.events[event].push(callback);
}
emit(event, ...args) {
if (this.events[event]) {
this.events[event].forEach(cb => cb(...args));
}
}
off(event, callback) {
if (this.events[event]) {
this.events[event] = this.events[event].filter(cb => cb !== callback);
}
}
}
// 使用示例
const emitter = new EventEmitter();
emitter.on("message", (data) => console.log(data));
emitter.emit("message", "Hello World!");
优化方向
- 防止内存泄漏:增加
once
方法(执行后自动取消订阅) - 性能优化:用
Map
代替Object
(查找更快) - 错误处理:
try-catch
包裹回调执行 - 异步支持:
setTimeout
或Promise
处理异步事件
5. 跨域
原因
浏览器同源策略限制(协议+域名+端口一致)。
解决方案
- CORS(服务端设置)
Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, POST Access-Control-Allow-Headers: Content-Type
- JSONP(仅GET请求)
function handleResponse(data) { console.log(data); } const script = document.createElement("script"); script.src = "https://api.example.com/data?callback=handleResponse"; document.body.appendChild(script);
- 代理服务器(开发环境常用)
// webpack.config.js devServer: { proxy: { "/api": "http://localhost:3000", }, },
- Nginx反向代理
location /api { proxy_pass http://backend-server; }
6. TCP三次握手 & 四次挥手
三次握手(建立连接)
- SYN:客户端发送
SYN=1, seq=x
- SYN-ACK:服务端回复
SYN=1, ACK=1, seq=y, ack=x+1
- ACK:客户端发送
ACK=1, seq=x+1, ack=y+1
目的:确认双方收发能力正常,防止历史连接干扰。
四次挥手(断开连接)
- FIN:客户端发送
FIN=1, seq=u
- ACK:服务端回复
ACK=1, ack=u+1
(半关闭状态) - FIN:服务端发送
FIN=1, seq=v
- ACK:客户端回复
ACK=1, ack=v+1
(等待2MSL后关闭)
为什么四次?
因为TCP是全双工,需分别关闭发送和接收通道。
7. nextTick原理和底层
Vue的nextTick
用于在DOM更新后执行回调,基于 微任务队列 实现。
实现原理
- 优先使用微任务(
Promise.then
>MutationObserver
>setImmediate
>setTimeout
) - 回调队列:将回调函数存入队列,在下一个事件循环执行
示例代码:
let callbacks = [];
let pending = false;
function flushCallbacks() {
pending = false;
const copies = callbacks.slice();
callbacks = [];
for (let cb of copies) cb();
}
function nextTick(cb) {
callbacks.push(cb);
if (!pending) {
pending = true;
Promise.resolve().then(flushCallbacks);
}
}
8. Promise并发、嵌套及底层原理
Promise并发控制
function limitRequest(urls, limit) {
const results = [];
let count = 0;
async function run() {
while (urls.length && count < limit) {
const url = urls.shift();
count++;
try {
const res = await fetch(url);
results.push(res);
} catch (err) {
results.push(err);
} finally {
count--;
if (urls.length) run();
}
}
}
run();
return results;
}
Promise嵌套(回调地狱)
fetch("/api/user")
.then(user => fetch(`/api/profile/${user.id}`))
.then(profile => fetch(`/api/posts/${profile.id}`))
.then(posts => console.log(posts));
底层原理
Promise基于 状态机 实现:
- 状态:
pending
、fulfilled
、rejected
- then方法:返回新Promise,支持链式调用
- 微任务队列:
then
回调放入微任务队列
9. new过程发生了什么
function myNew(constructor, ...args) {
// 1. 创建空对象,继承原型
const obj = Object.create(constructor.prototype);
// 2. 执行构造函数,绑定this
const result = constructor.apply(obj, args);
// 3. 如果构造函数返回对象,则使用它
return result instanceof Object ? result : obj;
}
// 示例
function Person(name) {
this.name = name;
}
const p = myNew(Person, "John");
10. 浏览器缓存(详细)
见 第3部分,补充:
- Cache-Control指令:
no-cache
:协商缓存no-store
:禁用缓存must-revalidate
:过期后必须验证
11. 算法与数据结构
常见前端相关算法:
- 排序:快速排序、归并排序
- 搜索:二分查找
- 数据结构:栈、队列、链表、二叉树(虚拟DOM Diff用)
- LeetCode高频题:两数之和、LRU缓存、反转链表
12. 为什么选前端?学习过程?
为什么选前端
- 即时反馈:所见即所得,快速验证想法
- 技术栈丰富(HTML/CSS/JS、框架、Node.js)
- 市场需求大(Web、移动端、Electron等)
学习过程
- 基础:HTML/CSS/JavaScript(《JavaScript高级程序设计》)
- 进阶:ES6、Webpack、Vue/React
- 工程化:Git、CI/CD、性能优化
- 全栈拓展:Node.js、数据库(MySQL/MongoDB)
总结
本文涵盖了前端面试高频问题,建议结合项目经验回答,例如:
- 提到Vue3时,可对比React Hooks
- 讨论缓存时,可举例项目中的缓存策略优化
- 手写代码时,说明优化思路(如
Map
替代Object
)
希望这份详细解答能帮助你准备面试! 🚀
以下是针对您面试问题的全面解析,结合Vue核心原理、浏览器机制、网络通信等模块组织答案,并标注关键技术和易考点:
一、Vue核心原理
$nextTick
原理
Vue的响应式更新采用异步队列机制,当数据变化时,DOM更新会被推入队列,在下一个事件循环中批量执行。$nextTick
将回调函数加入队列尾部,确保在DOM更新后执行。
实现优先级:Promise.then
>MutationObserver
>setImmediate
>setTimeout
。// 简化实现 let pending = false; const callbacks = []; function nextTick(cb) { callbacks.push(cb); if (!pending) { pending = true; Promise.resolve().then(flushCallbacks); } }
$emit
原理
子组件通过this.$emit('event', data)
触发自定义事件,父组件通过@event
监听。底层是发布订阅模式:Vue实例维护事件中心(_events
对象),$emit
触发时遍历对应事件回调执行。computed vs watch
特性 computed watch 用途 同步计算新值(如过滤数据) 监听数据变化执行异步/复杂操作 缓存 依赖不变时复用结果 无缓存,每次变化都执行 异步支持 ❌ 不支持 ✅ 支持 典型场景 购物车总价、表单校验 搜索建议、路由参数监听 操作DOM的生命周期
mounted
:组件首次渲染完成,DOM已挂载,可安全操作。updated
:数据更新导致DOM重新渲染后(慎用,易引发循环更新)。
二、浏览器与HTTP
强缓存 vs 协商缓存
类型 响应头 状态码 优先级 强缓存 Cache-Control: max-age=3600
200 (from disk/memory cache) 高 协商缓存 ETag
/If-None-Match
304 低 过程:强缓存生效时不请求服务器;失效时携带 If-Modified-Since
或If-None-Match
向服务器验证。跨域解决方案
- CORS:服务端设置
Access-Control-Allow-Origin
- JSONP:利用
<script>
标签无跨域限制<script src="https://api.com/data?callback=handleData"></script>
- 代理服务器:开发环境用webpack-dev-server代理,生产环境用Nginx反向代理。
- CORS:服务端设置
HTTP/2.0新特性
- 多路复用:一个TCP连接并行传输多个请求/响应,解决HTTP/1.1队头阻塞
- 二进制分帧、头部压缩、服务器推送
多路复用示例:
!https://example.com/http2-multiplexing.png
(图示:多个流共享同一连接)
HTTPS加密流程
- 客户端发送加密算法列表 + 随机数A
- 服务器返回证书 + 随机数B
- 客户端验证证书(CA机构签发)
- 用证书公钥加密预主密钥发送
- 双方用随机数A+B+预主密钥生成对称加密密钥
- 后续通信使用对称加密(如AES)。
三、CSS与DOM
垂直居中方案
/* 方案1:Flex (推荐) */ .parent { display: flex; align-items: center; justify-content: center; } /* 方案2:Grid */ .parent { display: grid; place-items: center; } /* 方案3:绝对定位 + transform */ .child { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
回流(reflow) vs 重绘(repaint)
操作 触发条件 性能影响 回流 尺寸/位置/结构变化 ⚠️ 高 重绘 颜色/背景等样式变化 ⚠️ 中 优化建议: - 避免频繁操作DOM,使用
documentFragment
- 用CSS3动画(触发GPU加速)替代JS动画。
- 避免频繁操作DOM,使用
四、其他核心问题
Node.js实现Token
流程:const crypto = require('crypto'); // 生成Token(JWT简化版) function createToken(payload, secret) { const header = Buffer.from(JSON.stringify({ alg: 'HS256' })).toString('base64'); const body = Buffer.from(JSON.stringify(payload)).toString('base64'); const sign = crypto.createHmac('sha256', secret).update(`${header}.${body}`).digest('base64'); return `${header}.${body}.${sign}`; } // 验证时重新计算签名对比
CommonJS vs ES6模块化
特性 CommonJS ES6 Modules 加载方式 运行时加载(同步) 编译时静态分析 输出 module.exports
export
/import
引用类型 值的拷贝(基础类型) 值的引用(实时绑定) 深拷贝实现与必要性
原因:对象赋值仅复制引用,修改嵌套对象会污染原数据。
手写深拷贝:
function deepClone(obj, map = new WeakMap()) {
if (typeof obj !== 'object' || obj === null) return obj;
if (map.has(obj)) return map.get(obj); // 解决循环引用
const clone = Array.isArray(obj) ? [] : {};
map.set(obj, clone);
for (let key in obj) {
if (obj.hasOwnProperty(key)) clone[key] = deepClone(obj[key], map);
}
return clone;
}
- 发布订阅模式手写
class EventEmitter {
constructor() { this.events = {}; }
on(event, cb) {
(this.events[event] || (this.events[event] = [])).push(cb);
}
emit(event, ...args) {
this.events[event]?.forEach(cb => cb(...args));
}
off(event, cb) {
this.events[event] = this.events[event]?.filter(fn => fn !== cb);
}
}
// 优化点:增加once()、错误处理、异步执行
五、面试策略建议
- React弥补方案:
- 快速学习React核心概念(组件化、Hooks、状态管理)
- 强调Vue底层原理的掌握(如虚拟DOM Diff、响应式实现)。
- 项目亮点突出:
- 在自我介绍中量化成果,如“Vue2升Vue3提升构建速度80%”
- 主动引导技术提问(如提到性能优化引面试官追问)。
- 框架对比:
方向 Vue优势 React优势 响应式 Proxy深度监听 不可变数据+手动优化 模板语法 SFC单文件组件开发高效 JSX灵活性强
面试核心在于 原理深度 + 实战亮点,即使部分问题未答全,展示出学习能力和技术热情也能加分。建议针对React补强基础概念,同时深化Vue原理理解(如虚拟DOM源码、依赖收集过程)。
以下是针对您提出的技术问题的系统性解答,结合原理与实践经验详细展开:
⚙️ 1. Vue双向绑定原理
实现机制:
- Vue2:基于
Object.defineProperty
劫持数据属性的getter/setter
,通过依赖收集(Dep类) 和观察者模式(Watcher) 实现:- 数据读取时触发
getter
,将当前Watcher添加到Dep订阅列表。 - 数据修改时触发
setter
,通知Dep所有Watcher更新视图。
- 数据读取时触发
- Vue3:改用
Proxy
代理整个对象,支持动态增删属性及深层嵌套监听,性能更高。
流程示例:
// 简化版数据劫持
function defineReactive(obj, key) {
const dep = new Dep();
let val = obj[key];
Object.defineProperty(obj, key, {
get() {
dep.depend(); // 收集依赖
return val;
},
set(newVal) {
val = newVal;
dep.notify(); // 通知更新
}
});
}
🖼️ 2. 图片压缩方案
前端实现要点:
- Canvas压缩:调整尺寸与质量
function compressImage(file, quality = 0.8) { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); const img = new Image(); img.onload = () => { canvas.width = img.width * 0.5; // 尺寸减半 canvas.height = img.height * 0.5; ctx.drawImage(img, 0, 0, canvas.width, canvas.height); canvas.toBlob(blob => { // 上传blob对象 }, 'image/jpeg', quality); }; img.src = URL.createObjectURL(file); }
- CDN加速:上传压缩后图片至CDN,利用边缘节点缓存减少加载时间。
🌐 3. URL到页面渲染过程
关键步骤:
- DNS解析:域名 → IP地址(本地缓存 → 递归查询根域、顶级域等)。
- TCP连接:三次握手建立可靠连接。
- HTTP请求:发送请求头/体,服务器返回响应。
- 解析渲染:
- 构建DOM树:解析HTML生成节点树。
- 构建CSSOM:解析CSS样式。
- 合成渲染树:合并DOM与CSSOM,计算布局与绘制。
🔍 4. DNS解析详细过程
分层查询流程:
- 浏览器缓存 → 系统缓存(hosts文件) → 路由器缓存。
- 本地DNS服务器 → 根域名服务器(返回顶级域地址) → .com域服务器 → 目标域名服务器。
CDN关联:
- 智能DNS解析用户位置,返回最近的CDN节点IP,加速资源访问。
🤝 5. 三次握手与四次挥手
- 三次握手(建立连接):
- 客户端→服务端:SYN=1, seq=x
- 服务端→客户端:SYN=1, ACK=1, seq=y, ack=x+1
- 客户端→服务端:ACK=1, seq=x+1, ack=y+1。
- 四次挥手(断开连接):
- 客户端→服务端:FIN=1
- 服务端→客户端:ACK=1(半关闭)
- 服务端→客户端:FIN=1
- 客户端→服务端:ACK=1(等待2MSL关闭)。
🧩 6. Class与函数式组件互用性
- Class组件使用Hooks:❌ 不可用,Hooks仅适用于函数组件。
- 函数组件模拟Class语法:✅ 可通过闭包+useState/useEffect实现状态管理,但违反设计原则。
⚡ 7. 组件优化与函数组件优势
优化方向 | Class组件 | 函数组件 |
---|---|---|
优化手段 | shouldComponentUpdate |
React.memo |
状态逻辑复用 | HOC或Render Props | Hooks(useState/useEffect) |
打包体积 | 较大(需继承) | 更小(无this绑定) |
性能 | 实例化开销大 | 轻量,适合频繁更新场景 |
函数组件核心优势:
- 逻辑复用更灵活(自定义Hooks)。
- 避免
this
指向问题,代码更简洁。
📦 8. Webpack工程化实践
常用能力:
- Loader:处理非JS资源(如
babel-loader
转译ES6+)。 - Plugin:扩展功能(
HtmlWebpackPlugin
生成HTML)。 - 代码分割:
SplitChunksPlugin
拆分公共代码。
兼容性方案:
- JS兼容:Babel +
@babel/preset-env
+core-js
按需polyfill。 - CSS兼容:PostCSS +
autoprefixer
自动加前缀。 - Polyfill服务:动态注入(如
polyfill.io
)。
✅ 9. 兼容性工作接受度
- 合理投入:若为必要业务需求(如政府/银行需兼容IE),可接受短期集中处理。
- 效率建议:
- 使用自动化工具(如ESLint检测语法兼容性)。
- 建立浏览器兼容矩阵,优先级分级处理。
💎 总结建议
- 原理深挖:如双向绑定需说清Vue2/Vue3差异,DNS解析关联CDN优化。
- 工程思维:图片压缩结合CDN、Webpack拆分polyfill提升效率。
- 框架理解:函数组件+Hooks是趋势,掌握逻辑复用与性能优化点。
接受兼容性工作需明确范围与工具支持,避免低效重复劳动。以上方案可大幅提升开发效率,缩短适配周期。
以下针对您提出的问题,结合前端核心原理与实践经验进行系统化解析:
🔐 一、登录 Token 存储与 Cookie 安全配置
Token 存本地 vs Cookie
- Token 存 LocalStorage 的原因
- SPA 架构适配:前后端分离项目中,前端需主动控制 Token 发送逻辑(如
Authorization: Bearer <token>
),LocalStorage API 更灵活。 - 规避 CSRF:Token 不自动随请求发送,避免因 Cookie 自动携带导致的跨站请求伪造风险。
- 跨域支持:LocalStorage 数据不会自动发送,适合跨域 API 调用场景。
- SPA 架构适配:前后端分离项目中,前端需主动控制 Token 发送逻辑(如
- Cookie 的适用场景
- 传统服务端渲染(SSR)项目,依赖自动发送机制。
- 需严格防御 XSS 时,配合
HttpOnly
属性(见下文)。
- Token 存 LocalStorage 的原因
配置 Cookie 防 JS 操作
通过服务端设置安全属性:Set-Cookie: token=abc123; HttpOnly; Secure; SameSite=Strict; Max-Age=3600
HttpOnly
:禁止 JavaScript 读取 Cookie,彻底防御 XSS 窃取。Secure
:仅通过 HTTPS 传输,防止中间人攻击。SameSite=Strict
:阻止跨站请求携带 Cookie,根治 CSRF。
🔄 二、协商缓存详解
- 流程:
- 首请 → 服务器返回资源 +
ETag
(哈希标识)或Last-Modified
(修改时间)。 - 再请 → 浏览器带
If-None-Match
(ETag值)或If-Modified-Since
(时间戳)。 - 服务器比对:
- 未变更 →
304 Not Modified
(空响应体,用本地缓存)。 - 已变更 →
200 OK
+ 新资源。
- 未变更 →
- 首请 → 服务器返回资源 +
- 对比强缓存:
特性 强缓存 协商缓存 网络请求 无(直接读缓存) 有(服务器验证) 响应码 200 (from disk/memory) 304 Not Modified 适用资源 静态资源(JS/CSS/图片) 频繁更新资源(如用户数据)
⚠️ 三、HTTP 状态码 401/402/403
状态码 | 含义 | 典型场景 |
---|---|---|
401 | Unauthorized | 未携带 Token 或 Token 无效(需重新登录)。 |
403 | Forbidden | 已认证但权限不足(如普通用户访问管理员接口)。 |
402 | Payment Required | 保留状态码,实际极少使用(原设计用于支付场景)。 |
⚙️ 四、Cache-Control 关键属性
属性 | 作用 |
---|---|
max-age=3600 | 资源有效期(秒),优先级高于 Expires 。 |
no-cache | 禁用强缓存,每次需向服务器验证(仍可缓存)。 |
no-store | 彻底禁用缓存(敏感数据场景)。 |
public | 允许代理服务器(CDN)缓存。 |
private | 仅允许浏览器缓存(禁止代理缓存)。 |
must-revalidate | 缓存过期后必须向服务器验证。 |
🖖 五、Vue 的 MVVM 模式
- 核心流程:
- ViewModel:Vue 实例,通过
Data Binding
实现:- 数据变化 → 自动更新 DOM(
v-bind
/{{}}
)。 - DOM 事件 → 自动更新数据(
v-on
/v-model
)。
- 数据变化 → 自动更新 DOM(
- 与传统 MVC 区别:无需手动操作 DOM(如 jQuery),由框架自动驱动视图更新。
- ViewModel:Vue 实例,通过
🧵 六、JS 单线程与浏览器多线程
JS 单线程原因:
- 避免 DOM 操作冲突:多线程同时修改 DOM 会导致渲染结果不可控(如一个线程删节点,另一个线程修改其样式)。
- 历史设计约束:作为浏览器脚本语言,最初定位为轻量级交互工具。
浏览器多线程协作:
线程 职责 JS 引擎线程 执行 JS 代码(主线程)。 GUI 渲染线程 解析 HTML/CSS、布局、绘制(与 JS 引擎互斥)。 异步 HTTP 线程 处理网络请求( fetch
/XMLHttpRequest
),完成后回调推入任务队列。定时器线程 管理 setTimeout
/setInterval
,计时结束回调入队。
📡 七、WebSocket 连接维护
- 心跳机制:
// 客户端定时发送心跳包 const ws = new WebSocket('wss://api.example.com'); const heartbeatInterval = setInterval(() => { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: 'heartbeat' })); } }, 30000); // 30秒一次 // 服务端超时断开:若未收到心跳,关闭连接
- 自动重连:监听
onclose
事件,尝试重新建立连接。 - 帧控制:
WebSocket.bufferedAmount
检查未发送数据量,避免拥塞。
📏 八、Vue 列表高度获取场景题
正确方案:使用 ref
获取 DOM 高度(无需等待网络请求)
<template>
<ul ref="listRef">
<li v-for="item in list" :key="item.id">{{ item.name }}</li>
</ul>
</template>
<script>
export default {
data() {
return { list: [] };
},
async mounted() {
// 1. 请求数据
this.list = await fetchList();
// 2. 数据更新后DOM已渲染,通过ref获取高度
this.$nextTick(() => {
const height = this.$refs.listRef.clientHeight;
console.log('列表高度:', height);
});
}
};
</script>
错误思路分析:
- Promise.all 封装请求:网络请求完成 ≠ DOM 渲染完成,需等待 Vue 更新视图。
- setTimeout 延迟获取:不可靠,渲染时间受设备性能影响。
关键点:
ref
绑定:直接访问渲染后的 DOM 元素。$nextTick
:确保 DOM 更新后执行回调,避免读取到旧布局。
💎 总结
- 安全存储:Token 放 LocalStorage 利于 SPA 控制,Cookie 需配置
HttpOnly
+Secure
+SameSite
三位一体防护。 - 缓存策略:静态资源强缓存(
max-age=31536000
),动态数据协商缓存(ETag
)。 - Vue 设计:MVVM 通过数据绑定解放手动 DOM 操作,
ref
+nextTick
是访问渲染后 DOM 的标准方案。 - 单线程本质:JS 单线程为避免 DOM 冲突,浏览器通过多线程协作支持异步。
面试技巧:遇到“获取渲染后高度”类问题,优先考虑
ref
直取 DOM 而非依赖请求状态,结合nextTick
确保时机准确。
以下是针对前端面试题的详细解答,结合核心原理与最佳实践,帮助您系统化掌握知识点:
1. 为什么选择前端开发?
- 核心动机:
- 即时反馈:所见即所得(如动画、交互效果),快速验证想法。
- 技术广度:涵盖视觉设计(CSS)、逻辑(JavaScript)、工程化(Webpack)、跨端(Electron/React Native)。
- 影响力:直接塑造用户体验,业务价值可视化强(如数据看板、高交互应用)。
2. React 生命周期(类组件)
React 生命周期分为三个阶段:
挂载阶段
constructor
:初始化 state,绑定方法。static getDerivedStateFromProps(props, state)
:根据 props 更新 state(需返回对象或null
)。render
:返回 JSX。componentDidMount
:DOM 操作、网络请求、订阅事件。
更新阶段
static getDerivedStateFromProps
(同上)。shouldComponentUpdate(nextProps, nextState)
:性能优化,返回false
阻止渲染。render
:重新渲染。getSnapshotBeforeUpdate(prevProps, prevState)
:DOM 更新前捕获信息(如滚动位置),返回值传给componentDidUpdate
。componentDidUpdate(prevProps, prevState, snapshot)
:更新后操作(如基于snapshot
调整 UI)。
卸载阶段
componentWillUnmount
:清理定时器、取消网络请求、移除订阅。
为何废弃
componentWillMount
等?
React Fiber 架构引入异步渲染,旧钩子可能被多次调用,导致副作用(如重复请求)。替代方案:
- 数据请求移至
componentDidMount
。- 状态派生用
getDerivedStateFromProps
。
3. JavaScript 事件循环(Event Loop)
- 单线程模型:JS 主线程执行同步任务,异步任务由浏览器的多线程模块处理(如网络请求线程、定时器线程)。
- 任务队列:
- 宏任务(Macro Task):
setTimeout
、setInterval
、I/O、UI 渲染。 - 微任务(Micro Task):
Promise.then
、MutationObserver
、queueMicrotask
。
- 宏任务(Macro Task):
- 执行顺序:
- 执行同步任务(调用栈)。
- 清空微任务队列(优先级高)。
- 取一个宏任务执行,重复步骤 2。
// 示例
setTimeout(() => console.log("宏任务"), 0);
Promise.resolve().then(() => console.log("微任务"));
// 输出顺序:微任务 → 宏任务
4. URL 输入到页面渲染的流程
- DNS 解析:域名 → IP 地址(查询本地缓存 → 递归查询根域)。
- TCP 连接:三次握手建立可靠传输。
- HTTP 请求:浏览器发送请求头 + 请求体。
- 服务器响应:返回状态码(如 200)、响应头、HTML 文件。
- 解析渲染:
- 构建 DOM 树:HTML 解析为节点树(遇到
<script>
阻塞解析)。 - 构建 CSSOM:解析 CSS 为样式树。
- 合成渲染树:合并 DOM + CSSOM,计算布局与绘制。
- 重排(Reflow)与重绘(Repaint):动态修改 DOM/CSS 触发。
- 构建 DOM 树:HTML 解析为节点树(遇到
5. DOM 树与 CSSOM 树的构建关系
- 并行下载:HTML 解析时,预解析线程提前加载 CSS/JS 资源。
- 串行构建:
- CSSOM 构建会阻塞渲染树合成(避免无样式内容闪烁)。
- JS 执行会阻塞 DOM 构建(JS 可能修改 DOM/CSS)。
优化建议:CSS 内联或优先加载,JS 用
async
/defer
避免阻塞。
6. HTTP 状态码 301、302、304
状态码 | 含义 | 典型场景 |
---|---|---|
301 | 永久重定向 | 域名迁移(SEO 权重转移) |
302 | 临时重定向 | 登录跳转(保留原 URL SEO 权重) |
304 | 资源未修改(协商缓存) | 浏览器缓存有效,服务器直接返回空响应 |
7. 强缓存与协商缓存
强缓存(无需请求服务器)
- 响应头设置:
Cache-Control: max-age=3600 # 有效期(秒) Expires: Wed, 21 Oct 2025 07:28:00 GMT # 过期时间(HTTP/1.0)
- 缓存位置:
Memory Cache
(内存)、Disk Cache
(硬盘)。
协商缓存(需服务器验证)
- 响应头设置:
ETag: "d41d8cd98f00b204e9800998ecf8427e" # 资源哈希标识 Last-Modified: Wed, 21 Oct 2025 07:28:00 GMT # 最后修改时间
- 请求头验证:
If-None-Match: "d41d8cd98f00b204e9800998ecf8427e" # 匹配 ETag If-Modified-Since: Wed, 21 Oct 2025 07:28:00 GMT # 匹配修改时间
- 未变更 → 304 Not Modified(复用缓存)。
- 已变更 → 200 OK + 新资源。
8. 浏览器缓存类型
类型 | 特性 |
---|---|
Memory Cache | 内存缓存,快速但容量小,页面关闭即失效(适合静态资源) |
Disk Cache | 硬盘缓存,持久化存储(适合大文件如 CSS/JS) |
Service Worker | 离线缓存(需 HTTPS),可拦截请求 |
Push Cache | HTTP/2 服务器推送缓存(会话级,优先级最低) |
9. 登录校验方法
Token 认证(主流方案)
- 登录流程:
- 用户提交账号密码 → 服务器验证 → 返回 Token(如 JWT)。
- Token 存储:
- 安全方案:
LocalStorage
+axios
拦截器添加Authorization: Bearer <token>
。 - 防御 XSS:避免敏感操作,关键接口需二次验证(如短信)。
- 安全方案:
- Token 刷新:
- 短期 Token(1 小时) + 长期 Refresh Token(7 天),到期后自动刷新。
Session-Cookie 认证
- 流程:服务器生成 Session ID 存入 Cookie(需设置
HttpOnly
、Secure
、SameSite
)。 - 缺点:跨域限制、CSRF 风险(需配合 Token 使用)。
10. 用 Hook 实现 componentWillUnmount
在函数组件中使用 useEffect
的清理函数模拟:
import { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
// 组件挂载时执行(类似 componentDidMount)
const timer = setInterval(() => console.log('Tick'), 1000);
// 返回清理函数(类似 componentWillUnmount)
return () => {
clearInterval(timer);
console.log('定时器已清理');
};
}, []); // 空依赖表示仅执行一次
return <div>组件内容</div>;
}
关键点:
useEffect
的依赖数组为[]
时,仅挂载/卸载执行一次。- 清理函数在组件卸载前自动触发,用于取消订阅、定时器等。
总结建议
- 原理深挖:如事件循环需说清微任务优先机制,缓存设置需区分
max-age
与ETag
场景。 - 框架对比:React 生命周期迁移到 Hooks 是趋势(面试可提
useEffect
替代方案)。 - 实战结合:举例优化手段(如 DNS 预解析、CDN 缓存静态资源)。
面试时遇到开放性问题(如“为什么选前端”),关联业务价值(如“通过懒加载提升电商首屏转化率30%”)更易脱颖而出。