
在wxt框架中如何使用wasm三方库
本文讲介绍如何在wxt框架中使用wasm库。
WXT:Next-gen Web Extension Framework
WXT 是一个基于 Vite 的 Web 浏览器插件框架,旨在为开发者提供一个简单、高效、灵活的开发环境。 大概2023发布第一版,github start 数量已经有5.7k了(截止2025/2/21)。这是我第一次开发浏览器插件,便选择用这个框架试试水。 我开发的时候使用了一个wxt模板,同时支持tailwind,shadcn,多语言。个人感觉不错,欢迎大家尝试。
项目背景
我打算使用开发一个图片编辑插件。由于之前有一定的rust使用经验,我便把眼光又投向了基于rust开发,然后编译为wasm的图片库。想用来提高 性能(逼格)。 我有使用到两个wasm库,分别是:
这两个库我都是使用的官方的npm包,而不是自己构建的包。他们都是使用wasm-pack 打包,不过参数有所不同,这也导致了两者的所需要的支持方式有所区别。
wasm-pack 是如何给rust仓库打包的?
wasm-pack 是一个用于将 Rust 编译为 WebAssembly 的工具,类似于Emscripten之于C/C++。 wasmpack 在打包时,有一个可选项是--target
通过这个选项可以指定不同的bundler
Option | Usage | Description |
---|---|---|
not specified or bundler | Bundler | 输出适合与像Webpack这样的Bundler进行交互的JS代码。您将导入JS代码,并在package.json中指定模块键。默认情况下,sideEffects为false。 |
nodejs | Node.js | 输出使用CommonJS模块的JS代码,用于与require语句一起使用。package.json中的main键。 |
web | Native in browser | 输出可以在浏览器中作为ES模块原生导入的JS代码,但必须手动实例化和加载WebAssembly。 |
no-modules | Native in browser | 与web相同,除了JS代码被包含在页面上并修改全局状态,不支持像web那样多的wasm-bindgen特性。 |
deno | Deno | 输出可以在deno中作为ES模块原生导入的JS代码。 |
在photon中,根据官方文档描述使用wasm-pack build ./crate,没有指定bundler,而image中使用[wasm-pack build --target web]指定了target为web(通过github代码的实例推断)。 这两种打包方式在我实际使用时的区别就是,没有指定target时,wasm默认在import库的时候就会初始化,并且是顶层的异步初始化,而就是这个顶层的异步初始化导致一些列的编译失败ERROR Module format "iife" does not support top-level await. Use the "es" or "system" output formats rather.
。而指定target为web时,wasm的初始化是在页面加载完成之后,在代码中使用前手动初始化。
// no target
import {funcA} from 'photon'
funcA(...)
// with target=web
import initWasmMoudle, {funcB} from 'image'
async function init() {
await initWasmMoudle()
funcB(...)
}
针对这两种情况如何解决
我在wxt 中,是background脚本需要使用wasm,所以background脚本配置为例,其余的如content script 没尝试过,估计差不多。
没有指定target
没有指定target的情况下需要配置两个地方:
- 在
wxt.config.ts
中配置两个插件,分别是vite-plugin-wasm
vite-plugin-top-level-await
import {defineConfig} from 'wxt'; import react from '@vitejs/plugin-react'; import wasm from "vite-plugin-wasm"; import topLevelAwait from "vite-plugin-top-level-await"; export default defineConfig({ manifest: { // ... }, vite: () => ({ plugins: [react(), wasm(), topLevelAwait()], }), });
- 在 background脚本配置
type
类型为module
export default defineBackground({ // Set manifest options type: 'module', //非异步函数 main() { // 你的函数功能实现 } });
指定target为web
这种情况需要额外增加wxt的module来辅助打包,wxt module 有点类似于打包时提供给用户的hook,方便执行一些你们自己的操作。而我们这里要做的时,把node modules
目录下的wasm 文件拷贝到我们wxt的编译输出目录下,这点 官方文档已有说明。下面已我项目为例: 目录结构大概如下


import { resolve } from 'node:path';
import { defineWxtModule } from 'wxt/modules';
export default defineWxtModule((wxt) => {
wxt.hook('build:publicAssets', (_, assets) => {
assets.push({
absoluteSrc: resolve(
'node_modules/@refilelabs/image/image_bg.wasm',
),
relativeDest: 'image_bg.wasm',
});
});
});
最后还要把resurce 限制给加上
export default defineConfig({
manifest: {
web_accessible_resources: [
{
// We'll use this matches in the content script as well
matches: ['*://*/*'],
// Use the same path as `relativeDest` from the WXT module
resources: ['/image_bg.wasm'],
},
],
},
vite: () => ({
plugins: [react(), wasm(), topLevelAwait()],
}),
});
其他非rust编译的wasm库
这些库我没有尝试过,但方法估计也差不多,欢迎评论区留言!