WebAssembly初嘗試

来源:https://www.cnblogs.com/aloneMing/archive/2023/04/14/17318823.html
-Advertisement-
Play Games

<!-- 封裝的模板下載和導入按鈕和功能組件--> <template> <span style="margin-left: 10px"> <el-button size="mini" class="el-icon-download" @click="downFiles"> 下載模板</el-but ...


前言

  • 之前老是聽別人提到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是一個很有意思的技術方向,它為web應用提供了很多可能,圖片處理,視頻解析、加密等等一些複雜的場景都可以變得更輕便化。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 準備工作 三台虛擬機,關閉防火牆,關閉selinux 查看防火狀態 systemctl status firewalld 暫時關閉防火牆 systemctl stop firewalld 永久關閉防火牆 systemctl disable firewalld 查看 selinux狀態 getenfo ...
  • 摘要:在金融創新壓力下,傳統集中式資料庫的短板逐漸凸顯出來,唯有加速核心系統的升級和轉型,將應用遷移到更具有可持續演進支撐能力的資料庫上,才能解決根本問題。 本文分享自華為雲社區《全面數字化時代,國有大型銀行如何走好金融創新之路?》,作者:GaussDB 資料庫。 近些年來,金融行業數字化轉型不斷推 ...
  • GreatSQL社區原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。 GreatSQL是MySQL的國產分支版本,使用上與MySQL一致。 作者: Yejinrong/葉金榮 文章來源:GreatSQL社區投稿 背景介紹 編譯環境 編譯前準備工作 編譯GreatSQL 初始化並啟動Great ...
  • 本文由葡萄城技術團隊於博客園原創並首發轉載請註明出處:葡萄城官網,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。 項目想做數據可視化,想同時在PC端、手機端查看數據怎麼辦?業務主要關心的數據包括:銷售數據、業績達成、同比、環比,各產品銷售情況及潛客商機、未來收入預測等數據,最好附加人 ...
  • 新媒體時代,廣告樣式越來越豐富。相較於傳統的圖文信息,視頻類廣告更具有直觀性,能夠讓消費者在瞭解產品知識和功能的同時加深對產品的印象。 因此在各類網站或App上投放視頻類廣告是個很好的宣傳方式,但廣告商們如果想在網站上展示視頻廣告,必須確保視頻廣告投放協議與發佈渠道的播放器相容;如果不能相容,廣告商 ...
  • “我苦心鍛煉了三年,我變禿了,也變強了。” —— 琦玉老師 0x00 大綱 0x01 前言 四個月前,我在《你是來找茬的吧?對自己的博客進行調優》一文中探討了以博客的使用者而不是開發者身份去進行優化,究竟能做到何種程度的問題。當時以 Edge 瀏覽器的開發者工具里的 lighthouse 評分和載入 ...
  • 有一朋友想把網頁內容變成PDF下載下來。問我有沒有好辦法。 這還真巧了,咱公司也有這個需求,就是網頁生成合同,然後可以直接列印合同內容。最早吧,就是可以直接列印就好了。 當時為解決完美列印的問題,挺費勁的,當時第三方插件還有BUG(當然把解決放給發給作者了,作者早已經修複了),正經反覆折騰了好一陣子 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 Vue.js是一個基於組件化和響應式數據流的前端框架。當我們在Vue中編寫模板代碼時,它會被Vue編譯器處理並轉換為可被瀏覽器解析的JavaScript代碼。Vue中的模板實際上是HTML標記和Vue指令的組合,它們會被Vue編譯器處理並 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...