wasm-pack 是一个便捷工具, 当使用rust编写Web Assembly并且支持浏览器(JS)调用的库的时候, 需要一系列的工序. 使用wasm-pack后步骤简化成:
编写 rust 库代码 → build → npm publish
具体详情不赘述, 查看: rustwasm.github.io/docs/wasm-p…
这个文章讨论落地实际项目会遇到的问题和解决方案:
问题1:
使用 scripts 标签载入:
运行build后, wasm-pack 会在项目生成 pkg 文件夹, 文件夹内容如下:
这时候可以选择把文件推送到远程cdn, 然后通过 script 标签加载, 在 html 文件中:
<script type="module">
import init, { hello } from "https://some-cdn.com/xxx/my_lib.js";
await init();
hello();
</script>
然而如果需要在业务模块中调用, 这种方式就有缺陷, 一个简单的解决办法是, 存在global对象中, 供需要的时候调用:
<script type="module">
import init, { hello } from "https://some-cdn.com/xxx/my_lib.js";
await init();
window.hello = hello;
</script>
这种方式污染全局变量, 不能按需加载, 并且升级版本也不便.
问题2:
查看 pkg 文件夹中 package.json , 这个内容已经满足发布npm包的条件了:
然而, 如果我们需要部署到私域 npm 仓库是不行的, 我们需要在 name 字段加入私域名前缀, 比如私域前缀是 @abc ;
相关 issue: github.com/rustwasm/wa…
在项目新建 build.sh
文件
#!/bin/bash
# 运行wasm-pack build命令
wasm-pack build --target web --release
# 在生成的pkg/package.json文件的name字段之前添加"@abc"字符串
sed -i 's/"name": "\(.*\)"/"name": "@abc\1"/' pkg/package.json
如果需要支持windows 平台的, 需要创建 build.bat
文件
wasm-pack build --target web --release
@REM 在生成的pkg/package.json文件的name字段之前添加"@abc"字符串
powershell -Command "(Get-Content pkg/package.json) -replace '\"name\": \"(.*?)\"', '\"name\": \"@abc$1\"' | Set-Content pkg/package.json"
这时候每次构建时, 运行这个脚本
sh ./build.sh
问题3:
加载 wasm 文件跨域问题:
查看 wasm-pack 生成出来的文件:
如此看来, 是通过new URL加载 my_lib_bg.wasm 文件, 如果主站域名和静态资源的域名不一致, 导致不满足同源策略.
解决办法:
-
通过配置 nginx 支持跨域:
location ~* \.(js|eot|ttf|woff2?|otf|svg|svga|json|png|mp3|jpg|wav|tpl|css|js|html|mp4|wasm|asm|bin)$ {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
root /data/webapps/xxx.com/;
concat on;
concat_max_files 20;
}
-
通过webpack配置把wasm打包到JS文件中:
查看 __wbg_init 的实现:
-
如果 init 调用是是空, 就会默认加载 my_lib_bg.wasm 文件, 所以导致出现这个问题
-
如果 input 是字符串, 通过 fetch 请求字符串
-
否则 input 被当作 promise 调用
所以给这个方案提供了一个这种可能, 具体使用方法参考: webassembly - How can I make webpack embed my *.wasm for use in a web worker? - Stack Overflow
import init, { /* other stuff */ } from 'my-wasm-package';
import wasmData from 'my-wasm-package/my-wasm-package.wasm';
const wasmPromise = init(wasmData);