升级 WXT
概览
要将 WXT 升级到最新主版本:
安装时跳过脚本,这样
wxt prepare
不会运行 —— 主版本变更后它很可能会报错(我们稍后再运行它)。shpnpm i wxt@latest --ignore-scripts
按照下方的升级步骤修复所有不兼容变更。
运行
wxt prepare
。它应该可以成功运行,之后类型错误也会消失。shpnpm wxt prepare
手动测试,确保开发模式和生产构建都能正常工作。
对于次版本或补丁版本的更新,无需特殊步骤。只需用你的包管理器更新即可:
pnpm i wxt@latest
下方列出了升级到新版本 WXT 时需要处理的所有不兼容变更。
目前,WXT 处于预发布阶段。这意味着第二位数字的变更(v0.X
)被视为主版本变更,并包含不兼容变更。一旦 v1 发布,只有主版本号变更才会有不兼容变更。
v0.19.0 → v0.20.0
v0.20 是一次重大更新!有许多不兼容变更,因为这个版本旨在作为 v1.0 的候选发布。如果一切顺利,v1.0 将不会有额外的不兼容变更。
TIP
在更新代码前,请先通读所有变更内容。
移除 webextension-polyfill
WXT 的 browser
不再使用 webextension-polyfill
!
升级有两种方式:
停止使用 polyfill
如果你已经在用
extensionApi: "chrome"
,那你本来就没用 polyfill,无需更改!否则只需做一个更改:
browser.runtime.onMessage
不再支持用 promise 返回响应:tsbrowser.runtime.onMessage.addListener(async () => { const res = await someAsyncWork(); return res; browser.runtime.onMessage.addListener(async (_message, _sender, sendResponse) => { someAsyncWork().then((res) => { sendResponse(res); }); return true; });
继续使用 polyfill —— 如果你想继续用 polyfill,也可以!这样升级时就不用担心这部分了。
安装
webextension-polyfill
和 WXT 的新 polyfill 模块:shpnpm i webextension-polyfill @wxt-dev/webextension-polyfill
在配置中添加 WXT 模块:
tsexport default defineConfig({ modules: ['@wxt-dev/webextension-polyfill'], });
新的 browser
对象(及类型)由 WXT 的新包 @wxt-dev/browser
提供。该包延续了 WXT 为整个社区提供有用包的理念。就像 @wxt-dev/storage
、@wxt-dev/i18n
、@wxt-dev/analytics
一样,它设计为可在任何 web 扩展项目中轻松使用,不仅限于 WXT 项目,并为所有浏览器和 manifest 版本提供一致的 API。
移除 extensionApi
配置
extensionApi
配置已被移除。此前,该配置用于在 v0.20.0 之前切换到新的 browser
对象。
如果你的 wxt.config.ts
文件中有它,请将其移除:
export default defineConfig({
extensionApi: 'chrome',
});
扩展 API 类型变更
随着 v0.20 引入新的 browser
,类型的获取方式也变了。WXT 现在基于 @types/chrome
提供类型,而不是 @types/webextension-polyfill
。
这些类型与 MV3 API 更同步,bug 更少,结构更清晰,没有自动生成的名称。
要获取类型,请使用 wxt/browser
中的 Browser
命名空间:
import type { Runtime } from 'wxt/browser';
import type { Browser } from 'wxt/browser';
function getMessageSenderUrl(sender: Runtime.MessageSender): string {
function getMessageSenderUrl(sender: Browser.runtime.MessageSender): string {
// ...
}
如果你用自动导入,
Browser
会自动可用,无需手动导入。
并非所有类型名都与 @types/webextension-polyfill
一致。你需要根据实际用到的 browser.*
API 查找新的类型名。
public/
和 modules/
目录位置变更
public/
和 modules/
目录的默认位置已变更,以更好地与其他框架(Nuxt、Next、Astro 等)保持一致。现在,每个路径都相对于项目根目录,而不是 src 目录。
如果你用的是默认文件结构,无需更改。
如果你设置了自定义
srcDir
,有两种选择:将你的
public/
和modules/
目录移到项目根目录:html📂 {rootDir}/ 📁 modules/ 📁 public/ 📂 src/ 📁 components/ 📁 entrypoints/ 📁 modules/ 📁 public/ 📁 utils/ 📄 app.config.ts 📄 wxt.config.ts
保持目录不变,并在项目配置中指定:
tsexport default defineConfig({ srcDir: 'src', publicDir: 'src/public', modulesDir: 'src/modules', });
导入路径变更与 #imports
wxt/sandbox
、wxt/client
、wxt/storage
导出的 API 已迁移到 wxt/utils/*
下的独立导出。
为什么?
随着 WXT 的发展,越来越多的工具被加入,带副作用的工具如果未被 tree-shake,会被打包进最终产物。
这会导致问题,因为不是每个入口点都能用所有 API。有些 API 只能在 background 用,沙盒页面不能用扩展 API 等。这会导致 JS 在顶层作用域报错,阻止代码运行。
将每个工具拆分为独立模块可以解决这个问题,确保你只在能运行的入口点导入对应 API 和副作用。
请参考更新后的 API 参考 查看新的导入路径。
不过,你无需记住这些新路径!v0.20 新增了虚拟模块 #imports
,开发者只需从这里导入即可。详见 博客。
所以升级时,只需将所有从 wxt/storage
、wxt/client
、wxt/sandbox
的导入替换为 #imports
:
import { storage } from 'wxt/storage';
import { defineContentScript } from 'wxt/sandbox';
import { ContentScriptContext, useAppConfig } from 'wxt/client';
import { storage } from '#imports';
import { defineContentScript } from '#imports';
import { ContentScriptContext, useAppConfig } from '#imports';
你可以合并为一个导入语句,但直接查找替换每条语句更简单。
import { storage } from 'wxt/storage';
import { defineContentScript } from 'wxt/sandbox';
import { ContentScriptContext, useAppConfig } from 'wxt/client';
import {
storage,
defineContentScript,
ContentScriptContext,
useAppConfig,
} from '#imports';
TIP
要让类型生效,安装 v0.20 后需运行 wxt prepare
生成新的 TypeScript 声明。
createShadowRootUi
CSS 变更
WXT 现在通过在 shadow root 内设置 all: initial
,重置了网页继承的样式(如 visibility
、color
、font-size
等)。
WARNING
这不会影响 rem
单位。如果网页设置了 HTML 元素的 font-size
,你仍需用 postcss-rem-to-px
或类似库。
如果你用到了 createShadowRootUi
:
移除所有手动重置特定网站样式的 CSS。例如:
cssbody { /* 覆盖 Reddit 默认的 "hidden" 可见性 */ visibility: visible !important; }
仔细检查你的 UI 是否与之前一致。
如果新行为有问题,你可以禁用它,继续用原有 CSS:
const ui = await createShadowRootUi({
inheritStyles: true,
// ...
});
默认输出目录变更
outDirTemplate
配置的默认值已变更。现在,不同构建模式输出到不同目录:
--mode production
→.output/chrome-mv3
:生产构建不变--mode development
→.output/chrome-mv3-dev
:开发模式目录加-dev
后缀,不再覆盖生产构建--mode custom
→.output/chrome-mv3-custom
:自定义模式以-[mode]
结尾
如需旧行为(所有输出写到同一目录),设置 outDirTemplate
:
export default defineConfig({
outDirTemplate: '{{browser}}-mv{{manifestVersion}}',
});
WARNING
如果你之前手动加载过开发版扩展,请从新的 dev 输出目录卸载并重新安装。
移除已废弃 API
entrypointLoader
选项:WXT 现在用vite-node
导入入口点。v0.19.0 已废弃,迁移见 v0.19 部分。
transformManifest
选项:用build:manifestGenerated
钩子替代:tsexport default defineConfig({ transformManifest(manifest) { hooks: { 'build:manifestGenerated': (_, manifest) => { // ... }, }, });
新的废弃项
runner
API 重命名
为与 web-ext.config.ts
文件名保持一致,"runner" API 及配置项已重命名。你仍可用旧名,但它们已废弃,将在未来版本移除:
runner
选项重命名为webExt
:tsexport default defineConfig({ runner: { webExt: { startUrls: ["https://wxt.dev"], }, });
defineRunnerConfig
重命名为defineWebExtConfig
:tsimport { defineRunnerConfig } from 'wxt'; import { defineWebExtConfig } from 'wxt';
ExtensionRunnerConfig
类型重命名为WebExtConfig
tsimport type { ExtensionRunnerConfig } from 'wxt'; import type { WebExtConfig } from 'wxt';
v0.18.5 → v0.19.0
vite-node
入口点加载器
默认入口点加载器已改为 vite-node
。如果你用的 NPM 包依赖 webextension-polyfill
,需加到 Vite 的 ssr.noExternal
选项:
export default defineConfig({
vite: () => ({
ssr: {
noExternal: ['@webext-core/messaging', '@webext-core/proxy-service'],
},
}),
});
查看完整文档 了解更多。
此变更带来:
可导入变量并在入口点选项中使用:
import { GOOGLE_MATCHES } from '~/utils/constants'
export default defineContentScript({
matches: [GOOGLE_MATCHES],
main: () => ...,
})
可用 Vite 特有 API(如 import.meta.glob
)定义入口点选项:
const providers: Record<string, any> = import.meta.glob('../providers/*', {
eager: true,
});
export default defineContentScript({
matches: Object.values(providers).flatMap(
(provider) => provider.default.paths,
),
async main() {
console.log('Hello content.');
},
});
总之,现在可以在入口点的 main
函数外导入和执行代码 —— 以前不行。但仍建议避免在 main
外运行代码,以保证构建速度。
如需继续用旧方式,在 wxt.config.ts
中添加:
export default defineConfig({
entrypointLoader: 'jiti',
});
WARNING
entrypointLoader: "jiti"
已废弃,下个主版本将移除。
移除 CJS 支持
WXT 不再支持 Common JS。如果你用 CJS,迁移步骤如下:
- 在
package.json
中加上"type": "module"
。 - 将用 CJS 语法的
.js
文件扩展名改为.cjs
,或改为 ESM 语法。
Vite 也有 ESM 迁移指南,详见:https://vitejs.dev/guide/migration#deprecate-cjs-node-api
v0.18.0 → v0.18.5
此版本发布时未视为不兼容变更……但其实应该算。
新增 modules/
目录
WXT 现在识别 modules/
目录为 WXT 模块 文件夹。
如果你已有 <srcDir>/modules
或 <srcDir>/Modules
目录,wxt prepare
等命令会失败。
你有两种选择:
【推荐】保持文件原位,并告知 WXT 查找其他目录:
tsexport default defineConfig({ modulesDir: 'wxt-modules', // 默认为 "modules" });
重命名你的
modules
目录。
v0.17.0 → v0.18.0
自动将 MV3 host_permissions
转为 MV2 permissions
出于谨慎,此变更被标为不兼容,因为权限生成方式变了。
如果你在 wxt.config.ts
的 manifest 中列出了 host_permissions
并已发布扩展,请务必检查所有目标浏览器的 .output/*/manifest.json
文件,确保 permissions
和 host_permissions
未发生变化。权限变更可能导致扩展升级后被禁用,用户流失,请务必核对。
v0.16.0 → v0.17.0
Storage - defineItem
需 defaultValue
选项
如果你用带版本的 defineItem
且无默认值,现在需加 defaultValue: null
,并更新第一个类型参数:
const item = storage.defineItem<number>("local:count", {
const item = storage.defineItem<number | null>("local:count", {
defaultValue: null,
version: ...,
migrations: ...,
})
如果不传第二个 options 参数,仍默认为可空类型。
const item: WxtStorageItem<number | null> =
storage.defineItem<number>('local:count');
const value: number | null = await item.getValue();
Storage - 修正 watch
回调类型
如果你不用 TypeScript,这只是类型变更,不影响功能。
const item = storage.defineItem<number>('local:count', { defaultValue: 0 });
item.watch((newValue: number | null, oldValue: number | null) => {
item.watch((newValue: number, oldValue: number) => {
// ...
});
v0.15.0 → v0.16.0
输出目录结构变更
输出目录下 JS 入口点位置已变。除非你有后处理需求,否则无需更改。
.output/
<target>/
chunks/
some-shared-chunk-<hash>.js
popup-<hash>.js // [!code --]
popup.html
popup.html
popup.js // [!code ++]
v0.14.0 → v0.15.0
zip.ignoredSources
重命名为 zip.excludeSources
export default defineConfig({
zip: {
ignoredSources: [
/*...*/
],
excludeSources: [
/*...*/
],
},
});
未文档常量重命名
#380 中重命名了用于运行时检测构建配置的未文档常量。现已在此文档:https://wxt.dev/guide/multiple-browsers.html#runtime
__BROWSER__
→import.meta.env.BROWSER
__COMMAND__
→import.meta.env.COMMAND
__MANIFEST_VERSION__
→import.meta.env.MANIFEST_VERSION
__IS_CHROME__
→import.meta.env.CHROME
__IS_FIREFOX__
→import.meta.env.FIREFOX
__IS_SAFARI__
→import.meta.env.SAFARI
__IS_EDGE__
→import.meta.env.EDGE
__IS_OPERA__
→import.meta.env.OPERA
v0.13.0 → v0.14.0
Content Script UI API 变更
createContentScriptUi
、createContentScriptIframe
及部分选项已重命名:
createContentScriptUi({ ... })
→createShadowRootUi({ ... })
createContentScriptIframe({ ... })
→createIframeUi({ ... })
type: "inline" | "overlay" | "modal"
改为position: "inline" | "overlay" | "modal"
onRemove
现在在 UI 从 DOM 移除之前调用,之前是移除后mount
选项重命名为onMount
,以与onRemove
匹配
v0.12.0 → v0.13.0
新的 wxt/storage
API
wxt/storage
不再依赖 unstorage
。部分 unstorage
API(如 prefixStorage
)已移除,其他如 snapshot
变为新 storage
对象的方法。大部分标准用法不变。详见 https://wxt.dev/guide/storage 和 https://wxt.dev/api/reference/wxt/storage/(#300)
v0.11.0 → v0.12.0
API 导出变更
defineContentScript
和 defineBackground
现在从 wxt/sandbox
导出,不再是 wxt/client
。(#284)
如果你用自动导入,无需更改。
如果你禁用了自动导入,需手动更新导入语句:
tsimport { defineBackground, defineContentScript } from 'wxt/client'; import { defineBackground, defineContentScript } from 'wxt/sandbox';
v0.10.0 → v0.11.0
Vite 5
你需要将其他 Vite 插件升级到支持 Vite 5 的版本。
v0.9.0 → v0.10.0
扩展图标发现
WXT 不再自动发现 .png
以外的图标。如果你之前用 .jpg
、.jpeg
、.bmp
或 .svg
,需将图标转为 .png
,或手动在 wxt.config.ts
的 manifest 中添加。
v0.8.0 → v0.9.0
默认移除 WebWorker
类型
默认从 .wxt/tsconfig.json
移除了 "WebWorker"
类型。该类型适用于用 service worker 的 MV3 项目。
如需恢复,在项目 TSConfig 中添加:
{
"extends": "./.wxt/tsconfig.json",
"compilerOptions": {
"lib": ["ESNext", "DOM", "WebWorker"]
}
}
v0.7.0 → v0.8.0
defineUnlistedScript
未列出的脚本现在必须 export default defineUnlistedScript(...)
。
BackgroundDefinition
类型
将 BackgroundScriptDefintition
重命名为 BackgroundDefinition
。
v0.6.0 → v0.7.0
Content Script CSS 输出位置变更
内容脚本 CSS 以前输出到 assets/<name>.css
,现在为 content-scripts/<name>.css
,与文档一致。
v0.5.0 → v0.6.0
vite
配置需为函数
vite
配置项现在必须为函数。如果你之前用对象,改为 vite: { ... }
到 vite: () => ({ ... })
。
v0.4.0 → v0.5.0
还原 public 目录位置
默认 publicDir
从 <rootDir>/public
改回 <srcDir>/public
。
v0.3.0 → v0.4.0
更新默认路径别名
在 .wxt/tsconfig.json
中使用相对路径别名。
v0.2.0 → v0.3.0
移动 public 目录
默认 publicDir
从 <srcDir>/public
改为 <rootDir>/public
。
提升类型安全
为 browser.runtime.getURL
增加类型安全。
v0.1.0 → v0.2.0
重命名 defineBackground
将 defineBackgroundScript
重命名为 defineBackground
。