← 全部材料 扩展
CHROME EXTENSION · 14 个高频点

三个世界,一套消息

浏览器扩展看起来只是"写个页面加个脚本",实际难点全在上下文隔离上:后台拿不到 DOM、content script 拿不到页面变量、popup 一关就销毁。MV3 又把后台换成了会休眠的 Service Worker,很多老经验直接失效。

01先分清在哪个世界遇到"为什么拿不到这个"之前,先问自己这段代码跑在哪个上下文。
02记住会休眠Service Worker 随时被终止,是 MV3 一切问题的源头。
03权限就是产品申请得越多,用户越不敢装、审核越难过。这是一条产品约束。
1

MV3 到底改了什么★

不是加功能,是换了运行模型
MV3 最大的变化是把常驻后台页换成 Service Worker。它随时会被浏览器杀掉,所以"内存里存状态"这个老习惯必须改掉——一切要持久化的东西都得进 storage。

MV2 的习惯

  • background page 是常驻页面,有 DOM
  • 可以在内存里存全局变量当状态
  • webRequest 可以拦截并修改请求
  • 可以远程加载代码执行

MV3 的约束

  • Service Worker 随时休眠,全局变量会丢
  • 没有 DOM,不能用 document 和 XHR
  • declarativeNetRequest 只能声明规则,不能改请求体
  • 禁止远程代码,所有 JS 必须打包进来
MV3 的连锁反应后台会休眠 → 状态必须放 chrome.storage → 唤醒后要重建状态 → 消息监听必须注册在<b>文件顶层</b> (写在异步回调里,唤醒时事件已经错过了) webRequest 被限 → 拦截改请求要用 declarativeNetRequest 声明式规则 → 需要动态决策的场景变难,得靠规则 + 权限 不能远程加载代码 → 所有逻辑随包发布 → 热更新基本没了,改逻辑必须发新版本
面试怎么答MV3 把常驻后台页改成 Service Worker,它随时会被终止,所以状态必须存在 chrome.storage 里,事件监听要注册在文件顶层才能在唤醒时接住事件。webRequest 被 declarativeNetRequest 替代,只能声明规则不能改请求体。同时禁止远程代码,所有逻辑必须随包发布。
2

manifest.json 的骨架★

几个字段决定权限和入口
manifest_version 必须是 3;background.service_worker 指后台;content_scripts 指注入脚本;permissions 和 host_permissions 是两套权限,别混。
面试怎么答manifest_version 必须是 3,background 用 service_worker 指定,content_scripts 声明注入规则和 run_at 时机,action 定义工具栏按钮和弹窗。权限分成 permissions(API 权限)和 host_permissions(域名权限)两类,要按最小权限原则申请。
3

三个运行环境,各自的隔离★

同一个扩展,三套上下文
Service Worker 管事件和全局逻辑,没有 DOM;content script 跑在页面里但被隔离;popup / options 是普通网页,有自己的 DOM 和生命周期。三者之间靠消息通信。
Service Worker事件驱动,无 DOM,随时休眠
→
Content Script在页面里,但 JS 环境隔离
→
Popup / Options普通页面,关掉就销毁

各自的限制

  • SW 里没有 document、没有 window
  • SW 里 XMLHttpRequest 不可用,只能用 fetch
  • popup 一关就销毁,不能指望它存状态
  • content script 拿不到页面的 JS 变量

各自能做什么

  • SW 里发跨域请求(有 host 权限时不受 CORS 限制)
  • content script 直接改页面 DOM
  • popup 里展示 UI 和交互
  • 三者都能用 chrome.storage 共享数据
面试怎么答Service Worker 负责事件和全局逻辑但没有 DOM、随时休眠;content script 注入页面但 JS 环境隔离、拿不到页面变量;popup 和 options 是普通网页但关闭即销毁。跨域请求应该放在 Service Worker 里发,因为 content script 受页面 CORS 限制。三者之间靠消息通信和 storage 共享数据。
4

content script 的隔离世界★

为什么拿不到页面的变量
content script 和页面共享 DOM,但不共享 JS 执行环境。所以你能改页面的元素,却读不到页面定义的 window.foo。
页面拿不到扩展的 API,反过来也一样content script 的世界里: ✅ 有 chrome.runtime、chrome.storage ❌ 没有页面的 window 变量 主世界(页面自己的 JS): ✅ 有页面的一切 ❌ 没有 chrome.* API <em>所以两边的通信要靠 window.postMessage 中转。</em>
面试怎么答content script 与页面共享 DOM 但 JS 环境隔离,所以能改元素但读不到页面的全局变量,这样做是为了避免互相污染。需要访问页面变量时可以用 scripting.executeScript 指定 world 为 MAIN,或者注入脚本并通过 window.postMessage 中转通信。
5

