WebAssembly初尝试

前言

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

简介

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

开始尝试

准备工作

开始

  • 使用上面安装好的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初尝试

小结

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

原文链接:https://www.cnblogs.com/aloneMing/p/17318823.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:WebAssembly初尝试 - Python技术站

(0)
上一篇 2023年4月17日
下一篇 2023年4月17日

相关文章

  • JS表单数据验证的正则表达式(常用)

    让我来为您详细讲解一下JS表单数据验证的正则表达式的常用方法: 一、正则表达式简介 正则表达式(Regular Expression)是一种文本模式,用来匹配、查找和替换文本中的字符,本身并不是JavaScript专有的东西。正则表达式通常被用来检查输入的字符串是否符合指定的格式,例如验证Email、密码和手机号等。 二、JS中的正则表达式 JavaScri…

    JavaScript 2023年6月10日
    00
  • js实现浏览器倒计时跳转页面效果

    实现浏览器倒计时跳转页面效果,需要以下步骤: 第一步:编写HTML页面 首先,在HTML文件中,需要添加一个倒计时元素,并设定其id和初始时间。例如: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"&g…

    JavaScript 2023年6月11日
    00
  • js前端如何写一个精确的倒计时代码

    下面我将为你详细讲解JS前端如何编写一个精确的倒计时代码。 如何编写JS倒计时 步骤一:取值 我们需要先取得倒计时的结束时间,以便计算剩余时间。可以通过以下代码获取当前时间: const now = new Date().getTime(); 然后,可以通过设定一个结束时间(例如:2022年1月1日),并将其转换为时间戳: const end = new D…

    JavaScript 2023年6月11日
    00
  • JavaScript中 ES6变量的结构赋值

    下面是关于“JavaScript中 ES6变量的结构赋值”的完整攻略。 什么是ES6变量的结构赋值 ES6中引入了一种新的变量赋值方式,叫做“结构赋值”(Destructuring Assignment)。结构赋值可以让我们方便地从数组和对象中提取值,然后赋值给变量。 数组结构赋值 数组结构赋值是指对于数组中的每个元素,通过相应位置上的变量名进行访问和取值赋…

    JavaScript 2023年6月10日
    00
  • JS小数转换为整数的方法分析

    下面是详细讲解“JS小数转换为整数的方法分析”的攻略: 问题背景 在JavaScript中,我们有时需要将小数转换成整数。例如,将0.3转换成3,或者将0.6转换成6。本文将介绍几种方法来实现这种转换。 方法一:乘法转换 此方法很简单,只需将小数乘以10的n次幂,其中n是小数点右侧的位数。然后将乘积四舍五入取整。这样就可以得到整数值。 function to…

    JavaScript 2023年5月28日
    00
  • js实现DOM走马灯特效的方法

    来分享一下实现JS DOM走马灯特效的方法攻略。 1. 前置技能 在实现JS DOM走马灯特效之前,你需要具备以下技能: HTML基础知识 CSS基础知识 JavaScript基础知识 掌握DOM操作基础方法 2. 实现过程 步骤一:HTML骨架搭建 首先,我们需要在HTML中创建容器来承载图片,为了实现走马灯效果,我们需要在容器中创建两个相同的图片列表,并…

    JavaScript 2023年6月10日
    00
  • JavaScript中setMonth()方法的使用详解

    下面是关于“JavaScript中setMonth()方法的使用详解”的完整攻略。 什么是setMonth()方法? setMonth()方法是JavaScript中Date对象原型上的一个方法,用于设置Date对象的月份。它的用法如下: dateObj.setMonth(month[, date]); 其中,month是一个0到11的整数,对应1月到12月…

    JavaScript 2023年6月10日
    00
  • JavaScript高级程序设计 事件学习笔记

    《JavaScript高级程序设计》事件学习笔记 什么是事件? 事件是指在网页中发生的交互的行为,比如页面的加载、点击按钮、鼠标滚动等,这些行为就是事件。 事件监听器 事件监听器是用来监听特定事件并处理事件的函数。可以通过给元素添加事件监听器来创建响应用户操作的交互式网页。 在 JavaScript 中,我们可以使用 addEventListener() 方…

    JavaScript 2023年5月27日
    00
合作推广
合作推广
分享本页
返回顶部