当前位置:首页 > 编程语言 > 正文内容

如何为Npm发布WebAssembly包

a811625532年前 (2023-12-29)编程语言12

WebAssembly (WA *** ) 代码可以显著提高速度,基于它编写和发布 npm 软件包现在已经成为一个非常有趣的目标。本文将向您展示如何开发和发布 WA *** 软件包,以便您能在自己的工作中应用这项技术。

(通常缩写为 Wa *** )是一种突破性的二进制指令格式,它改变了 *** 开发的格局。它允许开发人员在浏览器中以接近原生的速度运行 JavaScript 之外的其他语言编写的代码。WebAssembly 最令人兴奋的应用之一是在 领域。

在深入探讨文章的其他部分之前,让我们先快速讨论一下使用 WebAssembly 的目的。我们的主要目标是通过构建一个搜索索引来发现使用 WebAssembly 实现 包的优势,该索引可以让我们高效地搜索字符串 *** 并返回相关匹配结果。这将是 Wade.js npm 软件包的简化版本,但使用的是 和 Wa *** 。最后,你将知道如何开发一个高性能的搜索包。

使用 WebAssembly 编写 npm 软件包的好处包括:

  1. 性能:WebAssembly 模块以接近原生的速度运行,比同等 JavaScript 实现快得多。
  2. 灵活性:开发人员可以在 *** 环境中利用 Rust、C 和 C++ 等语言的功能和特性。
  3. 安全性:WebAssembly 提供了一个沙箱式的执行环境,即使代码中存在错误,也能确保代码不会对主机系统造成危害。

要继续学习,请确保您的开发环境包括:

  • :构建服务器端应用程序必不可少的 JavaScript 运行环境。
  • :确保您有一个激活的 NPM 注册表账户,因为它是发布 npm 包所必需的。

在接下来的章节中,您将了解如何设置 Rust 开发环境、在 Rust 中实现搜索功能以及编译和发布 npm 软件包。

设置新的 Rust 项目

您可以按照此处的说明在本地计算机上。

安装好 Rust 后,就该获取 二进制文件了。这个小工具可以帮助你将 Rust 代码编译成 WebAssembly,并打包以实现无缝开发。

设置成功后,运行下面的 命令来设置一个新的 Rust 项目:

cargo new --lib refactored-couscous

通过添加 --lib,你可以指示 cargo 生成一个 Rust 库模板。你应该将文件夹名称 “refactored-couscous” 改为你喜欢的名称。cargo 生成的文件夹结构应与下面的目录相同:

refactored-couscous/
├── Cargo.lock
├── Cargo.toml
├── LICENSE_APACHE
├── src/
│   └── lib.rs
└── target/

接下来,让我们在代码执行过程中添加必要的依赖项。打开 Cargo.toml 文件,更新其依赖项部分:

# file: ./Cargo.toml
[dependencies]
wa *** -bindgen = "0.2"
js-sys = "0.3"

wa *** -bindgen crate 对于促进 Rust 和 JavaScript 之间的高级交互至关重要。它提供了 #[wa *** _bindgen] 属性,你可以用它来注释 Rust 结构和函数,使 JavaScript 可以访问它们。

js-sys crate 为 Rust 中的所有 JavaScript 全局对象和函数提供绑定。它是 wa *** -bindgen 生态系统的一部分,旨在与 wa *** -bindgen 配合使用。

用 Rust 重写简化的 Wade.js npm 软件包

在完成设置后,让我们开始实现搜索包。

在深入研究代码之前,了解我们的主要目标至关重要。我们的目标是建立一个搜索索引,让我们能够高效地搜索字符串 *** ,并返回相关的匹配结果。

// file: src/lib.rs
use std::collections::HashMap;
use wa *** _bindgen::prelude::*;
use js_sys::Array;
#[wa *** _bindgen]
#[derive(Debug, Clone)]
pub struct Token {
word: String,
position: usize,
frequency: usize,
}

我们首先导入必要的库。然后声明包含三个字段的 Token 结构:wordposition, 和 frequencystruct 将表示单个单词、单词在数据中的位置以及单词在给定字符串中的频率。

接下来,让我们定义搜索索引结构。搜索包的核心是 Index 结构,它将存储我们要搜索的所有字符串,并允许我们快速查找单词的出现。

// file: ./src/lib.rs
#[wa *** _bindgen]
#[derive(Debug)]
pub struct Index {
data: Vec<String>,
tokens: HashMap<String, Vec<Token>>,
}

