前端面试题手册

梳理高频技术问题,帮助你按主题复习和查漏补缺。

前端阅读 05月27日 22:34

Chrome 浏览器有哪些安全机制?

答案要点Chrome 的安全机制可以分三层理解:进程级隔离、网络级防护、页面级约束。面试时按这个层次回答,逻辑清晰且不容易遗漏。进程级隔离沙箱(Sandbox) 是 Chrome 安全的基石。每个标签页对应独立的渲染进程,渲染进程没有直接访问系统资源的权限——文件系统、网络、GPU 都要经过浏览器主进程中转。就算渲染进程被攻破,攻击者也只能在一个受限环境里打转。Chrome 2026 年已遭遇 4 个被利用的零日漏洞,分别涉及 CSS 处理、Skia 图形库、V8 引擎和 WebGPU/Dawn 组件。沙箱机制正是限制这类漏洞影响范围的关键防线。网络级防护HTTPS 强制:Chrome 逐步标记所有 HTTP 页面为不安全,默认将 http:// 升级为 https://。证书验证链确保你连的是真实服务器,而非中间人。混合内容拦截:HTTPS 页面中加载的 HTTP 资源会被自动阻止,防止加密通道被侧面突破。安全浏览(Safe Browsing):Google 维护恶意网址和钓鱼网站黑名单,Chrome 在导航前实时比对,发现风险会弹出醒目警告。页面级约束同源策略(SOP) 是 Web 安全的核心规则:协议 + 域名 + 端口三者一致才算同源。跨源读操作被默认禁止,JS 无法读取不同源的 DOM、Cookie 或响应数据。CORS(跨域资源共享) 是 SOP 的补充:服务器通过 Access-Control-Allow-Origin 等 HTTP 头声明允许哪些源访问,浏览器据此放行。注意:CORS 是浏览器端限制,服务端之间互相请求不存在跨域问题。CSP(内容安全策略) 通过 Content-Security-Policy 头或 meta 标签限制页面能加载哪些资源,从根本上切断 XSS 的注入通道:Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-abc123'这段配置只允许加载同源脚本,且必须携带指定 nonce,内联脚本和 eval 都会被拦截。SameSite Cookie:设置为 Strict 或 Lax 后,跨站请求不再自动携带 Cookie,CSRF 攻击失去赖以生存的身份凭证。面试追问方向沙箱能不能完全防止漏洞利用?不能,零日漏洞说明沙箱是纵深防御的一层而非万能方案CORS 预检请求什么场景触发?非简单请求(自定义头、非 GET/POST 方法等)会先发 OPTIONSCSP 的 nonce 和 hash 模式有什么区别?nonce 是一次性随机值,hash 是脚本内容的摘要指纹浏览器安全 vs 浏览器隐私有什么区别?安全防护恶意攻击,隐私防止用户追踪,Chrome 安全强但隐私默认偏弱
前端阅读 05月27日 22:32

Chrome 浏览器如何进行性能优化?

Chrome 性能优化从哪几个维度入手?三大维度:加载性能(资源到浏览器的时间)、渲染性能(浏览器绘制页面的效率)、运行时性能(页面交互的流畅度)。面试中按这个框架回答,比零散列举更有说服力。加载性能怎么优化?核心目标是缩短首屏时间。关键手段:资源压缩:Brotli 替代 Gzip,CSS/JS 做 Tree Shaking 删除死代码,图片用 WebP/AVIF 格式代码分割:路由级懒加载,首屏只加载关键 CSS 和 JS,非关键资源 defer 或 async缓存策略:静态资源配强缓存(Cache-Control: max-age),API 走协商缓存(ETag/Last-Modified)预加载:<link rel="preload"> 加载关键资源,<link rel="prefetch"> 预取下一页资源<link rel="preload" href="/critical.css" as="style"><link rel="preload" href="/hero.webp" as="image">追问:首屏 LCP 6 秒怎么排查?先用 DevTools Performance 面板定位 LCP 元素,再看网络瀑布图找阻塞链,逐项优化。渲染性能怎么优化?关键是减少重排(Layout)和重绘(Paint),因为它们会阻塞主线程。批量 DOM 操作:用 DocumentFragment 或 requestAnimationFrame 合并更新CSS 动画优先:用 transform 和 opacity 做动画,这两个属性只触发合成层,不触发布局和绘制避免强制同步布局:读写样式分开,不要在循环中交替读取 offsetWidth 和修改样式will-change 谨慎使用:提前告知浏览器该元素将变化,但滥用会导致图层爆炸// 差:读写交替,触发强制同步布局elements.forEach(el => { const width = el.offsetWidth; // 读 el.style.width = width * 2 + "px"; // 写});// 好:先读完再写const widths = elements.map(el => el.offsetWidth);elements.forEach((el, i) => { el.style.width = widths[i] * 2 + "px";});追问:如何判断页面是否存在布局抖动?DevTools Performance 面板中紫色 Layout 块频繁出现且与 Recalculate Style 交替就是布局抖动。运行时性能怎么优化?长任务拆分:超过 50ms 的任务会阻塞交互,用 requestIdleCallback 或 scheduler.yield() 拆分事件委托:列表场景在父元素绑定一个监听器,而非每个子元素都绑定防抖节流:scroll/resize/input 事件必须节流,搜索建议用防抖内存泄漏排查:DevTools Memory 面板拍快照对比,关注 Detached DOM 节点和未清理的定时器追问:内存泄漏常见原因有哪些?未清除的 setInterval、闭包持有大对象引用、DOM 移除后事件监听器未移除、全局变量意外挂载。用什么工具度量性能?Lighthouse:一键生成性能评分和优化建议Performance 面板:录制用户操作,分析主线程占用、布局、绘制耗时Core Web Vitals:LCP(加载)、INP(交互)、CLS(稳定),这三个指标直接影响搜索排名性能优化不是罗列清单,而是先度量再优化。找到瓶颈,针对性处理,才是面试官想听到的思路。
前端阅读 05月27日 22:32

Chrome 浏览器的多进程架构是怎样的?

