1. 使用 Rust 編寫更快的 React 組件

        共 6686字,需瀏覽 14分鐘

         ·

        2021-12-01 22:14

        大家好,我是 ConardLi,上周發(fā)了一篇 Wasm 的文章,主要分析的是今年 Google 開發(fā)者大會上的 Wasm 主題:

        Wasm 為 Web 開發(fā)帶來無限可能

        其實主要還是我個人對 Rust 比較感興趣,在今天的文章中,我將帶大家完成一個將 Rust 實際應(yīng)用到 React 項目中的小 Demo。

        Wasm

        在開始之前,我們還是先來回顧下 Wasm:

        WebAssembly 是一種二進(jìn)制指令格式,簡稱為 Wasm,它可以運行在適用于堆棧的虛擬機(jī)上。

        WebAssembly 存在的意義就是成為編程語言的可移植編譯目標(biāo),讓在 Web 上部署客戶端和服務(wù)端應(yīng)用成為可能。

        Wasm 具有緊湊的二進(jìn)制格式,可為我們提供近乎原生的網(wǎng)絡(luò)性能。隨著它變得越來越流行,許多語言都編寫了編譯成 Web 程序集的綁定工具。

        為什么是 Rust

        Rust 是一個快速、可靠二期又節(jié)約內(nèi)存的編程語言。在過去六年的 stackoverflow 的最受喜愛的編程語言中,它一直蟬聯(lián)榜首的位置,主要還是這個語言本身擁有眾多的優(yōu)點,比如:

        • 內(nèi)存安全
        • 類型安全
        • 消除數(shù)據(jù)競爭
        • 使用前編譯
        • 建立(并且鼓勵)在零抽象之上
        • 最小的運行時(無停止世界的垃圾搜集器,無 JIT 編譯器,無 VM
        • 低內(nèi)存占用(程序可以運行在資源受限的環(huán)境,比如小的微控制器)
        • 針對裸機(jī)(比如,寫一個 OS 內(nèi)核或者設(shè)備驅(qū)動,把 Rust 當(dāng)一個 ‘高層’匯編器使用)”

        另外,RustWebAssembly 領(lǐng)域的貢獻(xiàn)非常大的,使用 Rust 編寫 WebAssembly 非常簡單。

        但是,Rust 存在的目的不是為了替代 JavaScript 而是和他形成互補(bǔ),因為 Rust 語言的學(xué)習(xí)曲線是非常陡峭的,用它去完全替代 Web 開發(fā)幾乎是不可能的。

        所以,我們一般會在 Web 開發(fā)的工具鏈,或者前端頁面中一些非常大量的數(shù)據(jù)計算中的操作用到它。

        前置知識

        在開始開發(fā)之前,你需要了解一些前置知識,React 相關(guān)的就不多說了,我們來看看 Rust 相關(guān)的幾個重要概念。

        cargo

        cargorust 的代碼組織和包管理工具,你可以將它類比為 node.js 中的 npm

        cargo 提供了一系列強(qiáng)大的功能,從項目的建立、構(gòu)建到測試、運行直至部署,為 rust 項目的管理提供盡可能完整的手段。同時,它也與 rust 語言及其編譯器 rustc 本身的各種特性緊密結(jié)合。

        rustup

        rustupRust 的安裝和工具鏈管理工具,并且官網(wǎng)推薦使用 rustup 安裝 Rust。

        rustuprustc(rust編譯器) 和 cargo 等工具安裝在 Cargobin 目錄,但這些工具只是 Rust 工具鏈中組件的代理,真正工作的是工具鏈中的組件。通過 rustup 的命令可以指定使用不同版本的工具鏈。

        wasm-bindgen

        wasm-bindgen 提供了 JSRust 類型之間的橋梁,它允許 JS 使用字符串調(diào)用 Rust API,或者使用 Rust 函數(shù)來捕獲 JS 異常。

        wasm-bindgen 的核心是促進(jìn) javascriptRust 之間使用 wasm 進(jìn)行通信。它允許開發(fā)者直接使用 Rust 的結(jié)構(gòu)體、javascript的類、字符串等類型,而不僅僅是 wasm 支持的整數(shù)或浮點數(shù)類型。

        wasm-pack

        wasm-packRust / Wasm 工作組開發(fā)維護(hù),是現(xiàn)在最為活躍的 WebAssembly 應(yīng)用開發(fā)工具。

        wasm-pack 支持將代碼打包成 npm 模塊,并且附帶 Webpack 插件(wasm-pack-plugin),借助它,我們可以輕松的將 Rust 與已有的 JavaScript 應(yīng)用結(jié)合。

        wasm32-unknown-unknown

        通過 rustuptarget 命令可以指定編譯的目標(biāo)平臺,也就是編譯后的程序在哪種操作系統(tǒng)上運行。

        wasm-pack 使用 wasm32-unknown-unknown 目標(biāo)編譯代碼。

        好了,了解了 Rust 相關(guān)的一些知識,我們一起來完成這個 Demo 吧。

        一起來做個 Demo

        在開始之前,要確保你的電腦上已經(jīng)安裝了 NodeRust,可以在命令行分別輸入 npm、rustup 看看能否找到命令,如果沒安裝的話自己先安裝一下。

        初始化一個簡單 React 程序

        首先,我們來初始化一個 React 項目,命令行執(zhí)行 npm init

        然后,我們安裝一些開發(fā)項目必備的包:

        $?npm?i?react?react-dom
        $?npm?i?-D?webpack?webpack-cli?webpack-dev-server?html-webpack-plugin?
        $?npm?i?-D?babel-core?babel-loader?@babel/preset-env?@babel/preset-react

        然后,我們在項目中創(chuàng)建一些常用的文件夾:srcpage、publicbuild、和 dist

        我們在 page 文件夾中創(chuàng)建一個 index.jsx,編寫一些測試代碼:

        import?React?from?'react';
        import?ReactDOM?from?'react-dom';

        ReactDOM.render(<h1>code秘密花園?Hello,?world!h1>,?document.getElementById('root'));

        然后,我們?yōu)?babelwebpack 創(chuàng)建兩個配置文件:

        .babelrc

        {
        ????"presets":?[
        ????????"@babel/preset-env",
        ????????"@babel/preset-react"
        ????]
        }

        webpack.config.js

        const?HtmlWebpackPlugin?=?require('html-webpack-plugin');

        const?path?=?require('path');

        module.exports?=?{
        ??entry:?'./page/index.jsx',
        ??output:?{
        ????path:?path.resolve(__dirname,?'dist'),
        ????filename:?'bundle.[hash].js',
        ??},
        ??devServer:?{
        ????compress:?true,
        ????port:?8080,
        ????hot:?true,
        ????static:?'./dist',
        ????historyApiFallback:?true,
        ????open:?true,
        ??},
        ??module:?{
        ????rules:?[
        ??????{
        ????????test:?/.(js|jsx)$/,
        ????????exclude:?/node_modules/,
        ????????use:?{
        ??????????loader:?'babel-loader',
        ????????},
        ??????},
        ????],
        ??},
        ??plugins:?[
        ????new?HtmlWebpackPlugin({
        ??????template:?`${__dirname?}/public/index.html`,
        ??????filename:?'index.html',
        ????}),
        ??],
        ??mode:?'development',
        ??devtool:?'inline-source-map',
        };

        然后,在 public 下創(chuàng)建一個 index.html

        html>
        <html?lang="en">

        <head>
        ????<meta?charset="UTF-8">
        ????<meta?name="viewport"?content="width=device-width,?initial-scale=1.0">
        ????<title>code秘密花園title>
        head>

        <body>
        ????<div?id="root">div>
        body>

        html>

        下面檢查下你的 package.json,看看和我的是不是一樣:

        {
        ??"name":?"react-wasm",
        ??"version":?"1.0.0",
        ??"description":?"一個?Rust?編寫?React?組件的?Demo",
        ??"main":?"src/index.jsx",
        ??"scripts":?{
        ????"dev":?"webpack?server"
        ??},
        ??"keywords":?[],
        ??"author":?"ConardLi",
        ??"license":?"MIT",
        ??"dependencies":?{
        ????"react":?"^17.0.2",
        ????"react-dom":?"^17.0.2"
        ??},
        ??"devDependencies":?{
        ????"@babel/core":?"^7.16.0",
        ????"@babel/preset-env":?"^7.16.4",
        ????"@babel/preset-react":?"^7.16.0",
        ????"babel-loader":?"^8.2.3",
        ????"html-webpack-plugin":?"^5.5.0",
        ????"webpack":?"^5.64.2",
        ????"webpack-cli":?"^4.9.1",
        ????"webpack-dev-server":?"^4.5.0"
        ??}
        }

        下面,執(zhí)行 npm install,然后 npm run dev,你就可以跑起來一個非常簡單的 React 應(yīng)用:

        引入 Rust

        好了,下面我們來編寫我們的 Rust 組件(別忘了回顧下上面提到的 Rust 前置知識),首先我們使用 Rust 的包管理工具 cargo 來初始化一個簡單的 Rust 應(yīng)用程序:

        cargo?init?--lib?.

        執(zhí)行完之后,會創(chuàng)建一個 Cargo.toml 和一個 src/lib.rc 文件。

        然后,我們在 ?Cargo.toml 中引入 wasm-bindgen 這個包,另外我們還需要告訴編譯器這個包是一個 cdylib

        [package]
        name = "react-wasm"
        version = "1.0.0"
        edition = "2021"

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

        [dependencies]
        wasm-bindgen = "0.2"

        現(xiàn)在,你可以先嘗試執(zhí)行下 cargo build

        第一次執(zhí)行可能會比較慢,可以 Google 搜一下怎么將 cargo 配置為國內(nèi)源。

        好了,上面只是測試一下構(gòu)建,它現(xiàn)在還派不上用場,我們下面還要執(zhí)行一下編譯目標(biāo),執(zhí)行:

        $?rustup?target?add?wasm32-unknown-unknown

        指定好 wasm32-unknown-unknown 這個編譯目標(biāo),我們才能把它應(yīng)用到我們的 React 程序中,下面我們給我們的 src/lib.rs 寫兩個簡單的函數(shù):

        use?wasm_bindgen::prelude::*;

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

        #[wasm_bindgen]
        pub?fn?big_computation()?{
        ????alert("這個是一個超級耗時的復(fù)雜計算邏輯");
        }

        #[wasm_bindgen]
        pub?fn?welcome(name:?&str)?{
        ???alert(&format!("Hi 我是?{}?,我在 code秘密花園?!",?name));
        }

        為了確保我們的 Rust 應(yīng)用程序正常工作,我們重新用 wasm32-unknown-unknown 編譯一下:

        $?cargo?build?--target?wasm32-unknown-unknown

        然后我們安裝一下 wasm-bindgen-cli 這個命令行工具,以便我們能利用我們創(chuàng)建的 WebAssembly 代碼:

        $?cargo?install?-f?wasm-bindgen-cli

        安裝后,我們可以使用 Rust 生成的 WebAssembly 給我們的 React 代碼創(chuàng)建一個包:

        $?wasm-bindgen?target/wasm32-unknown-unknown/debug/react_wasm.wasm?--out-dir?build

        執(zhí)行完成后,編譯好的 JavaScript 包和優(yōu)化好的 Wasm 代碼會保存到我們的 build 目錄中,以供 React 程序使用。

        在 React 程序中應(yīng)用 Wasm

        下面,我們嘗試一下在我們的 React 程序中用上這些 Wasm 代碼,我們現(xiàn)在 package.json 中添加一些常用的 npm 腳本:

        ??"build:wasm":?"cargo?build?--target?wasm32-unknown-unknown",
        ??"build:bindgen":?"wasm-bindgen?target/wasm32-unknown-unknown/debug/rusty_react.wasm?--out-dir?build",
        ??"build":?"npm?run?build:wasm?&&?npm?run?build:bindgen?&&?npx?webpack",

        然后我們執(zhí)行 npm run build 就可以打包所有代碼啦。

        下面,我們還需要安裝一下上面我們提到的 wasm-packWebpack 插件,它可以幫助我們把 Wasm 代碼打包成 NPM 模塊:

        npm?i?-D?@wasm-tool/wasm-pack-plugin

        最后更新一下我們的 webpack.config.js,添加下面的配置:

        const?WasmPackPlugin?=?require("@wasm-tool/wasm-pack-plugin");

        ??...

        ??plugins:?[
        ????...
        ????new?WasmPackPlugin({
        ??????crateDirectory:?path.resolve(__dirname,?".")
        ????}),
        ??],
        ??...
        ??experiments:?{
        ????asyncWebAssembly:?true
        ??}

        下面,執(zhí)行一下這幾個命令:npm run build:wasm、npm run build:bindgen、npm run build,應(yīng)該都不會報錯。

        最后,我們在我們的 React 組件中調(diào)用一下我們剛剛生成的 Wasm 模塊:

        import?React,?{?useState?}?from?"react";
        import?ReactDOM?from?"react-dom";

        const?wasm?=?import("../build/rusty_react");

        wasm.then(m?=>?{
        ??const?App?=?()?=>?{
        ????const?[name,?setName]?=?useState("");
        ????const?handleChange?=?(e)?=>?{
        ??????setName(e.target.value);
        ????}
        ????const?handleClick?=?()?=>?{
        ??????m.welcome(name);
        ????}

        ????return?(
        ??????<>
        ????????<div>
        ??????????<h1>Hi?thereh1>

        ??????????<button?onClick={m.big_computation}>Run?Computationbutton>
        ????????div>
        ????????<div>
        ??????????<input?type="text"?onChange={handleChange}?/>
        ??????????<button?onClick={handleClick}>Say?hello!button>
        ????????div>
        ??????
        ????);
        ??};

        ??ReactDOM.render(<App?/>,?document.getElementById("root"));
        });

        下面,你就可以在 React 組件中愉快的使用 Rust 了!

        參考

        • https://www.rust-lang.org/learn
        • https://rustwasm.github.io/
        • https://www.joshfinnie.com/blog/using-webassembly-created-in-rust-for-fast-react-components/
        瀏覽 78
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
          
          

            1. 大奶毛片| 亚洲+日产+专区 | 欧美日韩综合网 | 被十几个男人扒开腿猛戳的小说 | 免费无码又爽又黄又刺激网站 |