牛客面经整理:字节前端风控一面(八股 / 工程化 / 安全 / JS)(二十)
收录日期:2026-04-28
关键词:前端、CSS、Tailwind、Vue2/Vue3、浏览器缓存、CORS、CSRF、Cookie、JWT、Event Loop、async/defer、Vite、Tree-shaking、手写题
1. CSS 盒模型有哪些参数?box-sizing 有什么用?
参考答案
- 盒模型从内到外:
content(内容区)→padding(内边距)→border(边框)→margin(外边距)。 width/height默认只作用在内容区(box-sizing: content-box)。box-sizing: border-box:width/height包含padding + border + content,更利于做布局(避免计算)。
2. 多列布局用什么更好?(Grid / 多列 / 表格)
参考答案
- 常见选择:
- 等高、二维对齐明显:优先
CSS Grid。 - 一维流式布局:
Flex更常用。 - 报刊式“分栏流”(内容自动从一列流到下一列):
column-count/column-width。
- 等高、二维对齐明显:优先
- 不建议用
table/隐藏表格做布局:语义差、可访问性与响应式维护成本高。
3. Tailwind 与传统 CSS/预处理器的主要区别?
参考答案
- Tailwind:以“原子类/工具类”为主,通过组合
class直接表达样式,减少手写 CSS 与命名成本。 - 传统 CSS:更多依赖语义类名 + 样式抽象;复用靠 BEM/组件化/变量 mixin 等。
- 工程化差异:Tailwind 依赖构建期扫描(purge/content)做按需生成与裁剪,最终产物更可控。
4. Tailwind 的缺点/风险点?
参考答案
- 可读性两极分化:类名堆叠,长
class需要格式化/抽离(@apply、组件封装、class 合并工具)。 - 约束与设计系统:需要
theme/tokens 统一,不然容易“随手写值”导致风格漂移。 - 构建期扫描坑:动态拼接类名可能被裁剪;需要 safelist 或显式枚举。
5. Vue2 和 Vue3 响应式最大的差异?
参考答案
- Vue2:基于
Object.defineProperty劫持 getter/setter;数组与新增属性有局限(需Vue.set等)。 - Vue3:基于
Proxy代理对象,支持更完整的拦截(新增/删除属性、数组索引等),实现更一致,性能也更好。 - 组合式 API/编译优化也常被提及,但“响应式底层”主要是
definePropertyvsProxy。
6. 浏览器缓存工作方式(强缓存/协商缓存)
参考答案
- 强缓存:命中时不发请求(或不走网络),常用
Cache-Control: max-age/immutable、Expires(旧)。 - 协商缓存:发条件请求,服务端根据
ETag/Last-Modified判断,没变化返回304 Not Modified。 - 常见策略:带 hash 的静态资源走强缓存;HTML 入口与 API 更偏协商/短缓存,便于更新。
7. 常见缓存相关头有哪些?(举例说明作用)
参考答案
Cache-Control:max-age、no-cache、no-store、public/private、s-maxage、immutable等。ETag/If-None-Match:实体标签与条件请求。Last-Modified/If-Modified-Since:基于修改时间的协商缓存(精度较低,可能误判)。Vary:告诉缓存“不同请求头组合会得到不同响应”(如按Accept-Encoding、Origin变化)。
8. 跨域 / 同源策略 / CORS 是什么?
参考答案
- 同源:协议/域名/端口一致才算同源;同源策略限制 JS 读取跨域响应(并非禁止发请求)。
- CORS:服务端用响应头授权跨域访问(如
Access-Control-Allow-Origin)。 - 预检(OPTIONS):非简单方法、非简单头或
Content-Type: application/json等会触发,需服务端正确响应允许的方法/头/凭证。 - Cookie:跨域携带凭证需要前端
credentials+ 服务端Access-Control-Allow-Credentials: true,且Allow-Origin不能是*。
9. CSRF 是什么?如何防?
参考答案
- CSRF:利用浏览器自动携带 Cookie 的特性,诱导用户在已登录态下对站点发起“非预期请求”。
- 防护:
- 关键接口校验 CSRF Token(同步到表单/请求头)。
SameSiteCookie(Lax/Strict/None; Secure)。- 校验
Origin/Referer(作为辅助手段)。 - 对敏感操作做二次确认/验证码/风控。
10. Cookie vs JWT:区别、优缺点与安全点?
参考答案
- Cookie(会话 ID):
- 状态可放服务端(更易做吊销/黑名单/会话管理)。
- 风险:CSRF;可通过
HttpOnly防 XSS 直接读取。
- JWT:
- 通常是“自包含”令牌(header.payload.signature),服务端不必存会话但常配合黑名单/短期 token + refresh。
- payload 是 Base64URL 编码,不是加密,不要放敏感信息;安全性靠签名防篡改。
- 风险:XSS 盗取(若放 localStorage);过期与吊销设计要清晰(短过期 + refresh + rotate)。
11. JS Event Loop:宏任务/微任务、requestAnimationFrame?
参考答案
- 事件循环流程(简化):
- 执行一个宏任务(script、setTimeout、I/O、UI 事件…)
- 清空微任务队列(Promise then/catch/finally、queueMicrotask、MutationObserver)
- 进入渲染(可能发生)/
requestAnimationFrame回调在渲染前触发 - 继续下一个宏任务
- 典型输出题:同步先执行;微任务早于下一轮宏任务。
12. 输出顺序题:1 4 3 2 为什么?
参考答案
console.log(1)
setTimeout(() => console.log(2), 0)
Promise.resolve().then(() => console.log(3))
console.log(4)
- 同步:
1、4 - 微任务:
3 - 下一轮宏任务:
2
13. async / defer 的区别?
参考答案
defer:- 脚本下载不阻塞解析 HTML;
- 执行会推迟到 HTML 解析完成后;
- 多个
defer脚本按顺序执行。
async:- 脚本下载不阻塞解析 HTML;
- 下载完成就尽快执行(可能打断解析);
- 多个
async执行顺序不保证。
14. Vite 为什么开发环境快?
参考答案
- 开发时用原生 ESM:按需请求模块,不做整包打包。
- 预构建:用 esbuild 对依赖做预打包/缓存,加速冷启动与请求数。
- HMR 粒度更细:改动只影响局部模块链路。
15. Tree-shaking 机制与“摇不掉”的原因?
参考答案
- 依赖前提:ESM 静态分析 + DCE(死代码消除)。
- 摇不掉常见原因:
- 使用 CommonJS(动态
require)导致静态分析困难。 - 模块有副作用(顶层改全局、注入样式等),打包器不敢删。
- 未正确标注
sideEffects或聚合导出/不精确导入导致整包引入。
- 使用 CommonJS(动态
16. 手写题:展平多层嵌套数组
参考答案
- 递归:
- 遍历元素,遇到数组递归展开,否则 push。
- 迭代(栈):
- 用栈模拟 DFS/BFS,注意保持顺序可用
unshift或倒序入栈。
- 用栈模拟 DFS/BFS,注意保持顺序可用
- JS 内置:
arr.flat(Infinity)(面试可提,但通常希望手写)。