Chrome 多进程架构将浏览器功能拆分到独立进程中运行,核心目标是隔离故障、提升安全和利用多核性能。最新版 Chrome 打开一个页面至少启动 4 个进程:浏览器进程、渲染进程、GPU 进程和网络进程。五种核心进程浏览器进程是整个浏览器的"总管",负责地址栏、书签、前进后退等 UI 交互,同时协调其他子进程的创建与销毁。渲染进程是数量最多的进程,每个标签页(严格说是每个站点)分配一个。它运行 Blink 排版引擎和 V8 引擎,将 HTML/CSS/JS 转化为可交互页面。渲染进程被沙箱隔离,即使被恶意代码攻破也无法直接访问系统资源。GPU 进程专门处理 GPU 任务,最初只为 3D CSS,现在所有页面合成和 UI 绘制都依赖它。网络进程负责所有网络请求,从浏览器进程中独立出来后,网络 I/O 不再阻塞 UI 线程。插件进程运行浏览器扩展,每个插件独立一个进程,崩溃不影响其他页面。架构演进:从多进程到面向服务2016 年起 Chrome 推进 SOA(面向服务架构)重构,将浏览器进程内的模块(存储、网络、UI 等)拆成独立服务,按需运行在独立进程或合并到现有进程中。资源充裕时拆分更细,资源紧张时合并更省内存——这种弹性是传统多进程架构做不到的。Site Isolation 站点隔离2018 年 Spectre 漏洞暴露了同进程内不同 iframe 可互相读取内存的风险。Chrome 随即上线 Site Isolation:不同站点(same-site)的页面强制分到不同渲染进程,即使同一标签页内的跨站 iframe 也不例外。这是对"每个标签页一个进程"策略的重要补丁。进程间通信各进程通过 Mojo(Chrome 现代 IPC 框架)进行异步消息传递,大数据场景使用共享内存。渲染进程通过 RenderProcess 对象与浏览器进程保持连接。追问方向Chrome 多进程的内存代价如何?——每个进程独立一份 V8 堆和 Blink 对象,内存开销显著,Chrome 用进程复用(Process Sharing)缓解。渲染进程为什么必须沙箱化?——渲染进程直接执行第三方代码,沙箱限制其系统调用,即使被攻破也只能在受限环境中操作。如何用 DevTools 观察进程?——打开 chrome://process-internals 或任务管理器(Shift+Esc)查看各进程的内存和 CPU 占用。
前端阅读 05月27日 22:07

Prettier 支持哪些配置文件格式?

答案Prettier 支持以下配置文件格式,按查找优先级从高到低排列:| 优先级 | 文件名 | 格式 ||--------|--------|------|| 1 | package.json 中 prettier 字段 | JSON || 2 | .prettierrc | JSON/YAML || 3 | .prettierrc.json | JSON || 4 | .prettierrc.json5 | JSON5 || 5 | .prettierrc.yml / .prettierrc.yaml | YAML || 6 | .prettierrc.toml | TOML || 7 | .prettierrc.js / .prettierrc.mjs | JS(ESM) || 8 | .prettierrc.cjs | JS(CJS) || 9 | prettier.config.js / prettier.config.mjs | JS(ESM) || 10 | prettier.config.cjs | JS(CJS) |Prettier 3.5+ 还支持 .prettierrc.ts / prettier.config.ts(TypeScript 格式),适合 TS 项目直接复用类型。选哪个? 小项目用 .prettierrc.json,简洁无歧义,编辑器能做 JSON Schema 校验;需要注释或动态逻辑时用 .prettierrc.js;TS 项目可考虑 .prettierrc.ts。// .prettierrc.json — 最常用{ "printWidth": 80, "tabWidth": 2, "semi": true, "singleQuote": true, "trailingComma": "es5", "endOfLine": "lf"}// .prettierrc.js — 需要注释时module.exports = { printWidth: 80, // 每行最大字符数 tabWidth: 2, // 缩进空格数 semi: true, // 行尾分号 singleQuote: true, // 单引号 trailingComma: "es5", endOfLine: "lf",};配置解析机制Prettier 从被格式化文件所在目录开始向上搜索配置文件,直到项目根目录,不支持全局配置,确保不同机器行为一致。如果同时存在 .editorconfig,Prettier 会将其作为基础配置,但 .prettierrc 中的选项优先。overrides 按文件类型定制{ "semi": true, "overrides": [ { "files": ["*.md", "*.json"], "options": { "tabWidth": 4 } } ]}.prettierignore类似 .gitignore,排除不需要格式化的文件:node_modulesdist*.min.js追问Q: .prettierrc 和 .prettierrc.json 有什么区别?没有本质区别。.prettierrc 优先级更高,Prettier 会先尝试按 JSON 解析,失败则尝试 YAML。.prettierrc.json 只按 JSON 解析,语义更明确,编辑器也能做 schema 校验。Q: Prettier 配置和 ESLint 冲突怎么办?安装 eslint-config-prettier 关闭 ESLint 中与 Prettier 冲突的规则即可。不要两边重复定义同一规则。Q: 为什么不推荐在 package.json 中配置?package.json 的 prettier 字段容易被忽略,且该文件本身体积大、职责多,配置混在一起不利于维护和代码审查。
前端阅读 05月27日 22:04

Prettier 插件有哪些?如何开发自定义插件?

直接回答Prettier 支持两类插件:语言解析器插件(为新语言提供格式化能力)和格式化增强插件(扩展现有语言的格式化行为)。开发自定义插件需实现三个核心部分:languages(语言定义)、parsers(解析器,文本→AST)、printers(打印器,AST→格式化文本)。Prettier 插件分类语言解析器插件 — 让 Prettier 支持新语言:@prettier/plugin-php、@prettier/plugin-ruby、@prettier/plugin-pug格式化增强插件 — 对已有语言做额外处理:prettier-plugin-organize-imports:自动排序 importprettier-plugin-tailwindcss:Tailwind 类名排序prettier-plugin-sort-json:JSON 键排序安装后在 .prettierrc 的 plugins 数组中声明即可。Prettier 也会自动加载 node_modules 下匹配 prettier-plugin-* 或 @scope/prettier-plugin-* 的包。如何开发自定义插件一个完整的插件至少导出 languages、parsers、printers:// my-prettier-plugin/index.jsmodule.exports = { languages: [ { name: "MyLang", parsers: ["mylang-parse"], extensions: [".mylang"], }, ], parsers: { "mylang-parse": { parse: (text) => { // 将源码文本解析为 AST // 可借助第三方解析器(如 babel, tree-sitter 等) return customParse(text); }, astFormat: "mylang-ast", locStart: (node) => node.start, locEnd: (node) => node.end, }, }, printers: { "mylang-ast": { print: (path, options, print) => { // 将 AST 节点转换为 Prettier Doc 对象 // Doc 是 Prettier 的中间表示,支持换行、缩进等 const node = path.getValue(); // 用 concat/line/hardline 等构建输出 }, }, },};开发要点:parse 函数的输入是源码字符串,返回 AST 对象locStart/locEnd 告诉 Prettier 每个 AST 节点的位置,用于错误定位print 函数返回 Doc 对象,用 concat、hardline、indent 等 API 拼接格式化输出如果只想对已有语言做预处理,只需在 parser 中实现 preprocess 函数,无需写完整 parser/printer调试方式: 运行 prettier --plugin ./my-prettier-plugin --debug-print-doc file.mylang 可查看生成的 Doc 结构。面试追问方向如果只想在格式化前对代码做预处理(如删除注释),怎么实现? — 在对应 parser 中实现 preprocess 函数即可,不需要自定义整个 parser/printer 链路。Prettier 的 Doc 是什么? — Prettier 内部的中间表示,类似虚拟 DOM。print 返回 Doc 而非字符串,Prettier 再根据行宽等配置将 Doc "渲染"为最终输出,这样能自动处理换行和缩进。插件和 Prettier 内置语言支持的区别? — 内置语言是 Prettier 核心代码的一部分,性能和兼容性更优;插件是独立模块,更新节奏自由但需自行维护与 Prettier 版本的兼容。
前端阅读 05月27日 22:04