在上述代码段中,data 字段是一个存储所有字符串的向量。tokens 字段是一个 hashmap,其中每个键是一个单词,其值是一个 Token 结构向量。通过这种结构,我们可以快速查找数据中出现的所有单词。

接下来,让我们为每个字符串实现标记化。标记化就是将字符串分解成单个词或 “tokens”。有了单个标记,我们就可以单独分析和处理每个单词。这种粒度使我们能够专注于特定的单词,从而更容易搜索、分析或处理文本。

// file: ./src/lib.rs
impl Token {
fn tokenize(s: &str) -> Vec<String> {
s.to_lowercase()
.split_whitespace()
.map(|word| word.to_string())
.collect()
}
}

tokenize 函数将字符串作为输入。它会将字符串转换为小写,以确保搜索不区分大小写。然后将字符串分割成单词,再将每个单词转换成字符串。最后,单词被收集到一个向量中并返回。

接下来,让我们初始化并填充搜索 Index

// file: ./src/lib.rs
#[wa *** _bindgen]
impl Index {
#[wa *** _bindgen(constructor)]
pub fn new() -> Self {
Index {
data: Vec::new(),
tokens: HashMap::new(),
}
}
pub fn add(&mut self, s: &str) {
let position = self.data.len();
self.data.push(s.to_string());
let tokens = Token::tokenize(s);
for token in tokens {
let frequency = s.matches(&token).count();
self.tokens.entry(token.clone()).or_insert_with(Vec::new).push(Token {
word: token,
position,
frequency,
});
}        
}
...   
}

在上述代码段中,new 函数初始化了一个空 Index。然后,add 函数允许我们向索引中添加新字符串。为此,它会对字符串进行标记化处理,计算每个标记的频率,并相应地更新 tokens 哈希表。

#[wa *** _bindgen(constructor)] 属性表示当从 JavaScript 访问 Rust 结构时,相关函数应被视为 Rust 结构的构造函数。

接下来,让我们实现搜索功能。为了在索引中搜索匹配项,我们将如下定义搜索函数:

// file: ./src/lib.rs
#[wa *** _bindgen]
impl Index {
...
pub fn search(&self, query: &str) -> Array {
let tokens = Token::tokenize(query);
let mut results = Vec::new();
for token in tokens {
if let Some(matches) = self.tokens.get(&token) {
for match_ in matches {
results.push(self.data[match_.position].clone());
}
}
}
results.sort();
results.dedup();
// Convert Vec<String> to js_sys::Array
results.into_iter().map(JsValue::from).collect()
}
}

search 函数首先对查询进行标记化。对于查询中的每个标记,它都会检查 tokens 哈希表中是否有匹配项。如果发现匹配,就会将 data 向量中的相应字符串添加到搜索结果中。然后对结果进行排序,并删除重复的结果。最后,使用 js_sys::Array 将结果转换为 JavaScript 数组并返回。

有了这个实现,我们就有了一个使用 Rust 构建的强大搜索索引。在下一节中,我们将深入探讨如何将 Rust 代码编译到 WebAssembly 中,以便将其无缝集成到 JavaScript 环境中。

将 Rust 代码反编译为 WebAssembly

本节将深入探讨将 Rust 代码转编到 WebAssembly 的不同 *** ,这取决于特定的 JavaScript 环境。在本讨论中,我们将集中讨论两个主要的编译目标: *** (与基于浏览器的应用程序有关)和捆绑程序(与服务器端操作有关)。

要转译为 WebAssembly,需要在项目根目录下运行下面的 wa *** -pack 命令:

wa *** -pack build --target web

执行命令后,会启动一系列进程。首先是将 Rust 源代码转译为 WebAssembly。随后,在生成的 WebAssembly 上执行 wa *** -bindgen 工具,生成一个 JavaScript 封装器,以促进浏览器与 WebAssembly 模块的兼容性。这个过程还协调了 pkg 目录的形成,将 JavaScript 封装器和原始 WebAssembly 代码重新定位到这个位置。根据 Cargo.toml 提供的信息,它还会创建相应的 package.json。如果存在 README.md 或许可证文件,则将其复制到软件包中。最终,这一系列操作会在 pkg 目录中创建一个合并的软件包。

在继续第二个目标之前,让我们先简单了解一下生成的 pkg 目录:

./pkg
├── LICENSE_APACHE
├── package.json
├── refactored_couscous.d.ts
├── refactored_couscous.js
├── refactored_couscous_bg.js
├── refactored_couscous_bg.wa *** 
└── refactored_couscous_bg.wa *** .d.ts