消息通信的四种方式★

选错会很难受
最常用的是 runtime.sendMessage(发给后台/其他扩展页)和 tabs.sendMessage(发给指定标签页的 content script)。需要持续双向通信就用 connect 长连接。
四种方式的选择runtime.sendMessage 扩展内部,一次性 tabs.sendMessage 发给某个标签页,一次性 runtime.connect 长连接,双向持续(适合流式数据) window.postMessage 主世界 ↔ content script 之间
面试怎么答扩展内部用 runtime.sendMessage,发给指定标签页用 tabs.sendMessage,持续双向通信用 runtime.connect 长连接,主世界和 content script 之间用 window.postMessage。异步回复必须 return true,消息监听必须注册在文件顶层,接收方不在时要处理连接失败。
6

Service Worker 的生命周期★

30 秒没事件就会被杀
MV3 的 Service Worker 空闲约 30 秒就会被终止,有事件时再唤醒重新执行脚本。所以全局变量、定时器、内存中的状态全都不可靠。
◐ 可交互:发消息、等它被回收,看内存里还剩什么
active 剩余 12.0s(真实 30s,这里压缩演示)
消息到达空闲计时归零 → 被回收
内存里的计数0
chrome.storage 里的值0
点「发一条消息」,它会唤醒 Service Worker。
install装好,可预置默认数据
→
activate接管,清理旧数据
→
idle 30s被终止,内存清空
→
事件到来重新执行整个脚本再处理

会丢的东西

  • 全局变量(重新执行后回到初始值)
  • setTimeout / setInterval 的计时器
  • 未完成的请求(会被中断)
  • 内存里累积的缓存

可靠的东西

  • chrome.storage 里的数据
  • 事件监听(因为注册在顶层)
  • chrome.alarms(专为长周期设计)
  • IndexedDB(也可以,但要注意时序)
面试怎么答MV3 的 Service Worker 空闲约 30 秒会被终止,有事件时重新执行整个脚本。所以状态必须放在 chrome.storage,定时任务要用 chrome.alarms 而不是 setInterval,顶层代码必须幂等避免重复注册监听。调试时 DevTools 打开会保持活跃,不要因此误判。
7

权限模型与最小权限★

权限越多,审核越难,用户越不敢装
activeTab 是个好东西:用户点击扩展时临时获得当前页权限,不用在安装时吓人。<all_urls> 则会让审核和用户都警惕。
权限的三个层次activeTab 用户点了才生效,临时授权当前页 ← 首选,能覆盖大多数场景 optional_permissions 运行时再申请,用户同意才生效 ← 高级功能用这个 host_permissions 指定域名,安装时就提示 <all_urls> 所有网站 ← 审核最严,尽量别用
面试怎么答权限要按最小必要原则申请。activeTab 在用户主动点击时临时授权当前页,能覆盖多数场景且安装提示更友好。可选权限可以在运行时申请。all_urls 这类宽泛权限会显著影响安装率和审核通过率,多余的权限是常见拒绝原因。
8

脚本注入:声明式与编程式★

什么时候该用 scripting
在 manifest 里写 content_scripts 是声明式,页面一匹配就自动注入;用 chrome.scripting.executeScript 是编程式,可以按用户操作、按条件动态注入。
面试怎么答声明式注入写在 manifest 的 content_scripts 里,页面匹配就自动执行;编程式用 chrome.scripting.executeScript 在需要时注入,可以注入文件或函数,还能指定在 MAIN world 执行以访问页面变量。函数注入时不能闭包外部变量,数据要通过 args 传。
9

declarativeNetRequest 替代 webRequest★

广告拦截类扩展的核心变化
MV3 不允许扩展在请求过程中"看一眼再决定"。只能事先声明规则,由浏览器自己执行。这换来了隐私(扩展看不到你的流量),代价是灵活性。

webRequest 能做但 DNR 不能

  • 根据请求内容动态决定拦不拦
  • 修改请求体
  • 读到完整的请求和响应内容
  • 任意复杂的判断逻辑

DNR 能做的

  • 按 URL、资源类型、域名拦截
  • 重定向、修改请求头
  • 动态增删规则(有数量上限)
  • 分静态规则和动态规则两套
面试怎么答MV3 用 declarativeNetRequest 取代 webRequest,只能事先声明规则由浏览器执行,不能动态读取请求内容,换来的是扩展看不到用户流量。规则按优先级匹配、有数量上限,支持拦截、重定向和修改请求头。
10

storage:唯一可靠的持久层★