使用 Prettier 时常见的问题有哪些?如何解决?

Prettier 配置不生效怎么办?修改 .prettierrc 后格式化无变化,是最常见的坑。排查顺序:先用 prettier --find-config-path <file> 确认实际加载的配置文件,可能被上层目录的配置覆盖;再检查 JSON 语法是否合法(多余逗号是最常见的错误);最后重启编辑器,VS Code 会缓存配置。多配置文件共存时,Prettier 按 .prettierrc > .prettierrc.json > .prettierrc.yml > prettier.config.js 的顺序查找,找到一个就停止。用 --config 显式指定可以彻底消除歧义。 追问: .prettierignore 和 .gitignore 的区别是什么? — .prettierignore 只控制 Prettier 忽略哪些文件,不影响 Git;.gitignore 不被 Prettier 自动读取,除非用 --ignore-path .gitignore 显式指定。如何忽略特定文件的格式化?三种方式,适用场景不同:项目级忽略: .prettierignore 文件,语法与 .gitignore 一致,适合忽略 dist/、vendor/ 等目录行内忽略: // prettier-ignore 注释,只忽略紧跟的下一行// prettier-ignoreconst matrix = [ [1, 2, 3], [4, 5, 6],];部分规则覆盖: overrides 字段,为特定文件类型设置不同规则{ "overrides": [ { "files": "*.md", "options": { "proseWrap": "always" } } ]} 追问: // prettier-ignore 能忽略整个文件吗? — 不能,它只作用于紧跟的下一个语法节点。要忽略整个文件,用 .prettierignore。Prettier 和 ESLint 冲突如何解决?这是面试高频题。冲突根源:ESLint 有格式规则(如缩进、引号),Prettier 也有自己的规则,两者同时作用就会打架。标准解法分两步:安装 eslint-config-prettier,它会关闭所有与 Prettier 冲突的 ESLint 规则(可选) 安装 eslint-plugin-prettier,将 Prettier 格式问题作为 ESLint 错误报告// .eslintrc.jsmodule.exports = { extends: [ 'eslint:recommended', 'plugin:prettier/recommended' // 必须放最后,否则会被前面的配置覆盖 ]};plugin:prettier/recommended 已经包含了 eslint-config-prettier,不需要重复引入。extends 的顺序很关键——后面的配置会覆盖前面的,所以 Prettier 相关配置必须放末尾。 追问: eslint-plugin-prettier 和 prettier-eslint 有什么区别? — eslint-plugin-prettier 让 ESLint 调用 Prettier;prettier-eslint 让 Prettier 的输出再过一遍 ESLint fix。前者是主流方案。大项目格式化太慢怎么办?Prettier 3.x 原生支持 --cache,只格式化变更的文件:prettier --write --cache .配合 lint-staged 只处理暂存文件更高效:{ "lint-staged": { "*.{js,ts}": ["prettier --write"] }}升级版本也有明显收益。Prettier 3 基于 ECMAScript 2024 重写了解析器,格式化速度比 2.x 快 2-3 倍。 追问: --cache 的缓存存在哪里? — 默认存在 node_modules/.cache/prettier,可以通过 --cache-location 自定义路径。VS Code 中 Prettier 保存不格式化?检查清单:安装了 Prettier 扩展settings.json 中设置 "editor.formatOnSave": true"editor.defaultFormatter" 设为 "esbenp.prettier-vscode"如果项目有多个 formatter,需要在 "[javascript]" 等语言设置中指定{ "editor.formatOnSave": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }}多编辑器协作时,加 .editorconfig 统一基础缩进和换行符,避免 Tab/Space 之争。团队版本不一致怎么处理?锁定版本是最直接的办法:// package.json{ "devDependencies": { "prettier": "3.3.3" // 不用 ^ 前缀 }}团队用 npm ci 安装依赖,它会严格按 package-lock.json 安装,忽略 node_modules 中已有的版本。CI 环境同理,npm ci 比 npm install 更可靠。升级 Prettier 大版本时,格式化结果可能变化。正确做法:单独开分支升级,确认无异常再合入主干,不要在功能分支中顺手升级。
前端阅读 05月27日 22:03

Prettier 的 .prettierignore 怎么配置?有哪些常用规则和踩坑点?

.prettierignore 文件怎么写?.prettierignore 放在项目根目录,语法和 .gitignore 完全一致。写好它才能放心跑 prettier --write .,否则会格式化不该动的文件。node_modulesdistbuildcoverage*.min.js*.min.csspackage-lock.jsonpnpm-lock.yamlPrettier 默认会忽略哪些文件?不需要手动写,Prettier 自动忽略:版本控制目录:.git、.svn、.hgnode_modules(除非显式加 --with-node-modules)另外,Prettier 自动读取同目录下的 .gitignore,所以 .gitignore 里已经排除的文件不需要在 .prettierignore 里重复写。四种忽略模式怎么用?1. 目录忽略node_modules**/dist2. 扩展名忽略*.min.js*.min.css*.d.ts3. 路径通配符src/**/*.generated.ts!src/vendor/*.js! 是否定模式,表示"前面忽略的里面,这条除外"。4. 行内忽略(prettier-ignore 注释)不想格式化某一段代码,在上方加注释:// prettier-ignoreconst matrix = [ [1, 000, 000], [0, 1, 000], [0, 000, 1]];HTML 里用 <!-- prettier-ignore -->,Markdown 里用 <!-- prettier-ignore -->,CSS 里用 /* prettier-ignore */。每种文件类型都有对应的注释语法。CLI 相关的忽略参数有哪些?--ignore-path .prettierignore.custom:指定自定义 ignore 文件路径--with-node-modules:取消默认对 node_modules 的忽略--list-different(别名 -l):只列出不符合格式的文件名,CI 场景常用# 用自定义 ignore 文件格式化prettier --write --ignore-path .prettierignore.custom "src/**/*.js"# CI 中检查是否有未格式化的文件prettier --check "src/**/*.js"常见踩坑ignore 规则写了不生效? 检查文件是否放在项目根目录,语法是否和 .gitignore 一致。想格式化被忽略的文件? 直接传绝对路径给 prettier,ignore 规则不拦截绝对路径。.prettierignore 和 .gitignore 规则冲突怎么办? .prettierignore 优先级更高。如果 .gitignore 排除了某个文件,但 .prettierignore 没有排除,Prettier 仍会格式化它。
前端阅读 05月27日 22:03

