如何将wasm文件正确打包到Angular自定义元素bundle中
问题根因
几个异常现象分别对应不同的错误来源:
- wasm跨域加载路径错误:Webpack默认基于当前页面的访问路径解析静态资源(包括wasm)的请求地址,而非基于引入的js bundle自身的托管域名,跨域引入脚本时就会默认从当前页面域名下查找wasm资源,导致404。
- 二次打包dist产物无效:先执行完Angular构建、再拿dist目录下的产物作为入口做二次打包时,wasm的导入逻辑已经被Angular内置的构建工具转换成了固定路径的运行时fetch请求,后置的wasm-loader根本无法捕获到原始的wasm导入语句,自然不会处理wasm文件。
NOT SUPPORTED: keyword "id"报错:这是自定义webpack构建器版本和Angular 14不兼容导致的。Angular 14内置使用ajv v8做schema校验,如果你安装的@angular-builders/custom-webpack大版本不是14,就会沿用旧版ajv v6的schema规则,用id而非$id作为schema主键,直接触发校验报错。- wasm-loader配置无效:Angular 14底层使用webpack 5,已经原生支持wasm资源处理,额外安装第三方wasm-loader反而会和原生逻辑冲突,导致wasm加载逻辑失效。
解决方案
根据公共库跨域分发的场景,优先推荐方案1,部署成本最低。
方案1:wasm内联进单bundle(无额外资源依赖)
这个方案会把wasm转换成二进制数据直接内联到js bundle中,不需要单独托管wasm文件,也不会存在路径解析问题,跨域引入直接可用。
- 安装版本匹配的构建依赖,从根源解决schema报错:
npm i -D @angular-builders/custom-webpack@14
注意:必须指定大版本为14,和你本地的Angular核心版本保持一致,不需要额外安装wasm-loader。
- 在项目根目录新建webpack配置文件
webpack.wasm-config.js,写入以下配置:
module.exports = { experiments: { asyncWebAssembly: true, syncWebAssembly: true }, module: { rules: [ { test: /\.wasm$/, type: 'asset/inline' } ] } };
- 修改
angular.json中自定义元素对应的构建配置,替换默认构建器为自定义webpack构建器:
找到对应项目的architect.build节点,做两处修改:
- 把
builder字段的原值@angular-devkit/build-angular:browser替换为@angular-builders/custom-webpack:browser - 在
options节点下新增自定义webpack配置路径:
"customWebpackConfig": { "path": "./webpack.wasm-config.js" }
其余原有配置(输出路径、入口文件、ts配置等)全部保留不变。
4. 执行Angular Elements构建时加上单bundle参数,最终只会输出一个js文件:
ng build --output-hashing none --single-bundle true
构建完成后wasm会被直接内联到输出的js bundle中,托管到任意服务器后,第三方页面跨域引入时不会再发起额外的wasm请求,直接可以运行。
方案2:固定wasm请求路径(适合wasm体积较大的场景)
如果wasm文件体积过大不适合内联,可以不把wasm打进bundle,而是在代码层面固定wasm的请求地址为js bundle自身的托管域名:
- 找到项目中初始化wasm模块的代码,不要用相对路径直接引用wasm,而是先获取当前执行的js脚本的源地址,再拼接wasm路径:
// 获取当前自定义元素脚本自身的加载地址 const currentScript = document.currentScript as HTMLScriptElement; const scriptBase = new URL(currentScript.src).origin; // 初始化wasm时传入路径解析规则,以Emscripten/wasm-pack生成的胶水代码为例 const wasmInstance = await initWasmModule({ locateFile: (fileName: string) => `${scriptBase}/${fileName}` });
- 正常执行Angular构建,把构建输出的js文件和wasm文件一起上传到你的托管服务器即可。无论第三方页面在哪个域名引入你的js bundle,wasm都会固定从你的托管服务器请求,不会出现路径解析错误。
之前尝试的误区说明
- 不要对Angular已经构建完成的dist产物做二次webpack打包:构建完成后的代码已经丢失了原始的静态资源导入依赖关系,webpack无法识别已经被转换成运行时fetch的wasm请求,打包不会生效。
- 不要在webpack 5环境下额外安装wasm-loader:webpack 5已经原生支持wasm的加载、解析、打包,第三方loader会和原生逻辑冲突,导致加载异常。
- 使用自定义webpack扩展Angular构建时,
@angular-builders/custom-webpack的大版本必须和Angular版本严格一致,否则会出现各类schema校验、构建流程不兼容的报错。
内容的提问来源于stack exchange,提问作者grim_i_am
相关产品推荐
相关产品推荐

