WebAssembly初尝试

这篇具有很好参考价值的文章主要介绍了WebAssembly初尝试。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

  • 之前老是听别人提到WebAssembly这个词,一直对其比较模糊,不能理解是个啥东西,后来自己实践了一下,发现其实就是一种提高代码性能的手段。

简介

  • WebAssembly 是一种运行在现代网络浏览器中的新型代码,并且提供新的性能特性和效果。它设计的目的不是为了手写代码而是为诸如 C、C++和 Rust 等低级源语言提供一个高效的编译目标。(解释来自MDN)
  • 通俗一点来讲,就是利用一些C、C++、Rust等偏底层的一些语言去实现部分功能并编译成二进制文件然后暴露给第三方平台(可能是浏览器端,也可能是服务端,还有可能是客户端),可以丰富和优化相关的应用。
  • 当然了,其实也有类似asm.js这种非底层语言去实现,但是它编译后的二进制文件减少了编译的过程,其实也是可以提高代码的性能。

开始尝试

准备工作

  • 我准备使用rust来演示一下,所以需要用到的第三方环境如下
    1. 安装Rustcurl https://sh.rustup.rs -sSf | sh -s -- --help。windows上可以下载https://static.rust-lang.org/rustup/dist/i686-pc-windows-gnu/rustup-init.exe。安装成功后可以在命令行输入rustc -V以及cargo -V来看看是否安装成功,cargoRust一个包管理器,类似npm之于nodejs
    2. 安装wasm-pack,参照官网https://rustwasm.github.io/wasm-pack/installer/。
    3. Rust基础语法,可以参考https://course.rs/about-book.html这位大佬编写的书,不枯燥又有趣,比官方的中文文档可读性更强。

开始

  • 使用上面安装好的wasm-pack新建一个空项目,wasm-pack new test-webassembly
  • 查看目录结构

    test-webassembly

    src

    lib.rc // 主入口
    utils.rs // 依赖的工具方法

    tests

    web.rs // 测试文件

    .appveyor.yml // 项目的前置依赖配置

    .gitignore // git忽略的文件配置

    .travis.yml // 该项目的CI环境配置文件

    cargo.toml // 该项目的配置文件,类似package.json

    README.md // 解释文件

重点文件分析

  • lib.rs,默认自带的这个文件只实现了一个greet方法,并暴露给了浏览器端,里面调用了alert方法。
    use wasm_bindgen::prelude::*; // 引入wasm_bindgen::prelude下所有的类和方法
    
    #[cfg(feature = "wee_alloc")]
    #[global_allocator]
    static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; // 一般不需要用,可以选择注释
    
    #[wasm_bindgen]
    extern {
        fn alert(s: &str); // 表示第三方环境可以直接用的方法定义,编译的时候会直接去调用第三方环境中的同名方法
    }
    
    #[wasm_bindgen] // 类似ts中的装饰器,打上这个标签,表示是需要与第三方环境去交互的,通俗一点,就是这个地方会被打包到第三方环境中。
    pub fn greet() {
        alert("Hello, test-webassembly!");
    }
    
  • Cargo.toml,这个文件主要就是一些项目配置信息了。
    [package]
    name = "test-webassembly" // 包名
    version = "0.1.0" // 包版本
    authors = ["xxx"] // 作者名
    edition = "2018" // rust版本
    
    [lib] // target设置,类似webpack种的target,将其打包成什么样的包
    crate-type = ["cdylib", "rlib"] // 分别代表动态系统库和rust静态库
    
    [features] // 用来条件编译
    default = ["console_error_panic_hook"] // 这个可以删掉不需要
    
    [dependencies]
    wasm-bindgen = "0.2.63" // 这个就是主要的处理rust代码转化成webassembly的包
    
    # The `console_error_panic_hook` crate provides better debugging of panics by
    # logging them with `console.error`. This is great for development, but requires
    # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
    # code size when deploying.
    # console_error_panic_hook = { version = "0.1.6", optional = true } 这个其实如果不是特别需要也可以不用
    
    # `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
    # compared to the default allocator's ~10K. It is slower than the default
    # allocator, however.
    # wee_alloc = { version = "0.4.5", optional = true } 这个一般用不上,注释掉就可以
    
    [dev-dependencies]
    wasm-bindgen-test = "0.3.13" // 用来测试的包,自己写一般可以不用
    
    [profile.release]
    # Tell `rustc` to optimize for small code size.
    opt-level = "s"
    

改造

接下来实现一个简单的图片处理工具,帮助web端更快的处理图片内容。

  1. 会用到image这个rust包。所以先改造它的Cargo.toml
[dependencies]
wasm-bindgen = "0.2.63"
// 在之前的dependencies下面添加image包
image = "0.24.6"
  1. 就以处理图片的其中内置的一个灰度方法来看一下。接下来改造lib.rs文件。
// 新增一个灰度方法,接受一个字节类型的集合,返回一个新的字节类型的集合
#[wasm_bindgen]
pub fn gray(_array: &mut [u8]) -> Vec<u8> {
    let mut img = image::load_from_memory(_array).unwrap(); // 将传入的u8集合转化成一个image对象
    img = img.grayscale(); // 调用第三方包的灰度方法得到新的图片
    let mut bytes: Vec<u8> = Vec::new(); // 定义一个新的u8集合
    img.write_to(&mut Cursor::new(&mut bytes), image::ImageOutputFormat::Png).unwrap(); // 将变换后的image转化成新的u8集合
    bytes // 将u8集合作为返回值抛出
}
  1. 运行命令wasm-pack build --release --target web,将rust文件打包成目标为web项目的可用二进制文件以及加载的js文件。可以看到项目多了一个pkg文件夹

    pkg

    .gitignore
    package.json
    README.md
    test_webassembly_bg.wasm // 二进制文件
    test_webassembly_bg.wasm.d.ts
    test_webassembly.d.ts
    test_webassembly.js // 这个就是加载这个二进制的文件

// test_webassembly.js这个文件主要看几个重点地方,其实不看也行,这个地方wasm-pack已经帮你处理好了,基本只需要学会用就行
// 加载二进制后的处理,返回暴露出的模块的实例
async function load(module, imports) {
    if (typeof Response === 'function' && module instanceof Response) {
      // 在支持instantiateStreaming的情况下,优先使用instantiateStreaming加载对应的二进制文件
        if (typeof WebAssembly.instantiateStreaming === 'function') {
            try {
                return await WebAssembly.instantiateStreaming(module, imports); 
            } catch (e) {
                if (module.headers.get('Content-Type') != 'application/wasm') {
                    console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
                } else {
                    throw e;
                }
            }
        }

        const bytes = await module.arrayBuffer();
        return await WebAssembly.instantiate(bytes, imports);

    } else {
      // 不支持的时候,就使用instantiate文件来加载
        const instance = await WebAssembly.instantiate(module, imports);

        if (instance instanceof WebAssembly.Instance) {
            return { instance, module };

        } else {
            return instance;
        }
    }
}
// 针对已经解析完的实例,做一些初始化操作,抛出模块实例的exports
function finalizeInit(instance, module) {
    wasm = instance.exports;
    init.__wbindgen_wasm_module = module;
    cachedInt32Memory0 = null;
    cachedUint8Memory0 = null;


    return wasm;
}
// 文件暴露的入口
async function init(input) {
    if (typeof input === 'undefined') {
        input = new URL('test_webassembly_bg.wasm', import.meta.url); // 默认的二进制文件地址
    }
    const imports = getImports();

    if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
        input = fetch(input); // 加载对应的二进制文件
    }

    initMemory(imports); // 这个就是之前上面申请空间的,可以注释掉的,不用看

    const { instance, module } = await load(await input, imports); // 调用webassembly的api去加载二进制文件

    return finalizeInit(instance, module); // 得到模块中抛出的exports
}

在html中使用,新建一个index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <input type="file" multiple="false" id="file">

    <div id="box"></div>
    <div>
        <button id="gray-btn">灰度</button>
    </div>
    <div id="target"></div>

    <script type="module">
        import init, { gray } from './pkg/test_webassembly.js';

        await init();

        const file = document.getElementById('file');
        const grayBtn = document.getElementById('gray-btn');
        let current = null;
        let uint8Array = [];
        file.addEventListener('change', (e) => {
            current = e.target.files[0];
            var reader = new FileReader(); 
            reader.readAsDataURL(current);
            reader.onload = function() {
                const img = new Image();
                img.src = this.result;
                document.querySelector('#box').appendChild(img);
            }
        });
        grayBtn.addEventListener('click', () => {
            var reader = new FileReader();
            reader.readAsArrayBuffer(current);
            reader.onload = function () {
                uint8Array = new Uint8Array(this.result);
                let newUnit8Array = gray(uint8Array); // 调用webassembly提供的灰度方法获取到灰度之后的结果

                const img = new Image();
                let blob = new Blob([newUnit8Array], { type: 'image/png' });
                img.src = window.URL.createObjectURL(blob);
                document.querySelector('#target').appendChild(img);
            }
        })
    </script>
</body>
</html>

使用npx http-server --port 3000启动静态服务器,因为webassembly是不支持本地的文件的,下面就是最终效果,先选择一张图片,之后点击灰度就能快速得到一张灰度的图片

WebAssembly初尝试文章来源地址https://www.toymoban.com/news/detail-413238.html

小结

  • WebAssembly是一个很有意思的技术方向,它为web应用提供了很多可能,图片处理,视频解析、加密等等一些复杂的场景都可以变得更轻便化。

到了这里,关于WebAssembly初尝试的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • element ui 的滚动条,Element UI 文档中没有被提到的滚动条

    element ui 的滚动条,Element UI 文档中被提到的滚动条 Element UI 官网中有用到自定义的滚动条组件,但是发布的所有版本中都不曾提及,个中原因我们不得而知,不过我们还是可以拿过来引用到自己的项目中。  使用的时候, 放在 el-scrollbar/el-scrollbar 标签内即可 如: 发现底部出

    2024年02月05日
    浏览(42)
  • 电脑开机为什么老是要两次?

    一、如果是新购的机器 有的人是新买没几天的机器,因为开始diY的时候忙着测试机器性能分数,装操作系统什么的,电脑老开着烤机,没发现这个问题。 等到一切安定下来后,才发现开机的故障。 这种问题,多半是你的电源跟主板或是显卡有冲突,或是供电不足造成的。

    2024年02月09日
    浏览(70)
  • 米贸搜|Facebook 老是封主页,怎么回事?

    十几二十个主页,申诉中了一个多月了”、“前天刚申诉回来,今天又给封了”,近期跨境群里多了更多类似“麻了”的发言。 就主页【被封】+【受限】问题频发,小豹整理以下相关预防及解除办法的资料,需要的赶紧收藏吧~ 一、近期主页被封原因 1.【买号】行为被识别

    2024年02月20日
    浏览(37)
  • 除了上述提到的应用场景,分布式系统在云计算中还有如下一些应用场景

    除了上述提到的应用场景,分布式系统在云计算中还有如下一些应用场景: 大规模视频和图像存储:分布式云存储可以为企业提供大规模视频和图像存储的解决方案,帮助企业存储和管理海量的视频和图像数据,提高数据的可靠性和安全性。 机器学习:在机器学习中,需要

    2024年01月19日
    浏览(49)
  • 电脑屏幕老是会无视频输入怎么办

    台式机开机后显示器显示无信号的原因和解决方法如下: 第一个原因:有可能台式主机和显示器的连接视频线接触不良(特别是接口处没有插好或者松动),还有可能这根连接的数据线出现问题,所以才会出现没有信号输入到屏幕,无显示,黑屏,处理方法:只要重新拨插一下

    2024年02月15日
    浏览(43)
  • scanf老是出错?带你详细解决输入缓冲区问题

    我们一般在进行 输入输出的时候,就会用到 scanf / printf 。 并且根据格式指定可以输入输出各种类型的数据 。可以 输入整形,字符,浮点型等其他类型的数据。 今天呢我先给大家再介绍一下 getchar 和 putchar. getchar呢是读取一个字符,并且只能读取一个字符。putchar呢则是输出

    2024年02月05日
    浏览(35)
  • Visual Studio Code将中文写入变量时,中文老是乱码问题

    对于这个问题,我也是弄了很久才知道,编码格式的问题 在此之前我们要先下载个插件 照这以上步骤,最后按F6运行即可,按F6是利用我们刚刚下载的插件进行编译,唯一有一点不好就是,用这种插件运行的话,打的断点是不起作用的。

    2024年02月05日
    浏览(49)
  • 移动网络玩外服游戏老是掉线?教你解决NAT类型严格的问题

    如果你的nat类型很严格的话,玩外服游戏经常掉线,因为nat类型越严格,能跟你匹配的玩家就会越少,像gta这种玩家之间的通信为主,服务器之间只是间歇式同步数据的游戏,你会非常的容易掉线。而且有些游戏为了降低服务器的压力,可能会踢出一些NAT严格类型的玩家,导

    2024年02月11日
    浏览(279)
  • 小米路由器频繁掉线的原因是什么?小米路由器老是掉线的解决办法介绍

    由于小米路由器内置1TB大硬盘,所有有很多用户都会选择使用小米路由器,那么,小米路由器频繁掉线是怎么回事?如何解决?针对此问题,本文就为打击介绍小米路由器老是掉线的原因及解决方法,希望本文可以帮助到大家。 小米路由器频繁断网掉线的原因以及解决办法汇

    2024年02月07日
    浏览(50)
  • 执行npm install时老是安装不成功node-sass的原因和解决方案

    相信你安装前端项目所需要的依赖包(npm install 或 yarn install)时,有可能会出现如下报错: 那么到底是什么原因导致这个问题出现呢? 最多的原因还是 node的版本 和node-sass的版本不一致,比如node版本是 16+的,而你的版本还是旧的 “node-sass”: “^4.14.1”。因为node并不会兼容

    2024年02月06日
    浏览(64)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包