美文网首页
2021-05-06 WebAssembly 第一天

2021-05-06 WebAssembly 第一天

作者: buddyCoder | 来源:发表于2021-05-06 17:14 被阅读0次

    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并写入如下内容:

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <title>hello-wasm example</title>
      </head>
      <body>
        <script src="./index.js"></script>
      </body>
    </html>
    

    最后,从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 第一天

          本文链接:https://www.haomeiwen.com/subject/cgpddltx.html