refactored_couscous.d.ts 是 TypeScript 声明文件,通过详细说明包中函数和模块的类型,为 TypeScript 开发人员提供了类型安全。 refactored_couscous.js 是由 wa *** -bindgen 创建的 JavaScript 封装器,它将 WebAssembly 模块与 JavaScript 领域连接起来,实现无缝集成。作为补充,refactored_couscous_bg.js 是一个辅助文件,用于处理一些底层操作以及与 WebAssembly 模块的交互。软件包的核心在于 refactored_couscous_bg.wa *** ,这是一个从 Rust 派生的 WebAssembly 二进制文件,封装了软件包的主要逻辑。最后,refactored_couscous_bg.wa *** .d.ts 是另一个 TypeScript 声明文件,与之前的 .d.ts 文件类似,但根据 WebAssembly 模块的具体情况进行了定制。

下一条 wa *** -pack 命令会将 Rust 代码转换为 WebAssembly 模块,该模块专门为使用基于 npm 的捆绑程序(如 Webpack 或 Rollup)而定制:

wa *** -pack build --target bundler

-target bundler 标志表示输出结果应与这些捆绑工具兼容,从而使生成的 WebAssembly 模块更容易集成到现代 *** 开发工作流程中。该命令生成的 pkg 目录与 --target web 标志生成的文件数量相同,但文件内容略有不同。

在 Web 应用程序中集成 WebAssembly 模块

既然我们已经知道了如何针对不同的 JavaScript 环境,那么让我们从 --target web 转置开始,在基于浏览器的应用程序中使用生成的模块。

在根目录下创建一个 index.html 文件,并用以下内容更新它:

<!-- file: ./index.html -->
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<title>Wade Search in WebAssembly</title>
<style>
/* for the styles, see the https://github.com/Ikeh-Akinyemi/refactored-couscous/blob/main/index.html */
</style>
</head>
<body>
<input type="text" id="searchInput" placeholder="Search..." />
<button onclick="performSearch()">Search</button>
<ul id="results"></ul>
<script type="module">
import init, { Index } from "./pkg/refactored_couscous.js";
let index;
async function setup() {
await init();
index = new Index();
// Sample data for demonstration purposes
index.add("Hello world");
index.add("Start your Rust journey here")
index.add("Found my empress.");
index.add("Talkin about systems")
index.add("Wade in Rust");
}
function performSearch() {
const query = document.getElementById("searchInput").value;
const results = index.search(query);
displayResults(results);
}
window.performSearch = performSearch;
function displayResults(results) {
const resultsElement = document.getElementById("results");
resultsElement.innerHTML = "";
results.forEach((result) => {
const li = document.createElement("li");
li.textContent = result;
resultsElement.appendChild(li);
});
}
setup();
</script>
</body>
</html>

在上述代码段中, <script> 元素中的 从生成的 JavaScript 封装器(refactored_couscous.js)中导入了 init 函数和 Index 类。init 函数非常重要,因为它初始化了 WebAssembly 模块,确保其可随时使用。

页面加载时会调用 setup 函数。它首先使用 await init() 确保 WebAssembly 模块完全初始化。然后,创建 WebAssembly 模块中 Index 类的实例,用于存储和搜索数据。

当用户点击 “Search” 按钮时,将触发 performSearch 函数。它从文本字段中检索用户输入的内容,使用 Index 类的 search  *** 查找匹配内容,然后使用 displayResults 函数显示结果。

displayResults 函数获取搜索结果,为每个结果创建一个列表项,并将其添加到网页上的 results 无序列表中。

在浏览器中加载 index.html 文件并进行单词搜索,如下图所示:

如何将 WebAssembly 模块作为 NPM 包

在本节中,我们将从 --target bundler 转译开始,用 使用生成的模块。然后,我们将把该包发布到 NPM 注册表,并在 Node.js 中安装和使用它。

使用 --target bundler 标志运行 wa *** -pack transpilation 命令。然后将位置更改为 pkg 目录,并运行下面的 npm 命令:

npm link

pkg 目录中运行 npm link 命令,就能像全局安装 NPM 软件包一样访问该软件包。这样,你就可以使用软件包,而无需将其实际发布到 NPM 注册表中。这对于测试和开发目的尤其有用。

接下来,在项目根目录下创建一个新文件夹,用于存放我们将要介绍的示例:

mkdir -p examples/webpack-impl

./examples/webpack-impl 文件夹中创建 package.json 文件,并添加以下配置:

// file: ./examples/webpack-impl/package.json
{
"scripts": {
"serve": "webpack-dev-server"
},
"dependencies": {
"refactored-couscous": "^0.1.0"
},
"devDependencies": {
"webpack": "^4.25.1",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.10"
}
}

