Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

将 Manifest V2 的扩展程序迁移到 Manifest V3 的注意事项 #121

Open
lmk123 opened this issue Dec 3, 2022 · 2 comments
Open

Comments

@lmk123
Copy link
Owner

lmk123 commented Dec 3, 2022

2023 年 11 月 17 日更新:Chrome 发了新公告,准备从 2024 年 6 月开始下架并禁用 Manfiest V2 了 😂

2022 年 12 月 13 日更新:哈哈哈,Chrome 果然推迟了 Manifest V2 的下线时间:

image

图中链接:

image

以下是原文。


最近着手准备将划词翻译迁移到 Manifest V3,我以为最多只是没了 DOM 环境,等真的开始迁移了才发现这件事比我想的要复杂得多。

Chrome 官方虽然提供了迁移指南,但这份指南略微简陋,我将在下面补充一些细节。

没了 DOM 环境后需要改动的点

先说说 DOM 环境吧,这一点划词翻译需要注意的地方有:

  • 所有用到了 XMLHTTPRequest 的地方(包括基于 XMLHttpRequest 的库例如 axios)都要换成 fetch。
    • 基于 axios 的错误处理、baseURL 接口调用、响应结果拦截之类的也要改为基于 fetch 来做
    • 特别指出一:fetch 中遇到 HTTP 400 之类的响应不会抛错,所以需要自行检查 response.ok 来判断调用是否成功。
    • 特别指出二:responseType: 'document' 要换成基于字符串的解析方式了。
  • Web Storage API 要换成 chrome.storage API
  • 能且只能基于 DOM 环境的操作(比如创建 iframe)需要改为创建标签页 + 消息传递的方式来替换。

Service Worker 需要单独的 Webpack 输出配置

划词翻译使用了 Webpack 来构建,目前,划词翻译使用同一份 Webpack 配置来处理内容脚本和背景页的代码,因为它们都是输出成 web 的,但背景页变成 Service Worker 之后,背景页的代码就要单独输出成 webworker 了。

Service Worker 不仅仅是没了 DOM 环境,它还是一个事件驱动的背景页

我一开始以为需要注意的点只是没了 DOM 环境,但后来我意识到,Service Worker 是一个事件驱动的背景页,这就很棘手了……

在解释这个问题之前需要先讲一下大致的背景。

在我 2012 年开发划词翻译的时候,背景页是持续存在的,也就是只要浏览器启动,那么背景页就会被加载并运行,直到浏览器关闭了背景页才会关闭。

后来在 2018 年,Chrome 推荐开发者迁移成事件驱动的背景页以减少资源占用,这种背景页简单概括就是,Chrome 会在扩展程序的背景页空闲时关闭背景页,只有产生了背景页监听的事件的时候才会重新打开背景页。

举个例子,假设我们的背景页只有下面的代码:

var tabs = []

chrome.tabs.onCreated.addListener(tab =>{
  tabs.push(tab.id)
})

setInterval(() => {
  console.log(tabs)
})

在背景页是持续存在的时候,这段代码没有任何问题,但是当背景页是事件驱动的时候就有问题了,它的运行逻辑会变成这样:

  1. 当浏览器被打开时,这个代码会执行一次,此时 tabs 是空数组
  2. 在背景页还没被 Chrome 关闭的情况下,用户新建了标签页时,onCreated 事件会被执行,此时 tabs 就不是空的了
  3. 当 onCreated 有一段时间(大概几分钟)没被触发的时候,背景页就会被 Chrome 关闭,无论你的代码(setInterval、Websocket、XMLHttpRequest 等)是否正在运行
  4. 当背景页被关闭后,下一次用户再创建了标签页的时候,Chrome 才会重新打开背景页确保 onCreated 事件会被触发,但此时 tabs 又变成空数组了

可以使用 chrome.runtime.onSuspend 在背景页被关闭得到通知,但无法阻止 Chrome 关闭它。另外,Manifest V3 中不会触发此事件

很明显,这么一对比,肯定是持续存在的背景页更省事,所以划词翻译一直没有迁移成事件驱动的背景页,但背景页变成 Service Worker 之后,浏览器会强制在 5 分钟后关闭 Service Worker,然后只在有事件被触发时才重新运行 Service Worker,所以从事实上将 Manifest V3 的扩展程序变成了事件驱动的背景页。

很多扩展程序就是因为这一点没法迁移到 Manifest V3,贴两个典型的:

于我而言,这才是最让我头皮发麻的一点,这意味着:

  • 所有临时变量都需要保存到 chrome.storage 里并从 chrome.storage 里读取
  • 所有 chrome 事件都要改为在顶层注册,不能异步注册

具体的细节可以查阅 Migrating from background pages to service workers

可是我的背景页非常的复杂,这一改动工作量巨大,且一定会出 bug,这才是迁移过程中最大的障碍。

Service Worker 直到现在(2022 年 12 月 7 日)仍有很多未解决的问题

距离 Chrome 决定全面禁用 Manifest V2 只有半年时间了,但 Service Worker 仍然有很多问题。

chrome.runtime.webRequest 不会唤醒 Service Worker 的 bug 直到 Chrome v108(两天前才刚发布)里才修复,而除此之外,我还看到很多 bug 反馈说还有很多事件都没有唤醒 Service Worker,比如 chrome.runtime.sendMessage(见 MV3 Serviceworker won't wake up when sent a message from the contentscript

换句话说,即使我今天就把划词翻译成功迁移到了 Manifest V3,那也需要用户全都立刻升级到最新版本的 Chrome 才能正常用,这根本不可能。

除此之外,Service Worker 还有很多不兼容 Manifest V2 的地方,有人列了一个清单:Use cases that are not well served by service workers

我怀疑今年 6 月份 Chrome 还是没法禁用 Manifest V2,也但愿如此 😂

总结

迁移最大的障碍是要把一个持续存在的背景页迁移成事件驱动的,其次才是 DOM 环境的问题。我打算先做 DOM 环境的迁移,万一 Chrome 之后提供持久存在的 Service Worker 了呢 😂

@biiauchann
Copy link

下一个版本中,edge中deepl也一样受到影响吗?

@lmk123
Copy link
Owner Author

lmk123 commented Dec 11, 2022

下一个版本中,edge中deepl也一样受到影响吗?

@biiauchann Chrome、Edge、Firefox 都会受到同等的影响

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants