2021-05-06 WebAssembly 第一天

2021-05-06 WebAssembly

开发环境

操作系统

目前用的操作系统是windows。

Node

开发环境 。

先下载一个Nodejs。

传送门

当前版本: 14.16.1

[图片上传失败...(image-842b47-1620292429591)]

Rust

这个就不介绍怎么安装了,

我的版本是

[图片上传失败...(image-8913b1-1620292429591)]

教程参考

编译成WebAssembly 有两个教程。

官网 mozilla官方教程

还有另外一个是 ,不过这个用的是他们自己写的一个构建工具。

secondstate 出品的SSVM

还有一本书,建议深入的时候读。上面教程都是非常简单的。书比较系统

rustwasm

动手

我们先按官方教程撸一把。

在本教程中,我们将使用 Rust 的 npm 包构建工具 wasm-pack 来构建一个 npm 包。这个包只包含 WebAssembly 和 JavaScript 代码,以便包的用户无需安装 Rust 就能使用。他们甚至不需要知道这里包含 WebAssembly!

安装wasm-pack

要构建我们的包,我们需要一个额外工具 wasm-pack。它会帮助我们把我们的代码编译成 WebAssembly 并制造出正确的 npm 包。使用下面的命令可以下载并安装它:

$ cargo install wasm-pack

构建我们的 WebAssembly npm 包

万事俱备,来创建一个新的 Rust 包吧。打开你用来存放私人项目的目录,我的目录是 d:/workspace:

$ cargo new --lib hello-wasm

[图片上传失败...(image-eca9bb-1620292429591)]

这里会在名为 hello-wasm 的子目录里创建一个新的库,里面有下一步之前我们所需要的一切:

+-- Cargo.toml
+-- src
    +-- lib.rs

首先,我们有一个 Cargo.toml 文件,这是我们配置构建的方式。如果你用过 Bundler 的 Gemfile 或者 npm 的 package.json,你应该会感到很熟悉。Cargo 的用法和它们类似。

接下来,Cargo 在 src/lib.rs 生成了一些 Rust 代码:

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}

我们完全不需要使用这些测试代码,所以继续吧,我们删掉它。

修改Cargo.toml

接下来不太按教程的套路走了,如果还按教程的套路,那么不如直接自己看教程好了。

首先我们需要去修改 Cargo.toml 添加下列内容

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2"

第一个部分 — [lib] — 告诉 Rust 为我们的包建立一个 cdylib 版本;官方解释

  • --crate-type=cdylib, #[crate_type = "cdylib"] - A dynamic system library will be produced. This is used when compiling a dynamic library to be loaded from another language. This output type will create *.so files on Linux, *.dylib files on macOS, and *.dll files on Windows.

第二个部分是 [dependencies] 部分。在这里我们告诉 Cargo 我们需要依赖哪个版本的 wasm-bindgen

[图片上传失败...(image-156bc0-1620292429591)]

我们到crate.io 上看一下,最新版本还是0.2.73

写点RUST代码吧。

让我们在 src/lib.rs 写一些代码替换掉原来的:

extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern {
    pub fn alert(s: &str);
}

#[wasm_bindgen]
pub fn say(name: &str) {
    alert(&format!("Hello, {}!", name));
}

这就是我们这个 Rust 项目的内容。它有三个主要部分,让我们按顺序来讲。

使用 wasm-bindgen 在 Rust 与 JavaScript 之间通信

第一部分看起来像这样:

extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

第一行就像在说 “哇 Rust,我们在用一个叫做 wasm_bindgen 的库”。在 Rust 当中,库被称为 “crates”,因为我们使用的是一个外部库,所以有 "extern"。通过 extern crate + crate名称将包内的模块引入当前环境。

在使用 extern 引入包之后,可以用 use 将模块引入到当前作用域。

第三行包括了一个将库中的代码引入到你的代码中的使用命令。在这个情况下,将会引入 wasm_bindgen::prelude 的全部模块。

在我们开始下一节之前,我们将讲一讲 wasm-bindgen.

wasm-pack 使用 wasm-bindgen,其它工具,去提供一个连接 JavaScript 和 Rust 的桥。它允许 JavaScript 使用 string 调用 Rust API,或者调用一个 Rust function 去捕获 JavaScript 异常。

我们将在我们的包中使用 wasm-bindgen 的功能。事实上,这是下一节的内容!

在 Rust 中调用来自 JavaScript 的外部函数

接下来的部分看起来像这样:

#[wasm_bindgen]
extern {
    pub fn alert(s: &str);
}

#[] 中的内容叫做 "属性",并以某种方式改变下面的语句。在这种情况下,下面的语句是一个 extern,它将告诉 Rust 我们想调用一些外部定义的函数。这个属性告诉我们 "wasm-bindgen 知道如何找到这些函数"。

第三行是用 Rust 写的函数签名。它告诉我们 "alert 函数接受一个叫做 s 的字符串作为参数。"

你可能会疑惑这个函数是什么,你的疑惑可能是正确的:这是the alert function provided by JavaScript!

编写能够在JavaScript中调用的 Rust 函数

最后一部分是这样的:

#[wasm_bindgen]
pub fn say(name: &str) {
    alert(&format!("Hello, {}!", name));
}

我们又看到了 #[wasm_bindgen] 属性。在这里,它并非定义一个 extern 块,而是 fn,这代表我们希望能够在 JavaScript 中使用这个 Rust 函数。 这和 extern 正相反:我们并非引入函数,而是要把函数给外部世界使用。

这个函数的名字是 say,它需要一个参数,一个字符串 (写作 &str)。它调用了我们前面在 extern 块中引入的 alert 函数。它传递了一个让我们串联字符串的 format! 宏的调用。

在这里有两个参数,一个格式化字符串和一个要填入的变量。格式化字符串是 "Hello, {}!" 部分。它可以包含一个或多个 {},变量将会被填入其中。传递的变量是 name,也就是这个函数的参数。所以当我们调用 greet("Steve")时我们就能看到 "Hello, Steve!"。

这个传递到了 alert(),所以当我们调用这个函数时,我们应该能看到他谈弹出了一个带有 "Hello, Steve!" 的消息框。

我们的库写完了,是时候构建它了。

构建包

现在我们已经完成了所有配置项,开始构建吧!在命令行输入以下命令:

$ wasm-pack build 
  • wasm-pack build, 构建生成 pkg 文件夹;
  • wasm-pack test 用于测试

这个命令将做一系列事情 (这会花一些时间,特别是当你第一次运行 wasm-pack)。想了解详细情况,查看这篇在 Mozilla Hacks 上的文章。简单来说,wasm-pack build 将做以下几件事:

  1. 将你的 Rust 代码编译成 WebAssembly。
  2. 在编译好的 WebAssembly 代码基础上运行 wasm-bindgen,生成一个 JavaScript 文件将 WebAssembly 文件包装成一个模块以便 npm 能够识别它。
  3. 创建一个 pkg 文件夹并将 JavaScript 文件和生成的 WebAssembly 代码移到其中。
  4. 读取你的 Cargo.toml 并生成相应的 package.json。
  5. 复制你的 README.md (如果有的话) 到文件夹中。

[图片上传失败...(image-438fd-1620292429591)]

[图片上传失败...(image-29f44-1620292429591)]

                        .......

[图片上传失败...(image-359e3a-1620292429591)]

第一次编译非常非常多东西.....

最后的结果?你在 pkg 文件夹下有了一个 npm 包。

我们看看里面有什么东西

[图片上传失败...(image-4eb720-1620292429591)]

对代码体积的一些说明

如果你检查生成的 WebAssembly 文件体积,它可能有几百 kB。我们没有让 Rust 去压缩生成的代码,从而大大减少生成包的体积。这和本次教程主题无关,但如果你想了解更多,查看 Rust WebAssembly 工作组文档上关于 减少 .wasm 体积 的说明。真的有用的传送门

我们先看看现在编译出来的wasm有多大?

[图片上传失败...(image-522d02-1620292429591)]

然后我们按照教程上的

Cargo.toml里面进行修改

[profile.release]
lto = true

这为 LLVM 提供了更多内联和修剪功能的机会。它不仅会变小,而且还会使它在运行时间更快!缺点是编译需要更长的时间。

告诉LLVM优化尺寸而不是速度

LLVM 的优化通行证经过调整,以在默认情况下提高速度,而不是尺寸。我们可以通过将该部分修改为:

[profile.release]
lto = true
opt-level = 's'

[图片上传失败...(image-654412-1620292429591)]

有效果哦!!

或者,更积极地优化尺寸,进一步的潜在速度成本:

[profile.release]
lto = true
opt-level = 'z'

[图片上传失败...(image-b7c211-1620292429591)]

多了一点....

我们建立一个使用我们包的网站

让我们建立一个使用我们包的网站! 人们通过各种打包工具使用 npm包,在本教程中,我们将使用 webpack。 它比其他某些打包工具稍微复杂一点,但展示了更实际的用法。

让我们离开pkg目录,并创建一个新目录site,尝试以下操作:

创建一个新文件 package.json,然后输入如下代码:

{
  "scripts": {
    "serve": "webpack-dev-server"
  },
  "dependencies": {
    "@mynpmusername/hello-wasm": "^0.1.0"
  },
  "devDependencies": {
    "webpack": "^4.25.1",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.10"
  }
}

我们没有像教程那样上传到npm中。我们需要对 dependencies 进行修改一下。指向相对地址

  • dependencies: 表示生产环境下的依赖管理;无论开发还是生产都会下载使用的依赖
  • devDependencies: 表示开发环境下的依赖管理;
{
  "scripts": {
    "serve": "webpack-dev-server"
  },
  "dependencies": {
    "hello_wasm": "file:../pkg"
  },
  "devDependencies": {
    "webpack": "^4.25.1",
    "webpack-cli": "^3.1.2",
    "webpack-dev-server": "^3.1.10"
  }
}

接下来,我们需要配置Webpack。 创建 webpack.config.js 并输入:

const path = require('path');
module.exports = {
  entry: "./index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "index.js",
  },
  mode: "development"
};

现在我们需要一个HTML文件。 创建一个index.html并写入如下内容:



  
    
    hello-wasm example
  
  
    
  

最后,从HTML文件中引用index.js

const js = import("../pkg/hello_wasm.js");
js.then(js => {
  js.say("WebAssembly");
});

这将从pkg文件夹导入我们的模块。这不是最佳做法,但这里只做一个演示,因此暂时就这样用。 加载后,它将从该模块调用say函数,并传入字符串“WebAssembly”参数。注意这里看上去没有什么特别的,但是我们正在调用 Rust 代码! 就JavaScript代码所知,这只是一个普通模块。

我们已经完成了所有的文件! 让我们试一下:

$ npm install
$ npm run serve

[图片上传失败...(image-a4d2b-1620292429591)]

我们看一下。写的wasm加载到了前端了。

[图片上传失败...(image-1019c7-1620292429591)]

打完收工

你可能感兴趣的:(2021-05-06 WebAssembly 第一天)