三块存储,用途不同
chrome.storage.local 存本地数据(默认 10MB,可申请更大),sync 跨设备同步但只有 100KB 左右,session 在浏览器会话内有效。
三块存储怎么选local 10MB(可 unlimitedStorage) 设备本地,存缓存和大量数据 sync 约 100KB,单项 8KB 跨设备同步,只适合设置类小数据 session 会话期,浏览器关闭清空 临时状态,替代以前的内存变量 <em>以前放全局变量的东西,现在都放 session。</em>
面试怎么答chrome.storage 分 local、sync、session 三块。local 容量大适合本地缓存,sync 只有约 100KB 适合跨设备同步设置,session 在浏览器会话内有效,正好替代以前的内存全局变量。onChanged 事件在三个上下文都能监听,可以用来替代部分消息通信。
11

扩展的五个 UI 入口

别把功能全塞进 popup
popup 只是最显眼的一个。右键菜单适合页面上下文操作,sidePanel 适合需要常驻的场景,commands 是快捷键,omnibox 把地址栏变成输入框。
按场景选入口popup 轻量操作,点开就关,一关就销毁 contextMenus 页面右键,适合"对选中文本做什么" sidePanel 需要和页面并行查看,常驻不销毁 ← MV3 新增 commands 高频操作,键盘快捷键 omnibox 关键词触发,适合搜索类 options 配置页,从扩展详情进入
面试怎么答扩展有 popup、右键菜单、侧边栏、快捷键和 omnibox 等多个 UI 入口,要按场景选择。popup 关闭即销毁不能承担长任务,需要常驻用 sidePanel,页面上下文操作用 contextMenus,高频操作配 commands 快捷键。
12

打包与上架审核

最常见的拒绝原因就那几个
上架审核主要看三件事:权限是否与功能匹配、功能是否描述清楚、有没有违规行为(远程代码、误导性描述、采集数据不声明)。
提交前的清单zip 打包(不要包含 node_modules 和源码,除非必要) zip -r ext.zip . -x "node_modules/*" -x "*.map" 必备材料 图标 128×128 截图(至少一张,显示真实界面) 隐私说明:采集什么数据、怎么用、存哪 权限用途说明:每个敏感权限都要解释 常见拒绝原因 ❌ 申请了用不到的权限 ❌ 描述里堆关键词,功能说不清 ❌ 远程加载代码(CDN 上的 JS 也算) ❌ 单一用途原则:一个扩展做太多不相关的事 ❌ 隐私政策缺失或不一致
面试怎么答上架要按规范打包并准备图标、截图、隐私说明和每个敏感权限的用途解释。最常见的拒绝原因是申请多余权限、描述不清、远程加载代码、违反单一用途原则和隐私政策缺失。审核周期不稳定,更新也需要审核,要预留时间。
13

调试的几个实用技巧

很多坑是工具问题不是代码问题
四个地方要看:页面(content script 的日志)、扩展的 Service Worker(后台日志)、popup(自己的 DevTools)、chrome://extensions 的错误面板。
日志分别在哪看content script 在<b>页面</b>的 DevTools Console 里 (但要在 Console 的 context 下拉里选扩展的上下文) Service Worker chrome://extensions → 点 "Service Worker" 链接 会打开独立的 DevTools popup 右键 popup → 检查 manifest / 权限 chrome://extensions → Errors 按钮
面试怎么答content script 的日志在页面 DevTools 里、Service Worker 的日志要点扩展管理页里的链接单独打开、popup 右键检查、权限和 manifest 错误在 chrome://extensions 的 Errors 里看。改代码没生效通常是没重新加载扩展或没刷新页面。
14

速答清单

考前十分钟只看这页
01MV3 相比 MV2 最大的变化?
后台从常驻页面换成 Service Worker,会随时休眠,状态必须存 storage。
02Service Worker 什么时候被杀?
空闲约 30 秒就被终止,有事件时重新执行整个脚本,所以顶层代码要幂等。
03content script 为什么拿不到页面变量?
它和页面共享 DOM 但 JS 环境隔离。要访问得用 scripting 注入到 MAIN world。
04跨域请求该在哪发?
Service Worker 里。content script 受页面 CORS 限制,SW 有 host 权限时可跨域。
05异步回复消息要注意什么?
onMessage 里必须 return true,否则通道立刻关闭。监听必须注册在顶层。
06定时任务用什么?
chrome.alarms,不能用 setInterval,因为 SW 会被终止。
07activeTab 有什么好处?
用户点击时才临时授权当前页,安装提示更友好,能覆盖多数场景。
08declarativeNetRequest 限制在哪?
只能声明规则由浏览器执行,不能动态读请求内容,也不能改请求体。
09三块 storage 怎么选?
local 存大量数据,sync 跨设备但只有约 100KB,session 存会话期临时状态。
10常见的审核拒绝原因?
多余权限、描述不清、远程加载代码、违反单一用途原则、隐私政策缺失。