前言
WebAssembly 是未来的趋势
高效
WebAssembly 有一套完整的语义,实际上 wasm 是体积小且加载快的二进制格式, 其目标就是充分发挥硬件能力以达到原生执行效率
安全
WebAssembly 运行在一个沙箱化的执行环境中,甚至可以在现有的 JavaScript 虚拟机中实现。在web环境中,WebAssembly 将会严格遵守同源策略以及浏览器安全策略。
标准
WebAssembly 在 web 中被设计成无版本、特性可测试、向后兼容的。WebAssembly 可以被 JavaScript 调用,进入 JavaScript 上下文,也可以像 Web API 一样调用浏览器的功能。当然,WebAssembly 不仅可以运行在浏览器上,也可以运行在非web环境下。
WebAssembly支持多种语言
目前能编译成 WebAssembly 字节码的高级语言有:
AssemblyScript:语法和 TypeScript 一致,对前端来说学习成本低,为前端编写 WebAssembly 最佳选择;
c\c++:官方推荐的方式。
Rust:语法复杂、学习成本高,对前端来说可能会不适应。
Kotlin:语法和 Java、JS 相似,语言学习成本低。
Golang:语法简单学习成本低。但对 WebAssembly 的支持还处于未正式发布阶段。
开发环境搭建
安装WebAssembly for Rust 环境
安装Rust
移步到 www.rust-lang.org/tools/insta… 哦~
构建工具
想要把 Rust 编译成 WebAssembly 需要一个工具: wasm-pack 。使用 wasm-pack 可以一条命令转成 WebAssembly 。 使用下面命令下载和安装:
bash cargo install wasm-pack
安装 wasm-pack 有坑
Ubuntu上可能会缺少依赖
installed libssl-dev on ubuntu 18 and still get this error:
error: failed to run custom build command for openssl-sys v0.9.39
process didn't exit successfully: /home/forecast/cs453_finalproj_backend/target/release/build/openssl-sys-864259d0b1f702fc/build-script-main (exit code: 101)
可能需要安装:
sudo apt install pkg-config
sudo apt install libssl-dev
来写点 Rust
新建 Rust 项目,本文以斐波那契数列为例
cargo new --lib hello-wasm-fibonacci
创建成功后提示: Created libraryhello-wasm-fibonacci
package
项目结构:
.
├── Cargo.toml
└── src
└── lib.rs
1 directory, 2 files
配置Cargo.toml
[package]
name = "hello-wasm"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
description = "A sample project with wasm-pack"
license = "MIT/Apache-2.0"
repository = "https://github.com/yourgithubusername/hello-wasm"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
终于可以开始写 Rust 代码
(本文不展开 Rust 语法,专注 WebAssembly for Rust)
删除原来 lib.rs 的所有代码,在里面写想要的东西
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
//生成 n 阶斐波那契数列
fn fibonacci(n: u32) -> u32 {
if n == 0 {
return 0;
} else if n == 1 {
return 1;
} else {
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
#[wasm_bindgen]
pub fn get_fibonacci(n: u32) -> u32 {
return fibonacci(n);
}
小问号你是否有很多朋友?
- 在 Rust 当中,库被称为 “crates”,因为我们使用的是一个外部库,所以有 "extern"
use xxx ::nnn::*;
是将库中的代码引入到你的代码中的使用命令。在这个情况下,将会引入 xxx::nnn 的全部模块- 在 #[] 中的内容叫做 "属性",并以某种方式改变下面的语句,类似于Typescript或者Java里面的注解。这里使用#[wasm_bindgen]属性,就可以把 Rust 函数开放给 JavaScript 使用。
打包发布到 npm
使用 wasm-pack 构建 WebAssembly
wasm-pack build --scope mynpmusername
登录 npm (默认已安装 npm )
npm adduser
Username: yournpmusername
Password:
Email: (this IS public) you@example.com
进入 WebAssembly 文件目录 pkg
cd pkg
由于 wasm-pack 生成的 package.js 漏了_bg.js ,需要修改package.json补充
{
"name": "@mynpmusername/hello-wasm-fibonacci",
"collaborators": [
"yourgitname <yourgitmail>"
],
"version": "0.1.0",
"files": [
"hello_wasm_fibonacci_bg.wasm",
"hello_wasm_fibonacci.js",
"hello_wasm_fibonacci_bg.js",
"hello_wasm_fibonacci.d.ts"
],
"module": "hello_wasm_fibonacci.js",
"types": "hello_wasm_fibonacci.d.ts",
"sideEffects": false
}
发布到npm
npm publish --access=public
在前端项目中使用
新建前端项目
mkdir web
新建 package.json
{
"scripts": {
"serve": "webpack-dev-server"
},
"dependencies": {
"@mynpmusername/hello-wasm-fibonacci": "^0.1.0"
},
"devDependencies": {
"webpack": "^4.25.1",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.10"
}
}
新建webpack.config.js
const path = require('path');
module.exports = {
entry: "./index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "index.js",
},
mode: "development"
};
index.htm
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>hello-wasm example</title>
</head>
<body>
<script src="./index.js"></script>
</body>
</html>
index.js
const fibonacci = import("./node_modules/@yournpmusername/hello-wasm-fibonacci/hello_wasm_fibonacci.js");
fibonacci.then(fibonacci => {
const a = fibonacci.get_fibonacci(10)
console.log(a)
});
AssemblyScript
typescript 编写 wasm
- 官方地址
- 实现方法
- yarn init -y
- yarn add @assemblyscript/loader
- yarn add assemblyscript -D
- npx asinit .
- yarn
- yarn asbuild or yarn asbuild:watch
{
"scripts":{
"asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --validate --sourceMap --debug",
"asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat --validate --sourceMap --optimize",
"asbuild": "npm run asbuild:untouched && npm run asbuild:optimized",
"asbuild:watch": "onchange -i 'assembly/**/*' -- npm run asbuild" 需要用 onchange库实现开发模式
}
}
tsconfig.json 配置
{
"extends": "../node_modules/assemblyscript/std/assembly.json",
"include": [
"./**/*.ts"
]
}
开发方式类似于 ts
// The entry file of your WebAssembly module.
export function add(a: i32, b: i32): i32 {
return a + b;
}
export function isPrime(x: u32): bool {
if (x < 2) {
return false;
}
for (let i: u32 = 2; i < x; i++) {
if (x % i === 0) {
return false;
}
}
return true;
}
export function fabonacci(n:number):number{
return n < 2 ? n : fabonacci(n - 1) + fabonacci(n - 2);
}
总结
- 经过比较 TypeScript 生成的 wasm 速度比 Rust 快, Rust 打包出来的 wasm 比 TypeScrip 要小很多倍 (下次再详细写)
- Rust 语言还需要学习
- wasm 性能比 JS 要强很多, 可以应用在高性能需求场景。
- wasm 目前打包工作流完善
相关Demo
如果懒得配环境,又想试试 WebAssembly
可以直接用 webassembly.studio/
作者
Benny Shi |
Ken.Xu |