
封面图是 mintlify,一个用 AI 来帮你写注释的神器,写的还挺好的。
众所周知,现在前端都喜欢写 rust, 要是不会用 go 或者 rust 双修都不好意思出去见人。作有追求的程序员我们总不能写个hello word 就满足了,我们至少要满足工程化,所以我用 rust 写了一个生成 changelog 的工具,顺便分享一些 node 和 rust 的工程化的问题。
📖 名词解释
👓 交叉编译
rust 在每个平台都有相关都会生成相应的可执行文件。rust 支持非常多的平台和cpu,为了方便开发者, rust 支持了很棒的交叉编译,可以在相应的平台的编译出相应的二进制文件。
rustc --print target-list | pr -tw100 --columns 3----------------------->aarch64- riscv64gc-unknown-freebsdnux-muslaarch64-apple-io i386-ap riscv64gc-unknown-linux-gnuaarch64- i586-pc riscv64gc-unknown-linux-muslaarch64-apple-io riscv64gc-unknown-none-elfaarch64- riscv64imac-unknown-none-elfslaarch64-fuchsia s390x-unknown-linux-gnunaarch64-kmc-soli s390x-unknown-linux-muslaarch64-linux-an sparc-unknown-linux-gnuaarch64-pc-windo sparc64-unknown-linux-gnuaarch64-unknown- sparc64-unknown-netbsdaarch64-unknown- sparc64-unknown-openbsdaarch64- i686-un sparcv9-sun-solarisaarch64- thumbv4t-none-eabimuslaarch64- i686-un thumbv6m-none-eabiaarch64-unknown- thumbv7a-pc-windows-msvcaarch64-unknown- thumbv7a-uwp-windows-msvcaarch64- i686-uw thumbv7em-none-eabiaarch64-unknown- thumbv7em-none-eabihfaarch64-unknown- thumbv7m-none-eabiaarch64-unknown- thumbv7neon-linux-androideabiaarch64- mips-un thumbv7neon-unknown-linux-gnueabaarch64- thumbv7neon-unknown-linux-musleaaarch64_ thumbv8m.base-none-eabicaarch64_ thumbv8m.main-none-eabimips64-unknown-linux-gnuabi64arm-linu thumbv8m.main-none-eabihfux-muslabi64wasm32-unknown-emscripteninux-gnuabi64wasm32-unknown-unknownwn-linux-muslabi64arm-unkn mipsel- wasm32-wasiarm-unkn wasm64-unknown-unknownuarmebv7r x86_64-apple-darwinwn-linux-muslarmebv7r x86_64-apple-iosknown-linux-uclibcarmv4t-u mipsel- x86_64-apple-ios-macabiarmv5te- x86_64-apple-tvosn-linux-gnux86_64-fortanix-unknown-sgxn-linux-gnumipsisa64r6-unknown-linux-gnuabi x86_64-fuchsiaarmv6-unknown-fr mipsisa64r6el-unknown-linux-gnua x86_64-linux-androidarmv6-un msp430- x86_64-pc-solarisarmv6k-nintendo- x86_64-pc-windows-gnuarmv7-ap x86_64-pc-windows-msvc-unknown-freebsdarmv7-li x86_64-sun-solarisnown-linux-gnuarmv7-un x86_64-unknown-dragonflyinux-gnuspearmv7-un x86_64-unknown-freebsduslarmv7-un powerpc x86_64-unknown-haikuarmv7-un x86_64-unknown-hermitdarmv7-un powerpc x86_64-unknown-illumospowerpc x86_64-unknown-l4re-uclibcarmv7-un x86_64-unknown-linux-gnuarmv7-wr x86_64-unknown-linux-gnux32armv7a-k x86_64-unknown-linux-muslslarmv7a-k powerpc x86_64-unknown-netbsdarmv7a-n x86_64-unknown-noneunknown-freebsdarmv7a-n x86_64-unknown-none-hermitkernel-gnuarmv7r-n x86_64-unknown-none-linuxkernelx-muslarmv7r-n x86_64-unknown-openbsdwn-linux-gnuarmv7s-a x86_64-unknown-redoxnown-linux-muslasmjs-un x86_64-unknown-uefie-elfavr-unkn x86_64-uwp-windows-gnue-elfbpfeb-unknown-no x86_64-uwp-windows-msvcbpfel-un x86_64-wrs-vxworksunknown-none-elf
👓 n-api
https://nodejs.org/api/n-api.html
N-API 是一个规范,主要是为了解决 Node.js 和 C++ 模块之间的通信。
#define NAPI_VERSION 3#include <node_api.h>
我们通过 node 调用 rust 也是借用了这个方式。
👓 cargo 和 rustup
rust 的包管理工具,比 npm 好多了。
rustup 是 rust 的管理工具,一般用来安装 交叉编译的工具和 rust 本身。

