本轮要点: 事件循环、前端存储、React 生命周期、列表和key、react 事件机制、react 渲染机制、浏览器的同源策略、CSRF攻击、 XSS防御、跨域资源共享(CORS)
本轮共 23 道题。答案默认折叠,便于先自行作答。
1. 介绍一下你觉的比较有成就感的项目
题目要点
STAR法则
参考答案
老传统了,基于简历挖掘一个最有亮点的项目
2. 遇到的最有挑战的技术是什么
题目要点
略
参考答案
略
3. 项目技术选型是如何衡量的
题目要点
- 列举技术选型的核心衡量标准
- 说明评估流程和实操方法
- 强调综合权衡,避免片面判断
- 结合项目需求和团队实际展开
参考答案
考察点
● 理解技术选型的核心因素及其权衡原则
考察对项目需求、团队能力与技术趋势的综合分析能力。
● 掌握技术选型中性能、开发效率、维护性等多维度考虑
考察系统架构设计和项目管理思维。
参考答案
一、技术选型的衡量维度
1. 业务需求匹配度
- 技术是否满足项目的功能需求、性能要求和扩展性。
- 是否支持项目特有的场景或复杂逻辑。
2. 团队技术栈和能力
- 团队是否熟悉该技术,学习成本及培训投入。
- 技术社区活跃度,文档完善程度,易用性。
3. 性能表现
- 技术的响应速度、负载能力、资源消耗。
- 是否支持性能优化方案(如 SSR、缓存策略等)。
4. 生态和兼容性
- 技术生态是否丰富,有无成熟的第三方库和工具支持。
- 与现有系统和平台的兼容性。
5. 维护成本与长期可持续性
- 技术的稳定性和升级频率。
- 社区支持和企业采用情况,避免技术孤岛。
6. 开发效率
- 开发周期和上线速度。
- 工具链和开发体验是否高效。
7. 安全性和合规性
- 技术在安全防护、隐私保护方面的能力。
- 是否满足行业合规标准。
二、技术选型的评估流程
- 需求调研:明确项目功能和非功能需求。
- 方案调研:收集备选技术方案及案例。
- 技术预研和验证:通过小型原型测试技术可行性。
- 成本评估:综合考量开发、维护、培训等成本。
- 风险分析:识别潜在风险,如社区活跃度、技术稳定性。
- 决策和方案确定:结合以上评估,选择最优技术方案。
三、总结
- 技术选型是一个多维度权衡过程,既要满足业务需求,也需考虑团队与生态。
- 注重长期维护和性能表现,避免盲目追新或仅凭个人偏好。
- 通过科学的评估流程和数据支撑,做出理性选择。
4. 项目中的性能监控和错误上报讲一下
题目要点
- 列举关键性能指标及监控方法
- 说明错误类型、捕获机制和上报流程
- 强调数据分析与持续优化的重要性
- 结合实际项目实践进行阐述
参考答案
考察点
● 理解前端性能监控的关键指标和监控手段
考察对用户体验优化和实时监控的认知。
● 掌握错误捕获、收集与上报机制
考察对异常管理和系统稳定性的保障方法。
参考答案
一、性能监控
1. 关键性能指标(KPI)
- 白屏时间(FP,First Paint):页面首次绘制内容所需时间。
- 首屏时间(FCP,First Contentful Paint):首个内容渲染时间。
- 页面加载时间(LCP,Largest Contentful Paint):最大可视内容加载完成时间。
- 交互准备时间(TTI,Time to Interactive):页面可交互时间。
- 阻塞时间(TBT,Total Blocking Time):主线程被阻塞的总时间。
- 资源加载时间、请求数量等。
2. 监控手段
- 利用浏览器原生 API:
Performance API(如performance.timing、PerformanceObserver)。 - 集成第三方监控平台(如 Sentry、NewRelic、Datadog 等)。
- 自定义打点埋点,采集关键操作和性能数据。
- 结合日志系统,监控网络请求时延、错误等。
3. 数据分析与告警
- 实时统计指标波动,设置阈值告警。
- 结合用户设备信息、网络环境进行性能归因分析。
二、错误上报
1. 错误类型
- JavaScript 运行时错误(try-catch、
window.onerror、window.addEventListener('error')捕获)。 - Promise 未捕获的异常(
unhandledrejection事件)。 - 资源加载失败(图片、脚本、样式等)。
- 接口请求失败(通过请求拦截器捕获网络异常)。
- 业务逻辑异常(自定义捕获并上报)。
2. 错误捕获机制
- 全局监听错误事件收集异常信息。
- 结合错误堆栈、用户操作轨迹、设备环境等上下文数据。
- 使用 try-catch 包裹关键代码块。
- 对异步代码的错误做好捕获和处理。
3. 上报方式
- 发送错误信息至日志收集服务器或监控平台。
- 支持批量发送,减少网络请求压力。
- 可通过 Beacon API 或 Fetch/XHR 实现可靠上报。
4. 反馈与修复
- 对错误进行分类和优先级排序。
- 结合版本信息定位问题根源。
- 快速响应修复,优化用户体验。
三、总结
- 性能监控和错误上报是保障前端应用稳定和流畅体验的重要手段。
- 通过多维度指标和全链路数据采集,实现实时监控与异常预警。
- 错误上报结合丰富上下文,有助于精准定位和快速迭代。
5. 登录的认证机制是?如何持久化登录的
题目要点
- 描述常见登录认证方式及其流程
- 讲解 Cookie、LocalStorage、SessionStorage 在持久化中的角色
- 说明安全措施及登录状态管理策略
- 结合场景讨论持久化优劣和注意事项
参考答案
考察点
● 理解常见的登录认证机制及其原理
考察对身份验证流程和安全保障的掌握。
● 掌握前端登录状态持久化的多种实现方式
考察对安全性和用户体验的综合考虑。
参考答案
一、登录认证机制
1. 基础认证流程
- 用户提交账号密码,前端通过 HTTPS 发送给服务端。
- 服务端验证用户身份,验证通过后生成认证凭证(如 Token 或 Session)。
- 前端保存凭证,后续请求携带凭证进行身份验证。
2. 常见认证方式
| 认证方式 | 说明 | 优缺点 |
|---|---|---|
| Session + Cookie | 服务器端保存登录状态,前端通过 Cookie 自动携带 Session ID。 | 简单易用,但需服务器维护状态,扩展性较差。 |
| JWT(JSON Web Token) | 无状态令牌,前端保存 Token(一般存 localStorage 或 Cookie),每次请求携带。 | 扩展性好,跨域支持好,但需防止 Token 泄露。 |
| OAuth / OAuth2 | 第三方授权登录,用户通过授权服务器获取访问令牌。 | 安全,适合第三方登录,但实现复杂。 |
3. 认证安全注意点
- 使用 HTTPS 保证传输安全。
- Token 或 Cookie 设置合理的过期时间和权限范围。
- 防止 XSS、CSRF 等安全风险。
- 服务器端做好 Token 校验和失效管理。
二、登录状态持久化
1. 使用 Cookie
- 适合存储短期登录信息,浏览器自动携带。
- 可设置 HttpOnly、防止 XSS。
- 受限于跨域和存储大小。
2. 使用 LocalStorage / SessionStorage
- LocalStorage:持久化存储,关闭浏览器后依然存在,适合长久登录状态。
- SessionStorage:会话级存储,关闭标签页即失效。
- 需注意 XSS 攻击风险,不建议存储敏感信息。
3. Token 管理
- 结合 Cookie 或 Storage 保存 JWT Token。
- 支持刷新 Token(Refresh Token)机制,保持登录连续性。
- 前端需根据 Token 有效期及时刷新或登出。
4. 自动登录方案
- 利用浏览器缓存或本地存储保存凭证。
- 启动时检查凭证有效性,自动恢复登录状态。
- 结合接口验证和超时机制保证安全。
三、总结
- 登录认证机制多样,需结合业务安全性和扩展性选型。
- 登录状态持久化需兼顾用户体验与安全风险,合理选择存储方案。
- 结合前后端协作,实现安全、可靠的登录管理。
6. Token 如何存储?
题目要点
- 列举 Cookie、LocalStorage、SessionStorage 及内存存储方式
- 说明各自优缺点及安全风险
- 强调防护措施(HttpOnly、CSP、SameSite)
- 根据应用场景推荐存储方案
参考答案
考察点
● 理解不同存储方案对登录 Token 的安全性和可用性影响
考察前端安全防护与状态管理能力。
● 掌握 Token 存储的优缺点及应用场景
考察实际项目中合理选用存储方案的能力。
参考答案
一、常见的登录 Token 存储方式
1. Cookie 存储
优点
- 浏览器自动携带 Cookie,免去手动添加请求头。
- 可设置
HttpOnly,防止 JavaScript 访问,降低 XSS 攻击风险。 - 支持设置
Secure(仅 HTTPS 传输)和SameSite(防止 CSRF)。
- 浏览器自动携带 Cookie,免去手动添加请求头。
缺点
- 需要服务器端配合设置。
- 容易受到 CSRF 攻击,需配合防护策略。
- 跨域使用受限。
- 需要服务器端配合设置。
适用场景
- 传统 Web 应用,依赖服务器维护会话。
- 对安全性要求较高时优先选择。
- 传统 Web 应用,依赖服务器维护会话。
2. Web Storage 存储(LocalStorage / SessionStorage)
优点
- 简单易用,前端控制灵活。
- LocalStorage 持久化,SessionStorage 会话级。
- 跨域时容易操作。
- 简单易用,前端控制灵活。
缺点
- 无法设置 HttpOnly,容易被 XSS 攻击窃取。
- 需要手动将 Token 附加到请求头(如 Authorization)。
- 无法设置 HttpOnly,容易被 XSS 攻击窃取。
适用场景
- SPA 应用,配合前后端分离的 Token 认证。
- 安全措施到位(如内容安全策略CSP)时可用。
- SPA 应用,配合前后端分离的 Token 认证。
3. 内存存储(变量或状态管理)
优点
- 不持久化,关闭页面或刷新即清除,减少泄露风险。
- 适合临时会话。
- 不持久化,关闭页面或刷新即清除,减少泄露风险。
缺点
- 页面刷新导致登录状态丢失,用户体验较差。
适用场景
- 对安全要求极高且不介意频繁登录的应用。
二、存储方案的安全考虑
防止 XSS
- 优先使用 HttpOnly Cookie。
- 采用严格的内容安全策略(CSP)限制脚本注入。
- 优先使用 HttpOnly Cookie。
防止 CSRF
- 设置 Cookie 的 SameSite 属性为
Strict或Lax。 - 对敏感请求做 CSRF Token 验证。
- 设置 Cookie 的 SameSite 属性为
Token 有效期和刷新
- 设计合理的 Token 过期机制和刷新流程。
- 避免长时间存储敏感信息。
- 设计合理的 Token 过期机制和刷新流程。
三、总结
- Cookie 存储安全性较高,适合传统及安全敏感应用。
- LocalStorage 便于操作,适合前后端分离场景,但需防范 XSS。
- 结合业务和安全需求,合理选择存储方式,并配合安全防护措施。
7. 为什么 LocalStorage 可能不安全
题目要点
- 重点指出无 HttpOnly 导致脚本可访问 LocalStorage
- 说明 XSS 攻击是主要威胁
- 提出避免存储敏感信息和加强安全策略的建议
- 强调持久化存储的安全风险
参考答案
考察点
● 理解 LocalStorage 的安全风险及攻击面
考察对前端存储安全隐患的认知。
● 掌握常见安全攻击手段及防护策略
考察防范 XSS 等攻击的能力。
参考答案
一、LocalStorage 不安全的主要原因
1. 无 HttpOnly 属性保护
- LocalStorage 是前端 JavaScript 可直接访问的存储空间。
- 不支持
HttpOnly标记,浏览器无法阻止脚本访问。 - 一旦页面存在跨站脚本攻击(XSS),恶意脚本可轻易读取并窃取 LocalStorage 中的敏感数据(如 Token)。
2. 易受到 XSS 攻击影响
- XSS 攻击使攻击者注入恶意代码,读取 LocalStorage。
- 被盗取的 Token 可能被用来伪造身份,造成账户安全隐患。
3. 数据存储持久性导致风险扩大
- LocalStorage 数据持久保存,即使关闭浏览器也存在。
- 攻击者获取后可长时间滥用,增加安全风险。
二、潜在攻击场景示例
- 恶意脚本通过输入框注入,读取 LocalStorage 中 Token。
- 令牌被发送至攻击者服务器,实现账号劫持。
- 用户无感知,造成隐私泄露和安全事故。
三、防护措施建议
1. 避免在 LocalStorage 中存储敏感信息
- 如登录 Token、密码等。
- 改用 HttpOnly Cookie 存储。
2. 加强前端安全策略
- 使用内容安全策略(CSP)限制可执行脚本来源。
- 严格输入校验和输出编码,防止 XSS 注入。
3. 实施多层安全防护
- 结合 CSRF 防护、Token 过期刷新策略。
- 服务端对异常行为进行监控和限制。
四、总结
- LocalStorage 不支持 HttpOnly,容易被 XSS 攻击窃取敏感数据。
- 持久化存储特性增加风险面,需谨慎存储。
- 结合安全策略和存储方式选择,保障用户信息安全。
8. 如何实现无感刷新 Token
题目要点
- 说明双 Token 机制及刷新原理
- 阐述前端拦截 401 状态实现刷新逻辑
- 讲解并发刷新请求的处理策略
- 结合代码示例说明实现细节
参考答案
考察点
● 理解 Token 刷新的机制和目的
考察对安全与用户体验平衡的掌握。
● 掌握前端如何配合后端实现无感刷新流程
考察实际开发中鉴权管理和状态维护能力。
参考答案
一、无感刷新 Token 的概念及原理
- 无感刷新 Token 指用户在 Token 过期时,前端自动使用刷新 Token(Refresh Token)获取新的访问 Token(Access Token),无需用户重新登录。
- 目的是提升用户体验,保证持续登录状态的同时保证安全。
二、实现流程
1. 双 Token 机制
- Access Token:短生命周期(如 15 分钟),用于身份认证。
- Refresh Token:长生命周期(如 7 天),用于换取新的 Access Token。
2. 典型刷新步骤
- 前端正常请求接口时带上 Access Token。
- 若接口响应返回 Access Token 过期错误(如 401 Unauthorized),前端触发刷新流程。
- 前端使用 Refresh Token 调用后端刷新接口,获取新的 Access Token(及可能新的 Refresh Token)。
- 前端更新本地存储的 Token。
- 重新发起之前失败的请求,用户无感知完成流程。
- 若 Refresh Token 也过期,则跳转登录。
3. 前端实现要点
- 拦截 HTTP 响应状态码(401)实现刷新逻辑。
- 防止并发多次刷新请求(使用锁或队列机制)。
- 安全存储 Refresh Token,通常放 Cookie(HttpOnly)或安全存储方案。
- 刷新失败时,清理状态并引导用户登录。
三、示例代码(基于 Axios 拦截器)
let isRefreshing = false;
let requestsQueue = [];
axios.interceptors.response.use(
response => response,
error => {
const { config, response } = error;
if (response && response.status === 401) {
if (!isRefreshing) {
isRefreshing = true;
return refreshToken().then(newToken => {
isRefreshing = false;
// 更新Token
setToken(newToken);
// 重新发起排队的请求
requestsQueue.forEach(cb => cb(newToken));
requestsQueue = [];
// 重新发起当前请求
config.headers['Authorization'] = `Bearer ${newToken}`;
return axios(config);
}).catch(() => {
// 刷新失败,跳登录
redirectLogin();
return Promise.reject(error);
});
} else {
// 刷新过程中,将请求加入队列等待Token更新
return new Promise(resolve => {
requestsQueue.push(token => {
config.headers['Authorization'] = `Bearer ${token}`;
resolve(axios(config));
});
});
}
}
return Promise.reject(error);
}
);
function refreshToken() {
// 调用刷新Token接口,返回新的Access Token
return axios.post('/auth/refresh-token').then(res => res.data.accessToken);
}
四、总结
- 无感刷新通过短生命周期 Access Token + 长生命周期 Refresh Token 实现。
- 前端需配合后端刷新接口,自动更新 Token,保证用户体验。
- 需考虑刷新流程的并发控制和安全存储。
- 失败时合理处理跳转登录,保障安全。
9. 分析下面JavaScript代码的输出顺序及原因
console.log('1');
Promise.resolve().then(() => console.log('2'));
setTimeout(() => console.log('3'), 0);
new Promise(res => {
console.log('4');
res();
}).then(() => console.log('5'));
// 输出顺序及原因?
题目要点
- 说明同步代码优先执行
- Promise.then 属于微任务,微任务优先于宏任务执行
- setTimeout 是宏任务,延迟执行
- 结合具体代码说明执行顺序及原因
参考答案
考察点
● 理解 JavaScript 事件循环(Event Loop)机制
考察对宏任务(Macrotask)和微任务(Microtask)调度的掌握。
● 掌握 Promise、setTimeout、同步代码的执行顺序
考察异步执行机制和执行栈的基本概念。
参考答案
一、代码执行及输出顺序分析
console.log('1');
Promise.resolve().then(() => console.log('2'));
setTimeout(() => console.log('3'), 0);
new Promise(res => {
console.log('4');
res();
}).then(() => console.log('5'));
输出顺序:
1
4
2
5
3
二、执行过程详解
1. 同步代码先执行
console.log('1'):同步,立即输出1。new Promise构造函数内的同步代码执行,console.log('4')立即输出4。- Promise 内的
res()调用表示该 Promise 立即 resolved。
2. 微任务队列(Promise.then)
Promise.resolve().then(() => console.log('2'))以及new Promise(...).then(() => console.log('5'))的.then()回调会被加入微任务队列。- 同步代码执行完毕后,事件循环进入微任务阶段,依次执行微任务:先输出
2,再输出5。
3. 宏任务队列(setTimeout)
setTimeout(() => console.log('3'), 0)放入宏任务队列,等待当前执行栈和所有微任务执行完毕后才执行。- 最后输出
3。
三、总结
- 同步代码优先执行,按顺序输出
1、4。 - 微任务(Promise.then)在当前宏任务结束后立即执行,输出
2、5。 - 宏任务(setTimeout)等待下一轮事件循环,最后输出
3。 - 理解事件循环机制是掌握 JS 异步执行关键。
10. process.nextTick 在 Node.js 中如何影响顺序
题目要点
- 解释
process.nextTick作用及其优先级 - 对比 Promise 微任务,强调执行顺序
- 结合示例说明输出顺序
- 提及事件循环饥饿风险及使用注意
参考答案
考察点
● 理解 Node.js 事件循环模型及微任务机制
考察对 process.nextTick 与 Promise 微任务区别的掌握。
● 掌握 process.nextTick 的执行时机和优先级
考察对异步任务调度顺序的深入理解。
参考答案
一、process.nextTick 的定义与作用
process.nextTick(callback)是 Node.js 提供的特殊异步调度 API。- 它会将回调函数放入“next tick 队列”,确保在当前执行栈结束后立即执行,优先于 Promise 微任务。
二、Node.js 事件循环中的执行顺序
- Node.js 事件循环大致分为几个阶段:Timers、Pending Callbacks、Idle, Prepare、Poll、Check、Close Callbacks。
- 每个阶段结束后,会清空微任务队列。
- Node.js 独有的
process.nextTick队列优先于 Promise 微任务执行,且会在每个事件循环阶段开始之前执行。
三、process.nextTick 与 Promise 微任务的区别
| 特性 | process.nextTick | Promise.then(微任务) |
|---|---|---|
| 执行时机 | 当前阶段结束后立即执行 | 当前阶段后执行,优先级稍低 |
| 是否阻塞事件循环 | 可能阻塞,连续调用导致“nextTick 饥饿” | 不会阻塞事件循环 |
| 适用场景 | 需要尽快执行的回调,优先于微任务 | 标准微任务调度 |
四、示例说明执行顺序
console.log('start');
process.nextTick(() => {
console.log('nextTick 1');
});
Promise.resolve().then(() => {
console.log('promise 1');
});
process.nextTick(() => {
console.log('nextTick 2');
});
Promise.resolve().then(() => {
console.log('promise 2');
});
console.log('end');
输出顺序:
start
end
nextTick 1
nextTick 2
promise 1
promise 2
- 同步代码先执行:
start和end。 - 所有
process.nextTick回调紧随其后执行,优先于 Promise 微任务。 - 最后执行 Promise 的
.then回调。
五、总结
process.nextTick优先于 Promise 微任务执行,是 Node.js 特有的微任务队列。- 适用于需要在当前阶段立即执行的异步操作。
- 过度使用可能导致事件循环饥饿,阻塞 I/O 等其他操作。
- 了解两者差异,有助于合理安排异步任务顺序和性能优化。
11. async/await 如何被编译为 Promise?其执行顺序与原生 Promise 的差异
题目要点
- 说明 async/await 编译为 Promise 链的原理
- 强调 async 函数同步执行至第一个 await
- 对比纯 Promise 的异步微任务行为
- 用示例说明执行顺序及事件循环中的表现
参考答案
考察点
● 理解 async/await 的本质及其与 Promise 的关系
考察对异步代码语法糖的实现机制掌握。
● 掌握 async/await 编译后的执行流程与事件循环中任务调度差异
考察对微任务和宏任务执行顺序的理解。
参考答案
一、async/await 的本质与编译原理
1. async 函数本质
async函数是语法糖,内部自动将函数返回值包装成 Promise。- 函数体中可以使用
await,暂停执行,等待 Promise 解决(resolve)后继续执行。
2. await 的实现
await expr相当于:先对表达式expr调用Promise.resolve(expr),等待其状态变为 fulfilled,再继续执行后续代码。- 实际上,
await后的代码被拆分成多个 Promise 的.then回调链。
3. 编译示例(简化版)
async function foo() {
const res = await bar();
console.log(res);
}
等价于:
function foo() {
return Promise.resolve()
.then(() => bar())
.then(res => {
console.log(res);
});
}
二、执行顺序差异分析
1. async 函数调用即同步执行至第一个 await
- 调用 async 函数时,函数体会同步执行,直到遇到第一个
await。 - 遇到
await会暂停函数执行,后续代码作为微任务放入 Promise.then队列。
2. Promise 与 async/await 微任务调度差异
await后的代码执行实际上是基于 Promise.then的微任务。- 但 async 函数的起始执行(即函数体中第一个 await 之前的代码)是同步的。
- 纯 Promise 链的所有
.then回调均是异步的微任务,整个链条不会阻塞同步代码。
3. 示例说明
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
async1();
console.log('script end');
输出顺序:
script start
async1 start
async2
script end
async1 end
async1()调用时,async1 start和async2()里的同步部分立即执行。await async2()暂停async1后续代码执行。script end同步执行。- 之后,
async1 end作为微任务执行。
三、总结
- async/await 是基于 Promise 的语法糖,编译成 Promise 链。
- async 函数体同步执行至第一个 await,后续作为微任务异步执行。
- Promise
.then回调全部异步执行,无同步阻塞。 - 理解执行时机差异,有助于调试和优化异步代码。
12. 项目中Node.js 如何实现高并发
题目要点
- 讲解 Node.js 事件驱动异步非阻塞特性
- 多进程 Cluster 使用及负载均衡方案
- 异步优化、缓存、连接池等细节
- 结合示例代码说明实践方法
参考答案
考察点
● 理解 Node.js 的单线程异步非阻塞模型及其高并发原理
考察对 Node.js 架构特点和并发处理方式的深入理解。
● 掌握多进程、多线程、负载均衡等高并发实战策略
考察项目中提升性能和扩展性的综合能力。
参考答案
一、Node.js 高并发的核心原理
- 事件驱动 + 异步非阻塞 I/O
Node.js 采用单线程事件循环模型,通过异步非阻塞 I/O 调度,避免线程切换和阻塞,提高资源利用率。 - 单线程避免线程开销
线程切换成本低,但线程数量多时资源竞争激烈,Node.js 通过单线程减少上下文切换和锁竞争,提升响应速度。
二、实现高并发的常用方法
1. 多进程(Cluster 模式)
- 利用 Node.js
cluster模块,启动多个 Worker 进程,利用多核 CPU。 - 主进程负责负载均衡,分发请求给 Worker。
- 进程间相互独立,避免共享状态竞争。
2. 负载均衡(反向代理)
- 使用 Nginx、HAProxy 等做反向代理,实现请求均匀分配。
- 配合 Cluster 多进程,分摊压力,提升吞吐量。
3. 进程间通信(IPC)
- Worker 进程间可通过 IPC 共享必要数据或协调任务。
- 通过消息传递保证数据一致性。
4. 异步优化
- 保证代码无阻塞,尤其避免同步操作(如文件读写、数据库查询)。
- 使用异步 API,合理控制异步并发数量。
5. 连接池和缓存
- 数据库连接池复用连接,减少连接开销。
- 使用 Redis、Memcached 等缓存热点数据,降低数据库压力。
6. 限流与降级
- 限制客户端请求频率,避免短时间内过载。
- 设计合理的降级策略保证服务稳定。
7. 服务拆分与微服务
- 将单体应用拆分为多个服务,分散压力。
- 便于独立扩展和维护。
三、示例:Cluster 简单用法
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log(`Master ${process.pid} is running`);
// Fork workers
for(let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`worker ${worker.process.pid} died`);
cluster.fork(); // 自动重启
});
} else {
// Workers can share any TCP connection
http.createServer((req, res) => {
res.writeHead(200);
res.end('Hello World\n');
}).listen(8000);
console.log(`Worker ${process.pid} started`);
}
四、总结
- Node.js 单线程异步模型适合高并发,但需要避免阻塞。
- 利用多进程和负载均衡充分发挥多核优势。
- 异步代码优化、缓存、连接池是性能关键。
- 限流降级和服务拆分保证系统稳定性和扩展性。
13. 事件循环各阶段的任务类型是什么
题目要点
- 列出事件循环的各阶段及对应任务类型
- 明确宏任务和微任务的区别和执行时机
- 结合浏览器和 Node.js 适当说明差异
- 强调微任务优先级高于宏任务
参考答案
考察点
● 理解事件循环(Event Loop)整体架构及各阶段作用
考察对浏览器或 Node.js 事件循环机制的系统性理解。
● 掌握宏任务(Macrotask)和微任务(Microtask)的区别及归属阶段
考察异步任务调度和执行顺序的深入认知。
参考答案
一、事件循环概述
- 事件循环是 JavaScript 运行时管理任务执行的机制。
- 它不断循环执行任务队列,保证异步代码有序执行。
二、浏览器事件循环阶段及任务类型
| 阶段名称 | 任务类型及功能描述 |
|---|---|
| 1. 定时器阶段 (Timers) | 执行 setTimeout 和 setInterval 回调(已到期的定时任务) |
| 2. 网络回调阶段 (Pending Callbacks) | 处理某些系统操作的回调,如 TCP 连接错误、关闭等 |
| 3. 空闲、准备阶段 (Idle, Prepare) | 浏览器内部准备阶段,供引擎使用,用户不可见 |
| 4. 轮询阶段 (Poll) | 从队列取事件回调执行,如 I/O 回调,若队列为空则等待新事件 |
| 5. 检查阶段 (Check) | 执行 setImmediate 的回调(仅 Node.js 环境) |
| 6. 关闭回调阶段 (Close Callbacks) | 处理关闭事件,如 socket 关闭时的回调 |
三、任务类型分类
| 类型 | 特点 | 例子 |
|---|---|---|
| 宏任务 | 队列任务,执行一个任务后才执行下一个宏任务 | setTimeout、setInterval、I/O回调、UI渲染等 |
| 微任务 | 当前宏任务执行结束后立即执行的任务队列 | Promise.then、MutationObserver、process.nextTick(Node.js) |
- 微任务总是在每个宏任务后执行,优先级高于下一个宏任务。
- 微任务执行期间产生的新微任务会继续执行,直到队列清空。
四、事件循环示意流程(浏览器环境)
- 执行全局同步代码
- 执行宏任务队列中第一个任务
- 执行所有微任务,直到队列清空
- 进行渲染更新(可能)
- 进入下一个宏任务循环
五、总结
- 事件循环包含多个阶段,每个阶段处理不同类型的任务。
- 宏任务是主任务,微任务紧跟宏任务后执行。
- 理解阶段和任务类型,有助于调试异步代码和性能优化。
14. 阻塞 I/O 与非阻塞 I/O 的底层实现区别
题目要点
- 定义阻塞与非阻塞 I/O 及其区别
- 描述系统调用行为及线程状态差异
- 介绍事件驱动机制及多路复用技术
- 结合性能和编程复杂度对比总结
参考答案
考察点
● 理解阻塞 I/O 和非阻塞 I/O 的基本概念
考察对操作系统底层 I/O 模型的掌握。
● 掌握两者底层实现机制及对应用程序的影响
考察对异步编程模型和性能优化的理解。
参考答案
一、阻塞 I/O 与非阻塞 I/O 的定义
- 阻塞 I/O(Blocking I/O)
当应用程序发起 I/O 操作后,调用线程会被挂起,直到操作完成才继续执行后续代码。 - 非阻塞 I/O(Non-blocking I/O)
应用程序发起 I/O 操作后,如果无法立即完成,调用线程不会等待,而是立即返回,应用程序可以继续执行其他任务,稍后通过通知机制获取结果。
二、底层实现区别
1. 阻塞 I/O
- 应用程序调用系统调用(如
read())时,内核会阻塞该进程或线程,直到数据准备好。 - CPU 会挂起该线程,调度其他线程运行,效率较低。
- 典型同步模型,简单但可能导致资源浪费。
2. 非阻塞 I/O
- 应用程序设置文件描述符为非阻塞模式(
O_NONBLOCK),调用read()时如果数据未准备好立即返回错误(如EAGAIN)。 - 应用程序需要不断轮询或结合事件通知机制(如
select、poll、epoll)来检测何时数据可读。 - 通过事件驱动的方式通知应用程序,避免线程挂起。
三、事件驱动与异步机制的支持
- IO多路复用(select/poll/epoll)
在非阻塞模式下,内核通过多路复用技术监听多个文件描述符事件,事件触发时通知应用程序。 - 异步 I/O(AIO)
内核直接完成 I/O 操作并通过回调或信号通知应用程序,完全解耦调用和完成。
四、应用层影响
| 特点 | 阻塞 I/O | 非阻塞 I/O |
|---|---|---|
| 编程模型 | 简单,同步阻塞 | 复杂,需要事件通知和状态管理 |
| 资源利用 | 线程挂起导致 CPU 和内存资源浪费 | 线程非阻塞,提高资源利用率 |
| 性能 | 低,线程阻塞和切换开销大 | 高,适合高并发和多连接场景 |
| 典型应用 | 传统同步服务器 | Node.js、Nginx 等异步事件驱动服务器 |
五、总结
- 阻塞 I/O 通过挂起线程等待数据,简单但效率低。
- 非阻塞 I/O 结合事件通知机制,避免线程阻塞,提高并发性能。
- 非阻塞 I/O 是现代高性能服务器和异步框架的基础。
15. React中,为什么合成事件/生命周期中 setState 是异步
题目要点
- 说明 React 异步批处理的性能目的
- 阐述合成事件机制如何触发批量更新
- 解释生命周期中异步更新原理
- 结合代码示例对比同步与异步场景
参考答案
考察点
● 理解 React setState 的异步更新机制
考察对 React 渲染优化和状态管理的理解。
● 掌握合成事件、生命周期中 setState 异步执行的原理及表现
考察对 React 事件系统和更新调度的深入认知。
参考答案
一、setState 异步更新的背景与定义
- React 中调用
setState后,状态不会立即更新,而是被合并并批量处理。 - 这种“异步”指的是状态更新不会立即生效,渲染和状态更新会延迟到合适时机合并执行。
二、为什么合成事件和生命周期中 setState 是异步?
1. 性能优化:批量更新机制
- React 为了减少重复渲染和提高性能,将多个
setState合并成一次更新。 - 合成事件和生命周期函数内,React 会开启批处理模式,收集多次状态更新,最后统一触发一次重新渲染。
2. 合成事件机制
- React 事件系统是基于事件委托实现的合成事件。
- 合成事件回调期间,React 自动开启批处理,所有
setState更新被合并。 - 只有等事件处理完成后,React 才执行更新渲染。
3. 生命周期函数中的异步更新
- 在生命周期函数(如
componentDidMount、componentDidUpdate)内,setState同样属于批量更新上下文。 - React 统一管理更新,避免多次重复渲染。
三、setState 何时是同步?
- 在原生事件回调或
setTimeout、Promise.then等异步环境中,React 默认关闭批处理,setState是同步执行。 - React 18 引入了自动批处理(Automatic Batching),更多场景实现异步批量更新,但之前版本此处是同步。
四、示例对比
// 合成事件中setState是异步批量的
handleClick = () => {
this.setState({ count: this.state.count + 1 });
console.log(this.state.count); // 可能打印旧值,因为setState异步
};
// setTimeout中setState同步更新(React 17及之前版本)
setTimeout(() => {
this.setState({ count: this.state.count + 1 });
console.log(this.state.count); // 新值,setState同步执行
}, 0);
五、总结
- React 异步批量更新是性能优化手段,避免重复渲染。
- 合成事件和生命周期内,React 自动开启批处理,
setState异步执行。 - 异步环境下,默认
setState同步执行,React 18 后自动批处理覆盖更多场景。 - 理解异步机制有助于正确编写和调试 React 状态更新逻辑。
16. React 如何批处理更新
题目要点
- 解释批处理更新的概念和必要性
- 说明不同环境下批处理触发条件
- 介绍 React 18 自动批处理的改进
- 结合示例说明批处理效果和实现方式
参考答案
考察点
● 理解 React 状态更新的批处理机制
考察对 React 性能优化和更新调度的掌握。
● 掌握 React 不同版本中批处理行为的变化及实现方式
考察对 React 事件系统和异步更新流程的理解。
参考答案
一、React 批处理更新的概念
- 批处理(Batching)是指 React 会将多次状态更新合并为一次更新,避免多次重复渲染。
- 这样可以提高性能,减少 DOM 操作次数,提升用户体验。
二、React 批处理更新的触发时机
1. 合成事件和生命周期函数内
- React 在合成事件处理函数和生命周期函数内,自动开启批处理。
- 多个
setState调用会合并成一次更新,最终只触发一次渲染。
2. 原生事件和异步回调中(React 17及以前)
- 在原生 DOM 事件或异步回调(如
setTimeout、Promise.then)中,React 默认关闭批处理。 - 每次
setState都会单独触发一次更新。
3. React 18 及以后版本
- React 18 引入了自动批处理(Automatic Batching),扩展批处理范围,覆盖更多异步场景(包括原生事件、
setTimeout、Promise等)。 - 通过新的调度机制,保证更多情况下状态更新合并,性能进一步优化。
三、React 批处理更新的实现原理
- React 维护一个更新队列,当多次调用
setState时,先将更新存储在队列中。 - 当前事件循环结束后,React 统一将所有更新合并,执行一次状态合并和一次渲染。
- React 内部通过合成事件系统拦截事件,开启批处理开关。
- React 18 基于调度库(scheduler)实现更细粒度的批处理和优先级控制。
四、示例说明
// 在合成事件中批处理更新,输出结果只触发一次渲染
handleClick = () => {
this.setState({ count: this.state.count + 1 });
this.setState({ count: this.state.count + 1 });
// 虽然调用了两次setState,但React只会批量合并一次更新
};
五、总结
- React 批处理更新是性能优化的关键手段。
- React 17 及以前版本仅在合成事件和生命周期内批处理。
- React 18 扩展了自动批处理范围,实现更全面的状态合并。
- 理解批处理机制,有助于编写高性能的 React 应用。
17. 在 setTimeout 或原生事件中为何同步更新?底层如何实现差异
题目要点
- 说明合成事件触发批处理机制
- 分析 setTimeout 和原生事件不同上下文导致不同执行行为
- 阐述 React 18 自动批处理的改进
- 结合示例说明同步更新现象
参考答案
考察点
● 理解 React 中批处理更新的触发机制及例外场景
考察对 React 内部事件系统和调度策略的认知。
● 掌握 setTimeout 和原生事件中同步更新的底层实现原理
考察对浏览器事件循环与 React 事件委托机制的结合理解。
参考答案
一、现象描述
- 在 React 合成事件和生命周期函数中,
setState是异步批处理执行的。 - 但在
setTimeout、原生 DOM 事件(非 React 合成事件)回调中,调用setState会立即同步执行更新。
二、原因分析
1. React 批处理的触发条件
- React 依赖其合成事件系统(事件委托机制)来自动开启批处理。
- 在合成事件的上下文中,React 会收集所有状态更新,统一批量执行。
- 生命周期函数也处于 React 调度上下文,支持批处理。
2. setTimeout 和原生事件非 React 事件系统外部
setTimeout和原生 DOM 事件回调不在 React 合成事件上下文内。- React 并不知道这些回调何时开始或结束,无法自动开启批处理。
- 因此,调用
setState后立即执行更新(同步更新)。
三、底层实现差异
| 方面 | 合成事件 / 生命周期 | setTimeout / 原生事件 |
|---|---|---|
| 批处理开关 | React 内部开启批处理开关 | 未开启批处理,setState 直接触发更新 |
| 事件调度机制 | React 事件委托,事件捕获阶段集中管理 | 浏览器原生事件回调,React 无法拦截 |
| 状态更新行为 | 多次 setState 合并异步执行 | 每次 setState 立即同步执行 |
四、React 18 及以后版本的变化
- React 18 引入了自动批处理(Automatic Batching),扩展了批处理范围,涵盖了
setTimeout、Promise、原生事件等。 - 通过调度库(scheduler)统一管理更新调度,解决了以前版本中同步执行问题。
五、示例说明
// React 17及以前版本示例
handleClick = () => {
setTimeout(() => {
this.setState({ count: this.state.count + 1 });
console.log('setTimeout内同步更新,state立即生效');
}, 0);
};
六、总结
- React 批处理依赖合成事件系统的上下文自动开启。
setTimeout和原生事件处于 React 事件系统外,默认同步更新。- React 18 通过自动批处理机制解决了该问题,提升一致性和性能。
- 理解这一机制有助于合理编写异步更新逻辑和调试。
18. useState 与 setState 的批处理机制是否一致
题目要点
- 说明
setState与useState都支持批处理更新 - 阐述 React 18 之前两者批处理差异
- 介绍 React 18 自动批处理如何统一两者行为
- 结合版本差异与实际应用场景做总结
参考答案
考察点
● 理解 React 函数组件(Hooks)与类组件中状态更新的批处理机制差异
考察对 React 两种组件状态管理及调度机制的深入理解。
● 掌握 React 版本演进中批处理行为的统一与差异
考察对 React 事件系统和自动批处理细节的掌握。
参考答案
一、概念说明
setState是 React 类组件中更新状态的方法。useState是 React 函数组件中管理状态的 Hook,返回的更新函数本质上也是异步批处理的。
二、批处理机制对比
| 特性 | 类组件 setState | 函数组件 useState 的更新函数 |
|---|---|---|
| 批处理机制 | React 16+ 开启批处理,合成事件和生命周期内异步合并多次状态更新。React 18 自动批处理扩展更多场景。 | 同样支持批处理,React 18 自动批处理覆盖绝大多数异步场景,包含 Hooks 状态更新。 |
| 执行时机 | 多个 setState 调用合并后,统一执行一次渲染。 | 多个 setState(useState 更新)调用合并执行,渲染触发时机一致。 |
| API差异 | setState 支持合并状态(对象式),且支持函数式更新。 | useState 直接替换状态,且必须通过更新函数调用,支持函数式更新。 |
| 异步行为 | 在 React 事件和生命周期中异步,非事件中同步(React 18自动批处理后大多异步)。 | 同步与异步行为与类组件保持一致,React 18 后自动批处理统一了行为。 |
三、React 18 以前的差异
- 早期版本中,函数组件的状态更新批处理不如类组件完善,特别是在非合成事件中的表现不一致。
- 类组件中
setState受生命周期和合成事件影响明显,函数组件中useState更新更多场景同步执行。
四、React 18 后的统一
- React 18 引入自动批处理(Automatic Batching),统一了函数组件和类组件状态更新的批处理行为。
- 不论是
setState还是useState的更新,都会在同一个事件循环内合并执行,避免不必要的重复渲染。
五、总结
setState和useState本质上都是异步批处理的,只是 API 和细节略有差异。- React 18 之前两者批处理机制存在部分不一致,React 18 后大多数场景实现统一批处理。
- 理解两者批处理差异,有助于更合理地设计状态更新逻辑和性能优化。
19. React 的链表遍历与 Vue 的双指针优化有何异同
题目要点
- 介绍 React Fiber 链表结构及遍历特征
- 解释 Vue 双指针遍历及 LIS 算法优化
- 对比两者数据结构、遍历策略和优化目标
- 总结设计思路与应用场景差异
参考答案
考察点
● 理解 React 和 Vue 中虚拟 DOM diff 算法的实现细节
考察对两大主流框架中核心渲染优化机制的掌握。
● 掌握链表遍历与双指针技术在 diff 算法中的应用及性能影响
考察对算法优化思路和前端框架渲染原理的理解。
参考答案
一、核心概念解析
- React 的链表遍历
React 的 Fiber 架构中,子节点以链表形式组织,遍历时通过链表结构顺序访问所有子节点。 - Vue 的双指针优化
Vue(特别是 Vue 3 的虚拟 DOM diff)使用双指针技术从头尾两端同时遍历新旧子节点列表,快速定位最长递增子序列(LIS),减少不必要的 DOM 操作。
二、实现机制对比
| 特性 | React 的链表遍历 | Vue 的双指针优化 |
|---|---|---|
| 数据结构 | Fiber 节点以链表形式连接,便于增删改查 | 新旧子节点以数组形式存储,利用双指针遍历 |
| 遍历方式 | 线性单向遍历(从头到尾) | 双指针同时从头尾向中间遍历,提升匹配效率 |
| 优化目标 | 实现可中断且可调度的遍历,支持并发模式 | 快速定位变更范围,减少无谓比对和移动操作 |
| 算法复杂度 | O(n),适合渐进式渲染 | 通过 LIS 等算法,最坏 O(n^2) ,优化后接近 O(n) |
| 渲染性能 | 利用 Fiber 架构支持时间切片与优先级控制 | 利用双指针减少比较次数,提升批量 DOM 操作效率 |
三、异同点总结
1. 共同点
- 都基于虚拟 DOM 树的子节点比对,实现最小化 DOM 更新。
- 都追求减少不必要的 DOM 操作和提高渲染性能。
- 都需要遍历节点列表,找出差异和变更。
2. 不同点
| 方面 | React 链表遍历 | Vue 双指针优化 |
|---|---|---|
| 数据结构 | 以链表组织,方便节点增删,支持可中断遍历 | 以数组为基础,适合随机访问和双向遍历 |
| 遍历策略 | 单向线性遍历 | 双向同时遍历,提高匹配速度 |
| 重点优化方向 | 渲染任务可拆分中断,支持并发渲染 | 减少无效对比,快速识别最长递增子序列 |
| 应用场景 | React Fiber 渲染调度 | Vue diff 算法中节点快速比对和重排 |
四、实际应用场景
- React 通过链表结构结合 Fiber 时间切片实现高效、可中断的渲染调度,适合复杂 UI 和多任务环境。
- Vue 通过双指针和 LIS 算法优化节点移动和复用,适合大批量节点列表快速更新,提升首屏和交互响应速度。
五、总结
- React 链表遍历注重任务拆分和调度灵活性,底层架构差异显著。
- Vue 双指针优化聚焦于算法层面的性能提升,减少不必要的 DOM 操作。
- 两者各有优势,分别服务于各自框架的设计哲学和性能需求。
- 理解两者异同有助于深入掌握虚拟 DOM 优化原理及前端框架性能调优。
20. Diff 算法的复杂度如何从 O(n³) 降到 O(n)
题目要点
- 说明传统 Diff 复杂度高的原因(全树暴力对比)
- 阐述同层比较、Key 映射、双指针遍历等优化手段
- 介绍最长递增子序列算法在移动节点优化中的作用
- 总结优化后时间复杂度可降至 O(n) 级别
参考答案
考察点
● 理解传统虚拟 DOM Diff 算法的复杂度瓶颈
考察对虚拟 DOM 算法的基本实现和性能问题的认识。
● 掌握现代 Diff 算法优化策略及关键技术
考察对算法优化思路(时间复杂度降低)的理解与应用。
参考答案
一、Diff 算法复杂度背景
- 传统的虚拟 DOM Diff 算法,特别是早期基于深度优先遍历的实现,往往采用暴力比较,即新旧节点树的每个子节点都进行逐一对比。
- 该算法在处理嵌套结构和列表节点时,涉及多层循环嵌套,导致时间复杂度达到 O(n³) 级别,严重影响性能。
二、优化思路及关键技术
1. 降低比较范围,利用“同层比较”原则
- 传统算法对任意节点进行全树比对,优化后限制只比较同层节点,避免跨层无效比对。
- 这样把树的比较转化为平行节点列表的比较,减少重复计算。
2. 利用唯一标识 Key 来快速定位节点
- 使用
key标识节点身份,避免无序节点的全量比较。 - 通过
key建立映射表,能快速查找对应新旧节点,大幅减少查找次数。
3. 采用双指针和多指针遍历
- 利用双指针(头尾指针)分别从新旧节点数组的两端遍历,快速确定移动、删除、插入操作。
- 避免嵌套循环,整体复杂度从三重循环降为线性。
4. 利用最长递增子序列(LIS)算法优化移动操作
- 在确定节点位置变化时,找到最长递增子序列,保持不动的节点无需重新渲染。
- 该算法复杂度约为 O(n log n),结合双指针遍历,整体接近线性。
三、具体流程示例
- 头部和尾部指针同时遍历,分别处理新增、删除或复用的节点。
- 建立旧节点 key 映射表,快速匹配新节点。
- 根据匹配结果执行新增、删除或移动操作。
- 通过 LIS 找出稳定节点,减少 DOM 操作。
四、总结
- 通过限制比较范围(同层节点),利用 key 唯一标识,避免全树暴力对比。
- 结合双指针遍历和 LIS 算法,实现线性或近线性时间复杂度。
- 这些优化使现代前端框架(如 React Fiber、Vue 3)能高效地处理复杂界面更新。
21. key 的作用是什么?索引作为 key 的风险
题目要点
- 说明
key的唯一标识和性能优化作用 - 分析索引作为
key的副作用和潜在风险 - 指出索引作为
key的有限适用场景 - 强调使用稳定唯一标识的最佳实践
参考答案
考察点
● 理解虚拟 DOM 中 key 的作用与重要性
考察对 React、Vue 等框架中列表渲染优化机制的理解。
● 掌握使用索引作为 key 可能带来的副作用和风险
考察对列表渲染中状态保持与性能影响的认识。
参考答案
一、key 的作用
1. 唯一标识列表中的每个节点
key用来唯一标识当前列表中的每个元素,帮助框架区分节点身份。- 在 Diff 过程中,利用
key来匹配新旧虚拟 DOM,判断节点是复用、移动还是新增/删除。
2. 优化渲染性能,减少不必要的 DOM 操作
- 通过准确的
key,框架能快速定位节点,避免全量重渲染。 - 减少节点的销毁和重建,保持组件状态(如输入框内容、动画状态)一致。
3. 保持组件内部状态的稳定性
key相同,组件实例复用;key不同,组件卸载重新挂载。- 保证用户交互状态和生命周期的正确关联。
二、索引作为 key 的风险
1. 引发不必要的组件重渲染和状态丢失
- 当列表数据发生增删改动时,索引对应的节点会变,导致错误复用组件。
- 例如删除某项后,后续项索引前移,但使用索引做
key会误认为是同一个组件,导致状态错乱。
2. 影响动画和过渡效果
- 错误的
key使框架误判元素移动为删除和新增,触发不必要的动画。 - 影响用户体验,造成界面抖动或闪烁。
3. 降低渲染效率
- 错误的
key会导致更多的 DOM 操作,降低性能。
三、使用索引作为 key 的适用场景
- 列表项内容和顺序固定且不会变化时,可以使用索引作为
key。 - 临时快速实现,但不推荐用于动态可变列表。
四、最佳实践
- 优先使用列表项中唯一且稳定的标识符作为
key(如数据库 ID)。 - 避免使用索引作为
key,尤其是列表可变时。 - 在无稳定标识时,考虑生成稳定唯一 ID。
五、总结
key是虚拟 DOM 差异算法中定位和复用节点的关键。- 错误使用索引作为
key会导致状态错乱、性能下降和体验问题。 - 选择合适的
key是列表渲染性能和正确性的保障。
22. 实现一个搜索框
题目要点
- 说明搜索框核心功能和性能优化点
- 给出简洁且高效的防抖实现方案
- 结合示例代码说明输入、搜索触发机制
- 强调交互设计和可用性考虑
参考答案
考察点
● 了解搜索框的核心功能实现
考察对前端输入交互、状态管理和事件处理的掌握。
● 掌握输入防抖(debounce)等性能优化策略
考察对用户体验和性能优化的重视。
● 熟悉常见的搜索联想和结果展示交互设计
考察界面交互设计与实现细节。
参考答案
一、核心功能及原理
- 搜索框主要包括文本输入、输入状态管理、触发搜索请求和显示搜索结果。
- 通常结合防抖函数避免频繁请求,提升性能和体验。
- 支持回车触发搜索、结果选择等交互。
二、核心用法 + 示例代码(React 示例)
import React, { useState, useEffect, useRef } from 'react';
// 防抖函数
function debounce(fn, delay) {
let timer = null;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
function SearchBox({ onSearch }) {
const [query, setQuery] = useState('');
const debouncedSearch = useRef(
debounce((value) => {
onSearch(value);
}, 300)
).current;
const handleChange = (e) => {
const value = e.target.value;
setQuery(value);
debouncedSearch(value);
};
const handleKeyDown = (e) => {
if (e.key === 'Enter') {
onSearch(query); // 立即搜索
}
};
return (
<input
type="text"
value={query}
placeholder="请输入搜索内容"
onChange={handleChange}
onKeyDown={handleKeyDown}
style={{ width: '300px', padding: '8px', fontSize: '16px' }}
/>
);
}
export default SearchBox;
三、使用场景和注意点
场景
- 电商搜索、文章检索、用户筛选等实时搜索需求。
- 联想提示和自动完成功能。
注意点
- 防抖避免频繁请求,提高性能。
- 输入合法性校验(如过滤特殊字符)。
- 支持键盘操作,提升无障碍体验。
- 结果展示区与搜索框交互良好,防止遮挡。
- 移动端适配,兼容性考虑。
四、总结
- 搜索框设计需要兼顾交互流畅性和性能。
- 防抖节流是关键优化手段。
- 结合业务场景灵活扩展功能(联想、历史、分类等)。
- 组件设计要考虑易用性和可维护性。
23. 算法题:编写一个函数 rgb2hex,将符合 rgb(r, g, b) 或 RGBA 格式的字符串转换为十六进制形式(如 #ffffff)。若输入格式不正确,则返回原字符串
示例:
● 输入:‘rgb(255, 255, 255)’ → 输出:’#ffffff’
● 输入:‘rgba(255, 0, 128, 0.5)’ → 需忽略透明度,输出:’#ff0080’
● 输入:‘hsl(120, 50%, 50%)’ → 输出原字符串(格式不匹配)
题目要点
- 说明正则匹配与数字提取原理
- 阐述 RGB → HEX 转换步骤
- 介绍输入合法性校验与容错处理
- 提供可读且健壮的示例代码
参考答案
考察点
● 字符串解析与正则表达式应用
考察对字符串格式校验与提取的掌握。
● RGB 与 HEX 颜色格式转换原理
考察颜色数值转换及进制转换能力。
● 容错处理与健壮性设计
考察对输入格式异常的合理处理。
参考答案
一、原理说明
rgb(r, g, b)和rgba(r, g, b, a)格式均由整数红绿蓝三色值组成,范围通常是 0~255。- 十六进制颜色格式为
#RRGGBB,每个颜色通道用两位 16 进制表示。 - 对于
rgba,忽略透明度a,仅转换r, g, b。 - 需严格匹配输入格式,若不匹配,返回原字符串。
二、核心用法 + 示例代码
function rgb2hex(colorStr) {
// 匹配 rgb 或 rgba 格式,捕获 r, g, b 三个数字
const regex = /^rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})(?:\s*,\s*[\d.]+\s*)?\)$/i;
const match = colorStr.match(regex);
if (!match) return colorStr; // 格式不匹配,返回原字符串
// 解析数字
const r = Number(match[1]);
const g = Number(match[2]);
const b = Number(match[3]);
// 验证范围是否合法
if ([r, g, b].some(n => n < 0 || n > 255)) return colorStr;
// 转换为两位十六进制字符串
const toHex = (n) => n.toString(16).padStart(2, '0');
// 拼接成十六进制颜色
return `#${toHex(r)}${toHex(g)}${toHex(b)}`.toLowerCase();
}
三、使用场景和注意点
- 常用于 CSS 样式动态转换、颜色处理工具、调试工具等。
- 需确保输入格式严格匹配,避免误判。
- 忽略透明度参数,简化输出。
- 支持大小写不敏感匹配。
四、总结
- 通过正则提取 rgb(a) 格式数字,校验数值范围。
- 转换为标准六位十六进制字符串。
- 输入格式不合规时返回原字符串,保证健壮性。
- 代码简洁高效,易于扩展支持更多颜色格式。