Prettier 如何在团队协作中发挥作用?有哪些最佳实践?

核心答案Prettier 的团队协作价值是用机器执行替代人工争论,分三个层次落地:配置即合约:.prettierrc 提交到版本控制,团队共享格式规则提交即格式化:Husky + lint-staged 在 pre-commit 自动处理,开发者无感知合并即拦截:CI 中 prettier --check 失败则阻断 PR核心原则:Prettier 管格式(缩进、换行、引号),ESLint 管质量,各司其职。配置与编辑器.prettierrc 是团队格式规范的单点真相:{ "semi": true, "singleQuote": true, "tabWidth": 2, "trailingComma": "es5", "printWidth": 80, "endOfLine": "lf"}endOfLine: "lf" 容易被忽略——Windows 默认 CRLF,混用会产生 Git diff 噪音。编辑器侧用 .vscode/settings.json 配合保存即格式化:{ "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true}工作流集成Pre-commit 自动格式化(只处理暂存文件):npm install --save-dev husky lint-stagednpx husky add .husky/pre-commit "npx lint-staged"{ "lint-staged": { "*.{js,ts,tsx,json,css,md}": ["prettier --write"] }}CI 兜底检查(防止绕过 hooks 提交):- name: Check format run: npx prettier --check "src/**/*.{js,ts,tsx}"与 ESLint 协作两者冲突是落地最常见的障碍。用 eslint-config-prettier 关闭 ESLint 中与 Prettier 重叠的格式规则:npm install --save-dev eslint-config-prettier// .eslintrc.jsmodule.exports = { extends: ["eslint:recommended", "prettier"] // prettier 必须放最后}执行顺序:Prettier 先格式化,ESLint 再检查质量,反过来会互相覆盖。踩坑要点版本锁定:Prettier 2 和 3 格式化结果不同,必须锁版本历史代码:单独 PR 执行 prettier --write,混入业务变更会导致 git blame 失效Monorepo:根目录放配置,子包通过 "prettier": "../.prettierrc" 引用,防止配置漂移性能:.prettierignore 排除 dist/node_modules,Prettier 3 支持 --cache 增量处理追问Prettier "不妥协的格式化"在什么场景下反而降低效率?如何应对?Prettier 3 的破坏性变更如何平滑升级?团队成员坚持用自己偏好的格式,配置如何平衡灵活性和一致性?
前端阅读 05月27日 22:02

如何在 VS Code 和 WebStorm 中配置 Prettier?

核心答案:项目级 .prettierrc + 编辑器保存时格式化不管用什么编辑器,Prettier 配置的核心原则只有一条:把规则写在项目根目录的 .prettierrc 里,编辑器只负责触发格式化。这样团队成员用不同编辑器,跑出来的结果完全一致。{ "semi": true, "singleQuote": true, "trailingComma": "es5", "printWidth": 80, "tabWidth": 2}VS Code 怎么配?安装 esbenp.prettier-vscode 扩展,在 .vscode/settings.json 中设为默认格式化器并开启保存格式化:{ "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true}关键设置 prettier.requireConfig: true——强制要求项目有 .prettierrc 才格式化,防止编辑器全局配置污染项目。WebStorm 怎么配?WebStorm 内置 Prettier 支持,路径:Settings → Languages & Frameworks → JavaScript → Prettier。勾选"On save"即可。如果项目 package.json 里有 prettier 依赖,WebStorm 会自动检测并启用。Vim/Neovim 怎么配?Neovim 推荐 null-ls(现已迁移至 none-ls):local null_ls = require("null-ls")null_ls.setup({ sources = { null_ls.builtins.formatting.prettier },})vim.cmd([[autocmd BufWritePre * lua vim.lsp.buf.format({ async = false })]])coc.nvim 用户::CocInstall coc-prettier,配合 BufWritePre 自动格式化。配置冲突怎么排查?Prettier 格式化不生效,按这个顺序排查:编辑器是否将 Prettier 设为默认格式化器(VS Code 常见问题:ESLint 扩展抢占了默认格式化器)项目是否有多个 .prettierrc(检查根目录和子目录),Prettier 按最近原则读取.prettierignore 是否误排除了目标文件VS Code 中 prettier.requireConfig 与项目配置是否匹配团队统一的关键动作.prettierrc 和 .prettierignore 提交到 Git.vscode/settings.json 也提交(VS Code 用户自动生效)README 中注明 WebStorm 用户需手动开启"On save"CI 中加 prettier --check . 防止未格式化代码合入
前端阅读 05月27日 22:01

Prettier overrides 配置怎么用?常见场景有哪些?

核心答案Prettier 的 overrides 允许为不同文件或目录设置差异化的格式化规则,核心语法是在配置文件中添加 overrides 数组,每项包含 files(匹配模式)和 options(覆盖选项):{ "semi": true, "singleQuote": true, "overrides": [ { "files": ["*.css", "*.scss"], "options": { "singleQuote": false } }, { "files": "*.json", "options": { "tabWidth": 4 } } ]}文件匹配模式单扩展名: "*.json" — 匹配所有 JSON 文件多扩展名: ["*.css", "*.scss", "*.less"] — 数组形式匹配多种文件目录匹配: "src/styles/**/*" — 按路径前缀限定范围排除文件: ["**/*.js", "!**/*.min.js"] — 用 ! 前缀排除,等价于 excludeFiles 字段常见场景不同语言差异化规则 — CSS 用双引号,JS 用单引号,Markdown 保留换行:{ "overrides": [ { "files": "*.css", "options": { "singleQuote": false } }, { "files": "*.md", "options": { "proseWrap": "preserve" } } ]}新老代码共存 — legacy 目录保留旧格式,新代码用新规则:{ "overrides": [ { "files": "legacy/**/*", "options": { "tabWidth": 4, "useTabs": true } }, { "files": "src/**/*", "options": { "tabWidth": 2 } } ]}指定解析器 — parser 只能写在 overrides 内,不要放顶层:{ "overrides": [ { "files": "*.vue", "options": { "parser": "vue" } } ]}优先级规则overrides 中匹配到的配置优先级高于全局配置多条 override 都匹配时,后面的覆盖前面的相同选项.editorconfig 的值会被 .prettierrc 和 overrides 依次覆盖面试追问overrides 和 .editorconfig 谁优先? — overrides 优先。Prettier 先读 .editorconfig 作为基础,再被 .prettierrc 和 overrides 层层覆盖。parser 能放顶层吗? — 不要。顶层 parser 会覆盖 Prettier 基于扩展名的自动推断,只在 overrides 中为特殊文件指定。overrides 匹配不到文件会报错吗? — 不会,未匹配的 override 静默忽略,不影响其他配置。
前端阅读 05月27日 22:01

Prettier 性能优化有哪些手段?

核心答案Prettier 性能优化有四个关键手段:缩小格式化范围、启用缓存、使用高性能解析器、升级 CLI。按收益从大到小排列:1. 只格式化变更文件——收益最大。用 lint-staged 配合 git hooks,仅处理暂存区文件,避免全量格式化:{ "lint-staged": { "*.{js,ts,tsx,json,css,md}": ["prettier --write"] }}2. 启用缓存——Prettier 3.x 支持 --cache,重复格式化时跳过未变更文件,配合 --cache-strategy=content 更精准(基于文件内容而非修改时间):prettier --write --cache --cache-strategy=content "src/**/*.{js,ts}"3. 换 Rust 解析器——安装 @prettier/plugin-oxc,用 OXC 替代默认解析器,单文件解析速度提升数倍:{ "plugins": ["@prettier/plugin-oxc"] }4. 开启实验性快速 CLI——Prettier 3.6+ 提供 --experimental-cli,实测 375 个 TSX 文件从 11 秒降到 1 秒:PRETTIER_EXPERIMENTAL_CLI=1 prettier --write .OXC 插件 + 快速 CLI 组合使用效果最佳。同时别忘配置 .prettierignore 排除 node_modules、dist、*.min.js 等无需格式化的文件。追问:Prettier 和 ESLint 怎么配合?用 eslint-config-prettier 关闭 ESLint 中与 Prettier 冲突的规则,再用 eslint-plugin-prettier 把格式问题作为 lint 错误报告。简洁配置:module.exports = { extends: ["prettier"], // 放最后,覆盖前面的格式规则};追问:Prettier 慢到不可接受怎么办?考虑 Oxfmt——Oxc 团队推出的独立格式化工具,基于 Rust 构建,比 Prettier 快 30 倍以上,且已通过 100% Prettier 兼容性测试。适合大型 monorepo 中对速度极度敏感的场景。代价是脱离 Prettier 生态,部分插件不兼容。
前端阅读 05月27日 22:00

Prettier 的缓存机制是怎么工作的?

核心回答Prettier 从 2.7 版本开始支持 --cache,原理是对比缓存键决定是否跳过格式化。缓存键包括:Prettier 版本、格式化选项、Node.js 版本、文件内容或元数据。只要这些值没变,直接跳过该文件,大型项目可提速 10-20 倍。# 启用缓存prettier . --write --cache# 指定缓存位置(默认在 node_modules/.cache/prettier/)prettier . --write --cache --cache-location .prettier-cache两种缓存策略Prettier 提供两种策略,关键区别在于判断"文件是否变化"的方式:content(默认):对文件内容做哈希,准确但稍慢。CI 环境必须用这个,因为 git clone 会重置文件时间戳metadata:比较文件的修改时间和大小,更快但在时间戳被改写的场景下可能误判# CI 环境 — 用 content 更稳prettier . --write --cache --cache-strategy content# 本地开发 — metadata 足够快prettier . --write --cache --cache-strategy metadata缓存什么时候会失效以下任一变化都会导致缓存失效:Prettier 版本升级.prettierrc 或 .editorconfig 配置变更CLI 传入的格式化选项改变文件内容/路径变化注意:插件版本不在缓存键中,更新插件后需手动清缓存。实际配置建议{ "scripts": { "format": "prettier . --write --cache", "check": "prettier . --check --cache" }}CI 中缓存 .prettier-cache 目录可进一步加速,Babel monorepo 实测从 29 秒降到 1.3 秒。# 清除缓存rm -rf node_modules/.cache/prettier追问:缓存为什么没有生效?常见原因有三个:一是用了 metadata 策略但 git 操作重置了时间戳,改用 content;二是更新了 Prettier 插件但没清缓存;三是运行时没带 --cache 参数,Prettier 会自动删除已有缓存文件。
前端阅读 05月27日 21:59

如何管理 Prettier 版本?升级策略有哪些?

核心答案Prettier 版本管理的核心原则就一条:项目内锁定精确版本,全员统一。升级时按 semver 级别分策略处理,主版本升级必须走迁移流程。锁定版本的正确姿势:{ "devDependencies": { "prettier": "3.8.0" }}用 --save-exact 安装,避免 ^ 导致团队成员版本不一致。Prettier 每个 patch 都可能改变格式化输出,这不是理论风险,是实际踩过的坑。版本升级策略Patch/Minor 升级——低风险,可直接升级:npm install --save-dev --save-exact prettier@3.8.2npx prettier --check "src/**/*.{js,ts,tsx}"跑一遍 --check,无差异就提交。建议用 Dependabot 或 Renovate 自动处理。Major 升级(如 2.x → 3.x)——高风险,必须走迁移流程:创建升级分支阅读 Changelog 中 Breaking Changes检查插件兼容性(插件 API 在 3.0 有 Breaking Change)全量 --check 后逐文件修复CI 绿了再合Prettier 3.x 迁移要点从 2.x 升级到 3.x 是最常见的主版本升级场景,注意这几个坑:插件 API 变更:embed 方法签名不兼容,自定义插件必须重写CSS 解析器拆分:--parser css 不再兜底解析 SCSS/LESS,需明确指定 scss 或 lessESLint 集成:搭配 eslint-config-prettier 关闭冲突规则:// .eslintrc.jsmodule.exports = { extends: ["prettier"] // 必须放最后}异步 API:3.x 的 prettier.format() 变成异步,脚本中需 await回滚与团队协同升级出问题,回滚要快:npm install --save-dev --save-exact prettier@2.8.8git checkout HEAD -- package-lock.jsonnpm ci团队升级的核心不是技术,是同步:锁定版本 + npm ci 保证环境一致,Husky + lint-staged 保证提交前格式化,CI 中 --check 兜底。三者缺一,迟早出格式冲突。追问Prettier 和 ESLint 冲突怎么解决?——eslint-config-prettier 关闭 ESLint 格式规则,Prettier 只管风格,ESLint 只管逻辑。为什么不用 npx prettier?——npx 每次拉最新版,团队成员格式化结果不一致,是线上事故的常见原因。如何在 monorepo 中统一版本?——根 package.json 锁版本,子包用 workspace 协议引用,CI 在根目录统一 npm ci。
前端阅读 05月27日 21:56

whistle 代理工具有哪些应用场景?

答案Whistle 是基于 Node.js 的跨平台 Web 调试代理工具,通过规则文本拦截和修改 HTTP/HTTPS 请求与响应。相比直接改代码或配 hosts,核心优势是不改代码、不切环境、规则可复用且仅对本地生效。前端开发中最常用的五个场景:本地联调将线上域名指向本地开发服务,省去切环境或改代码:api.example.com host 127.0.0.1:3000联调只影响你自己,其他同事访问线上不受干扰。多个后端服务可同时指向不同本地端口:api.example.com host 127.0.0.1:3000pay.example.com host 127.0.0.1:4000需要同时联调 WebSocket 时也没问题,whistle 默认支持 ws/wss 协议代理。接口 Mock后端接口未就绪时,用本地 JSON 文件返回模拟数据:api.example.com/user resBody://{mock-user.json}在 Whistle 的 Values 面板中创建 mock-user.json,写入模拟响应体即可。也可以直接指定状态码:api.example.com/error resStatus://500用 resDelay://3000 模拟接口超时,验证前端的 loading 和兜底逻辑。需要按条件 Mock 时,用 resScript 编写动态逻辑:// timeout.js — 支付接口模拟 5 秒超时if (rules.requestHeaders.referer && rules.requestHeaders.referer.indexOf('/pay') !== -1) { rules.responseDelay = 5000; rules.responseStatus = 504;}跨域处理开发阶段给响应注入 CORS 头,绕过浏览器同源限制:api.example.com resHeaders://{cors.json}cors.json 内容示例:{ "access-control-allow-origin": "*", "access-control-allow-methods": "GET,POST,PUT,DELETE,OPTIONS", "access-allow-headers": "Content-Type,Authorization"}比后端加跨域配置更安全——只有你自己生效,不会影响线上环境。移动端抓包与调试手机配代理指向电脑 IP:8899,安装根证书后可捕获 HTTPS 请求:手机 Wi-Fi 设置中配置 HTTP 代理,服务器填电脑 IP,端口填 8899手机浏览器访问 ip:8899,下载并安装根证书iOS 需在"设置 → 通用 → 关于本机 → 证书信任设置"中手动启用信任Android 7+ 默认不信任用户证书,需用 whistle.startProxy(8899, true) 开启全局代理或 root 后安装到系统证书目录配合内置 weinre 远程调试移动端页面 DOM 和控制台:m.example.com weinre://debug也可以安装 whistle.inspect 插件注入 vConsole/Eruda,在 App 内直接查看调试面板:npm i -g whistle.inspectm.example.com whistle.inspect://vconsole异常场景模拟用规则快速构造边界条件,测试前端兜底逻辑:# 模拟接口延迟 3 秒api.example.com/api resDelay://3000# 模拟 500 错误api.example.com/api resStatus://500# 模拟弱网(限速 30kb/s)api.example.com/api reqSpeed://30复杂条件用 resScript 处理,简单场景一条规则就够。规则管理建议用 # 注释 标记每条规则的用途,方便团队协作JSON 数据统一放在 Values 面板管理,别直接内联在规则里调试完及时关闭代理并移除证书,防止隐私泄露和证书风险善用 ignore://* 跳过不需要代理的请求,减少干扰追问whistle 和 Charles/Fiddler 的区别? —— whistle 免费开源、基于规则文本配置(可版本管理)、支持 Node 插件扩展;Charles/Fiddler 偏 GUI 操作,Charles 收费且仅 macOS/Windows,Fiddler 仅 Windows。whistle 天然跨平台且规则可团队共享。如何让 whistle 代理 HTTPS 请求? —— 需要安装并信任 whistle 的根证书:在 Whistle 界面点击 HTTPS → 安装根证书,桌面端安装到系统钥匙串/iOS 信任设置中并启用信任,Android 7+ 用户证书默认不被信任需特殊处理。浏览器和系统层面都需要配置信任。SwitchyOmega 和 whistle 是什么关系? —— SwitchyOmega 是浏览器代理切换插件,负责把浏览器流量导向 whistle 所在的地址和端口;whistle 负责具体的规则匹配和请求处理。两者配合:SwitchyOmega 控制哪些流量走代理,whistle 决定代理后怎么处理。whistle 规则的优先级是什么? —— 默认从上到下匹配,先匹配到的规则优先。可用 enable://proxyFirst 改为代理优先模式。ignore:// 规则会跳过所有其他规则,优先级最高。同名规则后写的会覆盖前面的。whistle 插件有什么用? —— 通过 Node 模块扩展功能,如 whistle.inspect 注入 vConsole/Eruda、whistle.vase 做接口录制回放、whistle.autocoder 自动生成规则。安装后 w2 restart 即可使用。
前端阅读 05月27日 21:52

Chrome 浏览器渲染流程是什么?

渲染流程六步走Chrome 将 HTML、CSS、JavaScript 转化为屏幕像素,依次经历六个阶段:构建 DOM 树:HTML 解析器将标签转为节点。遇到非阻塞资源(如图片)继续解析,遇到 <script> 则暂停 DOM 构建,等脚本执行完才恢复。构建 CSSOM 树:CSS 解析器将样式规则转为 CSS 对象模型。CSS 是阻塞渲染的资源——CSSOM 未就绪前不会进行下一步。构建渲染树:DOM 与 CSSOM 合并,只保留可见节点。<head> 及子元素、display: none 的节点不进入渲染树(visibility: hidden 仍保留,因占空间)。布局(Layout/Reflow):计算每个节点的位置和大小,输出盒模型。这是重排发生的阶段。绘制(Paint):将可见部分绘制到屏幕。样式变化不影响布局时只触发重绘,开销远小于重排。合成(Composite):多个图层合并为最终画面。使用 3D transform、will-change、video/canvas 的元素被提升为独立合成层,由 GPU 处理,跳过主线程的重排和重绘。重排、重绘与直接合成重排:几何属性变化(宽高、位置、边距、字体大小),或读取 offsetWidth/scrollTop 等触发布局计算的属性重绘:外观属性变化(颜色、背景、阴影),几何未变直接合成:仅改 transform 和 opacity,跳过布局和绘制JS 与 CSS 的阻塞关系JS 阻塞 DOM 构建:遇到 <script> 暂停解析,async/defer 可改变行为CSS 阻塞 JS 执行:脚本依赖样式时须等 CSSOM 构建完毕GUI 渲染线程与 JS 引擎线程互斥:JS 执行期间不渲染追问方向从输入 URL 到页面渲染完成,完整链路是什么?requestAnimationFrame 在渲染流程哪一步执行?与 setTimeout 有何区别?如何用 DevTools 定位重排重绘?(Performance 面板查看 Layout/Paint 事件)合成层隐式提升机制是什么?过多合成层会导致什么问题?
前端阅读 05月27日 21:47

RxJS 中 switchMap、mergeMap、concatMap 有什么区别?常用操作符有哪些?

答案:六类核心操作符面试中最常考的 RxJS 操作符分六类:创建类:of、from、interval、timer — 把同步值、数组、Promise、定时器转为 Observable。转换类:map 逐值转换;switchMap 切换到新 Observable 并取消前一个(搜索框场景首选);mergeMap 并行处理所有内部 Observable(不取消,不保证顺序);concatMap 顺序处理,上一个完成才订阅下一个。过滤类:filter 条件过滤;take(n) 只取前 n 个;takeUntil(notifier) notifier 发出时终止;debounceTime 停顿后才发出(防抖);throttleTime 间隔内只取第一个(节流)。组合类:merge 并行合并;concat 顺序连接;combineLatest 任一更新都组合最新值;zip 按索引一一配对。错误处理类:catchError 捕获并返回替代 Observable;retry(n) 失败后自动重试 n 次。工具类:tap 执行副作用不改值(调试利器);delay 延迟发射;distinctUntilChanged 连续重复值只发一次。switchMap vs mergeMap vs concatMap:面试最高频对比三者的核心区别在于对前一个内部 Observable 的处理策略:switchMap:新值到来 → 取消上一个 → 订阅新的。搜索框、路由切换等竞态场景必选,保证只拿到最新结果。mergeMap:全部并行订阅,互不干扰。适合批量请求、日志上报等不关心顺序的场景。可传第二参数控制并发数。concatMap:排队串行,上一个完成才订阅下一个。适合顺序敏感的写操作,如依次保存表单步骤。// switchMap:搜索框 — 自动取消旧请求,避免竞态fromEvent(input, 'input').pipe( debounceTime(300), switchMap(query => http.get('/search', { query })))// mergeMap:批量删除 — 限制3并发ids.pipe(mergeMap(id => http.delete(`/item/${id}`), 3))// concatMap:串行保存 — 保证顺序actions.pipe(concatMap(action => http.post('/save', action)))选错操作符的后果:搜索场景用 mergeMap 会显示旧请求结果覆盖新结果;表单保存用 mergeMap 可能后发先至导致数据错乱。高频实战组合模式// 搜索防抖 + 去重 + 空值过滤 + 自动取消旧请求searchInput.pipe( debounceTime(300), distinctUntilChanged(), switchMap(query => query.length > 0 ? searchAPI(query) : EMPTY))// 表单自动保存(去重 + 防抖)form.valueChanges.pipe( debounceTime(1000), distinctUntilChanged(), switchMap(data => saveAPI(data)))追问方向combineLatest vs zip 的区别?— combineLatest 任一更新即触发输出,zip 等所有 Observable 都发了新值才按索引配对如何避免内存泄漏?— takeUntil(destroy$\) 模式,组件销毁时 destroy$.next() 统一取消,优于手动 unsubscribeshareReplay(1) 解决什么问题?— 多订阅者共享同一数据源并缓存最新值,避免重复 HTTP 请求exhaustMap 是什么?— 忽略新值直到当前内部 Observable 完成,适合防止重复提交(登录按钮连击)forkJoin vs combineLatest?— forkJoin 等所有完成后只取各自最后一个值,类似 Promise.all;combineLatest 每次变化都触发
前端阅读 05月27日 21:46

whistle 如何支持 WebSocket 代理和调试?

核心答案Whistle 基于代理机制自动拦截 WebSocket 连接,在 Network 面板的 Frames 标签中实时展示收发消息,配合规则可以实现暂停、忽略、Mock 等调试操作。具体分三步:1. 抓包查看消息设置代理后,Whistle 自动捕获 ws/wss 请求。在 Network 面板选中该请求,切换到 Frames 标签即可看到实时消息流,JSON 消息会自动格式化。2. 用规则控制连接行为# 暂停接收服务端消息ws://example.com enable://pauseReceive# 暂停发送客户端消息ws://example.com enable://pauseSend# 忽略收发(不转发)ws://example.com enable://ignoreReceivews://example.com enable://ignoreSend3. 用 Composer 构造和发送消息在 Frames 面板底部有 Composer 区域,可以直接向服务端或客户端发送自定义文本/JSON 数据,用于模拟服务端推送或异常响应。追问一:如何修改 WebSocket 消息内容?Whistle 原生不支持直接改写 WebSocket 消息体,需要借助 frameScript 规则或插件:frameScript:在 Rules 面板配置 ws://example.com frameScript://{wsModify.js},脚本中监听 onframe 事件对帧数据做替换插件方式:安装 whistle.script 插件,配置 ws://example.com script://handleWS,在脚本中拦截并修改 message追问二:Whistle 如何处理 wss 连接?和 HTTPS 一样需要安装 Whistle 的根证书。证书装好后,Whistle 通过 MITM 方式解密 wss 流量,抓包和调试方式与 ws 完全一致。追问三:如何 Mock WebSocket 服务端?用 Composer 手动发消息是最快的方式。如需自动化,可以写插件监听 upgrade 事件,在 server.on('connection') 回调中按业务逻辑返回 Mock 数据,配置规则指向该插件即可。
前端阅读 05月27日 21:45

什么是 HttpOnly Cookie?如何使用 HttpOnly Cookie 防止 XSS 攻击?

什么是 HttpOnly CookieHttpOnly 是 Cookie 的一个属性,设置后浏览器禁止 JavaScript 通过 document.cookie 读取该 Cookie,从而阻止 XSS 攻击窃取会话信息。核心原理浏览器在收到 Set-Cookie: sessionId=abc123; HttpOnly 响应头后,会将该 Cookie 标记为只读。此时 document.cookie 的返回值中不包含该条目,恶意脚本无法获取。Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Strict// 未设 HttpOnly → 可窃取document.cookie; // "sessionId=abc123; theme=dark"// 已设 HttpOnly → 不可见document.cookie; // "theme=dark"如何设置Node.js Express:res.cookie("sessionId", token, { httpOnly: true, secure: true, sameSite: "strict"});PHP:setcookie("sessionId", $token, [ "httponly" => true, "secure" => true, "samesite" => "Strict"]);Nginx:proxy_cookie_flags ~ httponly secure samesite=strict;局限性HttpOnly 只能防止 Cookie 被读取,不能阻止 XSS 本身。攻击者仍可通过 XSS 执行以下操作:利用 fetch("/api/transfer", {credentials:"include"}) 携带 Cookie 发起请求修改页面 DOM 或重定向到钓鱼网站窃取未设 HttpOnly 的其他 Cookie因此 HttpOnly 必须与 CSP、SameSite、输入过滤等手段配合使用。追问HttpOnly 能防 CSRF 吗? 不能。CSRF 是浏览器自动携带 Cookie 发起的跨站请求,与 JavaScript 读取无关,需靠 SameSite 或 CSRF Token 防护。哪些 Cookie 不应设 HttpOnly? 前端 JS 需要访问的功能性 Cookie,如主题偏好、语言设置等。会话和认证类 Cookie 必须设 HttpOnly。设了 HttpOnly 为什么还会丢 Cookie? 中间人攻击可在未加密的 HTTP 上截获 Cookie,必须同时设置 Secure 属性强制 HTTPS 传输。
前端阅读 05月27日 21:45

RxJS 中的 Observable 和 Promise 有什么区别?

核心答案Observable 和 Promise 都是处理异步操作的工具,关键区别在四个字:单值/多值、惰性/急切、可取消/不可取消、单播/多播。| 维度 | Promise | Observable ||------|---------|------------|| 值的数量 | 只能 resolve 一次 | 可以 next 多次 || 执行时机 | 创建即执行(eager) | 订阅才执行(lazy) || 取消 | 无法取消 | unsubscribe() 取消 || 多播 | 同一 Promise 多次 then 共享同一结果 | 默认冷 Observable,每次订阅独立执行 || 错误重试 | 需手动包装 | retry 操作符一行搞定 || 操作符 | then/catch/finally | map、filter、switchMap 等上百个 |逐条展开1. 单值 vs 多值Promise 只能 resolve 一次,后续调用被忽略;Observable 通过 next 可以持续推送数据。// Promise — 只拿到 1const p = new Promise(resolve => { resolve(1); resolve(2); // 无效});// Observable — 拿到 1, 2, 3const obs$ = new Observable(subscriber => { subscriber.next(1); subscriber.next(2); subscriber.next(3); subscriber.complete();});2. 惰性 vs 急切Promise 构造函数里的代码在 new 时就跑了;Observable 的回调要等 subscribe 才执行。这意味着不订阅的 Observable 什么也不做,省资源。3. 可取消这是面试高频追问点。Promise 一旦创建就跑到完,想中途撤销只能用第三方库包装。Observable 原生支持:const sub = interval(1000).subscribe(x => console.log(x));setTimeout(() => sub.unsubscribe(), 3000); // 3秒后停止4. 冷 vs 热(单播 vs 多播)冷 Observable 每次订阅都重新执行一遍;热 Observable(如 Subject)多个订阅者共享同一数据源。Promise 天然是"热"的——创建后结果确定,所有 then 拿到同一个值。// 冷 Observable — 两个订阅各跑一次const cold$ = of(Math.random());cold$.subscribe(v => console.log(v)); // 0.123cold$.subscribe(v => console.log(v)); // 0.456(不同值)// 用 share() 变热const hot$ = cold$.pipe(share());5. 错误处理与重试Promise 出错只能 catch 一次,重试要自己写循环。Observable 有 retry、retryWhen 等操作符,一行搞定指数退避重试。// Promise — 手动重试function retryFetch(url, n) { return fetch(url).catch(err => n > 0 ? retryFetch(url, n - 1) : Promise.reject(err));}// Observable — 一行重试http.get(url).pipe(retry(3));6. 操作符生态Promise 只有 then 链,复杂异步编排(竞速、合并、去抖)要手写。Observable 有 switchMap、mergeMap、debounceTime、combineLatest 等操作符,声明式描述数据流。面试追问方向什么时候用 Promise 就够了? 单次异步请求、不需要取消、不需要流式处理——Promise 更简单。Observable 能完全替代 Promise 吗? 理论上可以(from(promise) 转换),但简单场景用 Observable 是过度设计。Angular 为什么选 Observable? HTTP 请求可能需取消(路由离开),表单值变化是流,组件生命周期也是流——Observable 统一了这些模型。
前端阅读 05月27日 21:45

什么是 SameSite Cookie?它如何防御 CSRF 和 XSS 攻击?

答案SameSite 是 Cookie 的一个属性,控制浏览器是否在跨站请求中携带 Cookie。它是防御 CSRF 攻击的核心手段,也能在一定程度上限制跨站 XSS 的危害扩散。三种模式的区别:| 模式 | 跨站请求是否携带 Cookie | 典型场景 ||------|------------------------|----------|| Strict | 完全不携带 | 银行、支付等高安全应用 || Lax | 仅顶层导航(如点击链接)携带,异步请求不携带 | 大多数 Web 应用的推荐默认值 || None | 全部携带,必须配合 Secure | 第三方登录、iframe 嵌入等跨站场景 |res.cookie('sessionId', token, { httpOnly: true, secure: true, sameSite: 'lax' // 现代浏览器默认值});为什么 Lax 是推荐默认值?Strict 最安全但有体验问题:用户从邮件或搜索引擎点击链接进入网站时,Cookie 不会被发送,导致登录态丢失。Lax 在安全与体验间取了平衡——顶层 GET 导航携带 Cookie 保证正常跳转,POST 和异步请求不携带,阻断 CSRF。SameSite 能防 XSS 吗?不能完全防。SameSite 限制的是跨站请求中的 Cookie 发送,但 XSS 发生在同站内,脚本可以直接读取非 HttpOnly 的 Cookie 或发起同站请求。正确做法是 SameSite + HttpOnly + CSP 组合使用:res.cookie('sessionId', token, { httpOnly: true, secure: true, sameSite: 'strict' });// 配合 CSP 限制脚本来源res.setHeader('Content-Security-Policy', "script-src 'self'");None 模式有什么坑?SameSite=None 必须同时设置 Secure,否则浏览器会拒绝。而且 None 模式下 Cookie 在所有跨站请求中都会发送,等于放弃了 SameSite 的防护,必须额外依赖 CSRF Token 等机制兜底。追问Chrome 80+ 未显式设置 SameSite 时默认行为是什么?——默认 Lax,未声明的 Cookie 在跨站 POST/iframe/fetch 中不会发送。SameSite=Lax 下,哪些跨站请求会携带 Cookie?——顶层 GET 导航(<a> 点击、window.open),不包含 POST 表单、iframe、fetch/XHR。如果应用需要嵌入第三方 iframe 并保持登录态,怎么处理?——SameSite=None; Secure + CSRF Token 双重防护。