👓 optionalDependencies
optionalDependencies 代表可选的依赖,即使这个依赖安装失败了也可以继续使用,配合 package.json 中的 os 与 cpu 字段可以让不需要包安装失败,用来配置分发我们底层包。
"optionalDependencies": {"@umijs/doctor-darwin-arm64": "^0.0.11","@umijs/doctor-darwin-x64": "^0.0.11","@umijs/doctor-linux-x64-gnu": "^0.0.11","@umijs/doctor-win32-x64-msvc": "^0.0.11"},
👓 artifacts
构件 或者说叫产物,一般用于 CI/CD 中生成的某些需要持久化的东西,可以在多个 CI 进程中公用。与 cdn 不同的是,会在一次 CI/CD 中走完所有的生命周期。
🚀 开始开发
⁉️ 为什么要用 rust 开发?
rust 拥有很多底层的生态的,比如更加好用并且性能更的 git 操作库,git 官方支持拥有非常强的性能,可以在 2s 内处理完 procomponents 的所有 commit,并且生成文件。
use git2::Repository;let repo = match Repository::init("/path/to/a/repo") {Ok(repo) => repo,Err(e) => panic!("failed to init: {}", e),};
其次还有更快的网络请求库,比 node 要快 25 倍,虽然看起来不起眼,但是生成changelog 很重要的一步就是从 github 中读取 commit 的 pr 和创建人,根据 commit 的数量来看,这个请求将会非常密集,并且需要序列化多次 JSON,社区是由文章进行过比较的, rust 和 go 的性价比会好很多, tnpm 的raid 模式也通过将网络请求层换成 rust 获得了极大的性能提升。
Rust 拥有最好的并发生态系统,其次是 Java 和 Golang,它们已经有了成熟的选择。Node.js和Deno虽然不如其他节点好,但也提供了一个底层的生态系统。
对于这种需要高性能的密集计算任务 rust 还是比较合适的,而且输入和输出都会很简单。
🆕 创建项目
首先最重要的就是创建文件夹了。 我们这里使用 NAPI-RS 的架构,它可以帮助我们快速生成一个的脚手架, 这里可以看到所有的文档。
// 安装依赖yarn global add @napi-rs/cli# ornpm install -g @napi-rs/cli# orpnpm add -g @napi-rs/cli// 新建项目napi new
新建的时候会让我们选择平台,这里我们推荐增加一个 aarch64-apple-darwin 就好了,android 和 别的 linux 平台兼容性不是很好。

选择完成之后就是这样的场景:
这里只带了几个script ,我们可以用这个东西来编译和发布。
build 之后会生成 index.js 和 **.node 文件,这两个文件就是我们最后的产物。
✍ 开发功能
开发没什么好说的,rust 写起来那是相当简单随意。

这里有我用了两个比较复杂的库,一个用来解析 git 仓库,一个用 swc 来解析 js 的 AST。
git2 = "0.14"deno_ast = { version = "0.5.0", features = ["transforms", "utils", "visit", "view"] }
rust 的文档是真的不错。只有在lib 中加了 [napi] 的方法才会被暴露到 js 中,效果看起来是这样的。

方法中最好只用 String 来当参数,复杂的参数性能多的,转化成的开销不算小的,这是一个简单的 a+b 的代码,因为桥接的开销反而比 node.js 运行还要慢。

⚠️ 测试
写好代码自然要测试一下了,在 rust 中也提供了相应的方案。
断言写起来和我们的 node 也差不多
assert_eq!(2 + 2, 4);
写起来大概是这样的,我们可以调用 cargo test 或者 vscode 中点击小图标运行。
👩🏻💻 发布
对于我们这个框架的发布来说最难得就是发布了,因为我需要编译多个操作系统,并且同时发布他们。

基本思路是用不同得操作系统打包出相应操作系统得二进制文件,然后上传到 artifact
最后发布得时候我们将 artifact 移动到相应得 npm 包中,并且发布到 npm 上。

发布之后得效果:

我们通过 npm 依赖来获取安装到得包。

在 index 中我们通过不同得平台和不同得 cpu 来加载不同得node 文件,达到跨平台得效果。

其实这一步最难得就是编译了,rust 是出了名得编译速度慢,依赖操作系统又让他编译非常辛苦,经常出一些 java 和 node 不会碰到的问题。
这是我得 arm 版本 mac 得编译脚本,看这些脚本就知道我踩了多少坑。

而这个是 x86 的编译。

这也是我推荐只支持主流操作系统的原因。至于为什么支持的 arm 的 mac,主要原因还是我有。
😺 使用
度过了艰难的发布和部署,使用就是相当的简单和随意了,因为参数和返回相对比较简单,所以我们还会的到很不错的类型定义,调用起来可以说是相当舒服了。

ts 定义也很完整。

我这个里面一共带了四个方法,分别是:
- 生成上次tag 的 changelog
- 生成所有的changelog
- 检查路由配置的对不对
- 检查 npm 发包是不是真的成功了
检查路由配置是为了以后些基于 rust 的lint 规则,npm实在是太卡太慢,太复杂了。
🗨️ 简单的说两句
最后经过这次的处理,我感觉这个东西最方便的还是写贴近底层的代码,虽然成本不算太大,但是收益也不大,尤其是你还要编译到各个操作系统,这个如果没有好的 CI 做起来会非常辛苦。
这里只推荐这几种应用:
- 调用 图片处理,视频处理,大文件读取等 io,cpu,gpu 密集的功能
- cli 功能的开发,新建脚手架之类的会舒服很多,因为有很多方便的库
- 短时间内执行密集操作的。 比如说下载几百个小文件
- ❤️ 为爱发电