接下来,运行以下 npm 命令链接软件包并安装其他软件包:

npm link refactored-couscous && npm install

安装完成后,创建一个 index.html 文件并添加以下 HTML 代码:

<!-- file: ./examples/webpack-impl/index.html -->
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<title>refactored-couscous example</title>
<style>
/* for the styles, see https://github.com/Ikeh-Akinyemi/refactored-couscous/blob/main/examples/webpack-impl/index.html*/
</style>
</head>
<body>
<div id="loading" style="display: none">
<div class="2345HAO83c9085b6f9fe5d2 spinner"></div>
Loading...
</div>
<input type="file" id="fileInput" />
<input type="text" id="urlInput" placeholder="Enter URL" />
<button id="buildIndexButton">Build Index</button>
<input type="text" id="searchInput" placeholder="Search..." />
<button id="searchButton">Search</button>
<ul id="results"></ul>
<script src="./index.js"></script>
</body>
</html>

接下来,让我们创建一个 index.js 文件,并添加以下 JavaScript 代码:

// file: ./examples/webpack-impl/index.js
import("refactored-couscous").then((js) => {
function splitIntoSentences(text) {
return text.match(/[^\.!\?]+[\.!\?]+/g) || [];
}
const index = new js.Index();
...
});

本节首先动态导入 refactored-couscous 模块。模块导入后,回调函数将以导入的模块为参数( js )执行。

接下来,我们定义了一个名为 split into sentences 的实用函数,用于根据标点符号将给定文本分割成单个句子。然后,从导入模块中创建 Index 类的实例。

现在,让我们添加一个事件监听器,检查用户是否上传了要搜索的文件或 URL,然后使用资源的内容建立 Index

// file: ./examples/webpack-impl/index.js
import("refactored-couscous").then((js) => {
...
document
.getElementById("buildIndexButton")
.addEventListener("click", async () => {
const fileInput = document.getElementById("fileInput");
const urlInput = document.getElementById("urlInput");
const loadingDiv = document.getElementById("loading");
loadingDiv.style.display = "block";
if (fileInput.files.length) {
const file = fileInput.files[0];
const content = await file.text();
const sentences = splitIntoSentences(content);
sentences.forEach((sentence) => {
if (sentence.trim()) {
console.log(sentence);
index.add(sentence.trim());
}
});
} else if (urlInput.value) {
try {
const response = await fetch(urlInput.value);
const content = await response.text();
const sentences = splitIntoSentences(content);
sentences.forEach((sentence) => {
if (sentence.trim()) {
index.add(sentence.trim());
}
});
} catch (error) {
console.error("Error fetching URL:", error);
}
}
loadingDiv.style.display = "none";
});
...
});

在上述代码段中,我们通过 fileInput 文件输入元素检查是否提供了文件。如果是,它将读取文件内容,使用实用功能将其分割成句子,并将每个句子添加到索引中。

如果提供的是 URL 而不是文件,则会从 URL 获取内容,将其拆分成句子并添加到索引中。

接下来,让我们实现搜索 Index 并显示结果:

// file: ./examples/webpack-impl/index.js
import("refactored-couscous").then((js) => {
...
document.getElementById("searchButton").addEventListener("click", () => {
const loadingDiv = document.getElementById("loading");
loadingDiv.style.display = "block";
const query = document.getElementById("searchInput").value;
const results = index.search(query);
console.log(results);
loadingDiv.style.display = "none";
displayResults(results);
});
function displayResults(results) {
const resultsList = document.getElementById("results");
resultsList.innerHTML = ""; // Clear previous results
results.forEach((result) => {
const listItem = document.createElement("li");
listItem.textContent = result;
resultsList.appendChild(listItem);
});
}
});

之后,让我们在 examples/webpack-impl 文件夹的根目录下创建一个 webpack.config.js 文件来配置 Webpack,并在其中填充以下内容:

// file: ./examples/webpack-impl/webpack.config.js
const path = require("path");
module.exports = {
entry: "./index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "index.js",
},
mode: "development",
};

设置完成后,就可以执行项目了。确保您使用的是 v16,特别是考虑到 Node.js 环境的特殊性。

npm run serve

执行后,在浏览器中访问 http://localhost:8080/ 。您可以选择直接上传文件或输入指向 *** 内容的 URL。

显示的图像证实了我们软件包的功能。下一步是将此软件包发布到 NPM 注册表,让更多人可以访问它。为此,请切换到 pkg 目录,并在 package.json 配置中加入  type: "module" 。这将确保软件包与 兼容。

npm publish

这将把软件包发布到你在 上的账户,如下图所示:

发布后,我们可以使用 npm install refactored-couscous 命令安装软件包,并使用 E *** odule 系统将其导入 :

// file: ./examples/cli/src/index.js
import { Index } from 'refactored-couscous/refactored_couscous.js';
const index = new Index();
index.add("Hello world");
index.add("Rust is amazing");
index.add("Wade in Rust");
const results = index.search("rust");
console.log(results);

设置好代码后,就可以使用 Node.js 运行脚本了。确保启用 WebAssembly 模块的实验标志:

node --experimental-wa *** -modules ./src/index.js

执行脚本后,你会看到控制台中打印的搜索结果,显示哪些条目包含 “rust” 一词:

[ 'Rust is amazing', 'Wade in Rust' ]

这表明我们的软件包在对所提供的数据进行搜索时非常有效。

小结

在本文中,我们使用 Rust 和 WebAssembly 成功重写了一个简化版的 Wade.js npm 软件包。我们深入研究了 Rust 类型系统的复杂性、WebAssembly 的性能优势,以及 Rust、WebAssembly 和 JavaScript 之间的无缝互操作性。我们还探讨了如何构建文本数据并编制索引、执行搜索,以及如何将 WebAssembly 模块集成到浏览器和服务器端 JavaScript 环境中。最后,我们迈出了关键的一步,将我们的软件包发布到 NPM 注册表,使其可以被更广泛地使用。这个练习不仅展示了 Rust 和 WebAssembly 的强大功能和灵活性,还为将来创建更复杂、更高效、更安全的 *** 应用程序奠定了基础。

GitHub 库的链接提供。

扫描二维码推送至手机访问。

版权声明:本文由2345好导航站长资讯发布,如需转载请注明出处。

本文链接:http://2345hao.cn/blog/index.php/post/8155.html

分享给朋友:

“如何为Npm发布WebAssembly包” 的相关文章

PHP 8.1有哪些变化:新特性、改变及弃用等

PHP 8.1有哪些变化:新特性、改变及弃用等

不久前,PHP 8.0大张旗鼓地发布了。它带来了许多新特性、性能增强和变化——其中最令人兴奋的是新的JIT编译器。 技术世界总是在向前发展,PHP也是如此。 ,包含了几个令人兴奋的特性。它定于今年晚些时候于2021年11月25日发布。 在本文中,我们将详细介绍PHP 8.1将带来哪些新的东...

什么是JavaScript?网络上最流行的脚本语言一瞥

什么是JavaScript?网络上最流行的脚本语言一瞥

谁在尝试访问某些网站时没有遇到过更新Java的请求? 虽然许多人通过交互式网站功能熟悉Java,但用户可能不太熟悉JavaScript——或者,实际上,他们可能错误地认为两者是相同的。 在本文中,我们将讨论JavaScript 是什么以及Java和JavaScript之间的区别。然后我们将概...

宝塔面板教程之不同环境安装篇

宝塔面板教程之不同环境安装篇

Linux面板环境安装,主要支持LNMP和LAMP、Tomcat、node.js。不过对于大部分站长来说,主要是LNMP和LAMP两个环境的安装。 LNMP和LAMP两个环境的最大区别是,前者采用Nginx作为Web服务器,后者则采用Apache作为Web服务器。(选择哪个作为您的Web服务器,可...

宝塔面板教程之主界面管理篇

宝塔面板教程之主界面管理篇

本章节主要是对宝塔面板的主界面的各个版本进行一个简单的说明。 宝塔面板主界面主要包括:服务器操作系统、服务器状态、站点信息、软件管理及网络流量几个部分。 Windows面板有部分功能未实现,其余部分与Linux面板同步。 系统操作...

宝塔面板教程之监控管理篇

宝塔面板教程之监控管理篇

宝塔面板另外一个特质是,你无需通过Linux命令行来查看服务器各项指标状况,即可以阿里云服务器类似的可视化图表,查看资源使用、负载、CPU占用及内容使用百分比等指标。 默认监控是关闭,有需要的,可以开启,监控数据默认保存30天,可以自行修改,默认监控数据保存在日志,可手动清理该日志。 监控管理,...

宝塔面板教程之安全管理篇

宝塔面板教程之安全管理篇

宝塔面板的安全管理可以设置SSH开关、禁用PING、放行端口、屏蔽IP等相关的操作。 SSH的设置 SSH的关闭与启动,点击下图的开关,即可开启或关闭SSH远程连接。 SSH端口的修改,SSH远程连接的默认端口为22,修改端口前,请先查看该端口是否被占用。 PING命令的...