Chrome扩展圈子最近炸了锅,老早写的插件突然被喷“过时”。啥情况?原来谷歌搞了个叫Manifest V3的新规矩,老版V2的扩展从2023年起逐步被下架。这玩意说白了就是一套新的API规则,管着插件怎么跟浏览器唠嗑。不跟上这波改动,辛辛苦苦码的扩展可能直接翻车。这篇东西就用大白话拆解V3到底变了哪几块,手把手给老扩展动手术,保它继续活蹦乱跳。
清单新旧有啥不同
Manifest V3和V2的核心差异集中在四个关节眼上。把这几处整明白,迁移就稳了一大半。
后台脚本变服务员
V2时代,扩展后台跑的是持久或非持久的背景页面(background pages)。V3直接换成服务员(service worker),这货不干活就自动休眠,省内存杠杠的。对比一下代码就懂:
V2写法
"background": {
"scripts": ["background.js"],
"persistent": true
}V3写法
"background": {
"service_worker": "background.js"
}改完这处,原来的后台逻辑基本不用动,但得注意服务员里不能用window、document这些DOM玩意儿,就像外卖小哥只管跑腿不管做饭。
权限拆分更细
V3把主机权限单独拎出来,不跟普通权限搅和在一块。以前写在一堆,现在分家过日子:
V2
"permissions": ["activeTab", "storage", "https://api.example.com/*"]V3
"permissions": ["activeTab", "storage"],
"host_permissions": ["https://api.example.com/*"]这么搞的好处是上架时审核更透明,哪家网站能被插件摸到一目了然。
安全策略变对象
内容安全策略(CSP)在V3里从字符串改成了对象,区分扩展页面和沙盒环境:
V2
"content_security_policy": "script-src 'self' https://trusted.com; object-src 'self'"V3
"content_security_policy": {
"extension_pages": "script-src 'self' https://trusted.com; object-src 'self'",
"sandbox": "script-src 'self' https://trusted.com; object-src 'self'"
}注意不能再用外部域的script-src,所有JS必须打包进扩展里,就像搬家不能指望邻居借你锅碗瓢盆。
拦网请求换规矩
V2里改网络请求用webRequest API,想拦广告就动态分析URL。V3换成了declarativeNetRequest,得提前写好要拦截的地址列表,像给保安一张黑名单,不能说“看着像坏人就拦”。
V2权限
"permissions": ["webRequest", "webRequestBlocking"]V3权限
"permissions": ["declarativeNetRequest"]代码里原来用chrome.webRequest.onBeforeRequest的地方,要改成chrome.declarativeNetRequest.updateDynamicRules。这波改动让广告拦截器有点伤,但普通功能扩展基本没影响。
动手改四个地方
迁移操作不玄乎,按下面四步走,老扩展原地复活。
改版本号和按钮
打开manifest.json,第一件事把"manifest_version"从2改成3。然后找"browser_action"或"page_action",全换成"action"。以前写chrome.browserAction.onClicked的地方,统统改成chrome.action.onClicked。这就像把家里的老式拉线开关换成触摸面板,功能一样但更利索。
挪主机权限
从"permissions"数组里把带http://、https://、*://的项揪出来,塞到新增的"host_permissions"里。普通权限像"storage"、"cookies"留在原地。举个例子,原来写:
"permissions": ["activeTab", "storage", "https://www.css-tricks.com/*", "*://*/*"]改成:
"permissions": ["activeTab", "storage"],
"host_permissions": ["https://www.css-tricks.com/*"],
"optional_host_permissions": ["*://*/*"]注意optional_host_permissions是V3新加的,运行时可以动态申请额外权限,适合那些“大部分网站不需要,但某些功能需要”的场景。
整容安全策略
把"content_security_policy"字段从字符串改成对象结构。如果原来写了外部域名(比如https://ajax.googleapis.com),必须删掉,因为V3禁止远程代码。所有脚本必须打包在扩展文件夹里。像这样改:
改前
"content_security_policy": "script-src 'self' https://cdn.jsdelivr.net; object-src 'self'"改后
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self'",
"sandbox": "script-src 'self'; object-src 'self'"
}要是扩展用了eval()或者new Function()这种动态执行字符串的骚操作,也得换掉,改用预定义函数或者chrome.scripting.executeScript注入文件。
替换请求拦截器
如果扩展里用到了chrome.webRequest API,得迁移到declarativeNetRequest。举个例子,原来拦某个广告域名:
V2代码
chrome.webRequest.onBeforeRequest.addListener(
function(details) { return {cancel: true}; },
{urls: ["*://*.doubleclick.net/*"]},
["blocking"]
);V3改法:先在manifest.json加上"declarativeNetRequest"权限,然后定义规则:
const rule = {
id: 1,
priority: 1,
action: { type: "block" },
condition: { urlFilter: "*://*.doubleclick.net/*", resourceTypes: ["main_frame", "script"] }
};
chrome.declarativeNetRequest.updateDynamicRules({ addRules: [rule], removeRuleIds: [1] });规则得预先写好,不能运行时动态生成URL模式。这就像提前给保安一张通缉令名单,不能看见谁长得像再打电话问。
迁移时可能会遇到服务员(service worker)执行上下文不兼容的情况。比如V2里用chrome.runtime.getBackgroundPage()拿后台页面的window对象,V3里服务员没有页面概念,得改成消息传递。原来这么写:
// V2: 在弹窗里直接调后台函数
chrome.runtime.getBackgroundPage(function(bg) {
bg.doSomething();
});改成V3后,弹窗发消息,服务员监听:
// 弹窗里
chrome.runtime.sendMessage({type: "doSomething"});
// background.js (service worker)
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.type === "doSomething") { doSomething(); }
});这就像以前直接推门进后台办公室喊人,现在得先按门铃传话。
时间线别踩坑
谷歌给老扩展判了死缓,具体节点得盯紧:
| 时间点 | 会发生啥 |
|---|---|
| 2023年1月 | 开发版Chrome关闭V2支持 |
| 2023年6月 | 商店禁止新V2扩展上架 |
| 2024年1月 | 正式版全面停用V2扩展 |
早改早省心,拖到最后一刻容易翻车。尤其是那些依赖webRequest的广告拦截类扩展,得花时间重新设计规则集。
另外注意,V3不让执行远程代码,所有JS、CSS、Wasm必须打包在扩展文件夹里。以前从CDN拉代码的骚操作,比如chrome.scripting.executeScript({code: fetch('https://some.cdn/script.js')}),直接报废。正确姿势是把文件放本地,用chrome.runtime.getURL('script.js')拿地址再注入。
跨域请求(CORS)也得挪到服务员里处理。以前内容脚本直接fetch第三方API,现在服务员代劳,结果通过消息回传。这就像点外卖别让客人自己跑腿,让服务员统一取餐。
还有个坑:V2里chrome.tabs.executeScript变成了V3的chrome.scripting.executeScript,参数格式也变了。原来写chrome.tabs.executeScript({code: 'alert("hi")'}),V3里得:
chrome.scripting.executeScript({
target: {tabId: tab.id},
func: () => alert("hi")
});或者注入文件:files: ["content.js"]。
最后,写死"manifest_version": 3后,可以用chrome.runtime.getManifest()看看版本号,确认迁移成功。跑一遍功能测试,尤其后台逻辑、权限请求、网络拦截这几块。
这波迁移看着烦,但其实改完一劳永逸。老司机带路到这,剩下的就是动手干。改完的扩展能在新规矩下继续浪,不怕被商店扫地出门。
