一个使用 Go 来实现 Nodejs 的渐变色进度条
npm install grprogress
const grprogress = require('grprogress')
progress.update(0.8)
函数名 | 参数类型 | 描述 |
---|---|---|
update() | number | 进度条进度,值在0-1之间。 |
待补充
- Go 实现渐变色进度条。
- Go 交叉编译可以生成不同平台的二进制文件,这种二进制文件在相应平台上可以直接命令行执行查看进度条效果。
- Nodejs 通过 child_process 的 spawnSync 调用 Go 的二进制文件,模拟第 2 步骤。
其实本质并不是运行 Go 代码,运行的是 Go 编译后的二进制文件,而 Nodejs 只是调用了 Go 的二进制文件而已。
二进制文件一般会比较大,动辄 3-50 Mb,如果需要兼容很多平台,比如兼容 Windows、MacOS、MacOS M、Linux 等,那么 npm 包的大小就会很大。
使用 npm install 会下载很多其他平台的二进制文件,完全是没必要的,只需要下载当前平台的二进制文件即可。
所以把主包和平台包分离,主包只包含核心代码,平台包包含对应平台的二进制文件,这样 npm install 就会下载当前平台的二进制文件,其他平台的二进制文件不会下载,从而提高下载速度。
所以主包叫 grprogress,平台包的名字按照一定的规则自动生成。比如 @grprogress/win32-x64、@grprogress/darwin-x64、@grprogress/darwin-arm64、@grprogress/linux-x64。
当你编写好 Go 文件后,可以使用 go build -o grprogress 生成二进制文件,如果要生成多个平台包,可以指定 os 和 cpu 参数。具体逻辑可以参考 build 文件,包含了生成二进制文件和 package.json 的逻辑。
在此仓库中 npm 文件夹下有个 package.json.template 文件,是用来生成各个平台包里的 package.json 文件的模板。
- name 根据上面平台包类似的规则生成。
- version 同步主包的版本。
- file 是当前平台的二进制文件名称,除了 Windows 的二进制文件会带有.exe,其他平台均不带后缀名,一律叫 grprogress。
- os 是平台,比如 Windows 平台是 win32、 MacOS 平台是 darwin、Linux 平台是 linux。
- cpu 是 cpu 架构类型,比如 x64、arm64 等,MacOS M 系列芯片就用 arm64。
当 package.json 中编写了 os 和 cpu 字段,那么在 install 某一个平台包时,它会自动检测当前平台是否与 os 和 cpu 匹配,如果匹配,则安装该平台包,否则不进行安装,并且提醒你当前平台和当前平台包里填写的 os 和 cpu 不匹配。
当所有平台包都生成好后,目录结构如下,只需要把主包和所有平台包一起发布到 npm 即可。
├─npm
│ └─win32-x64
│ └─grprogress.exe
│ └─package.json // @grprogress/win32-x64 平台包
│ └─darwin-x64
│ └─grprogress
│ └─package.json // @grprogress/darwin-x64 平台包
└─package.json // grprogress 主包
按照常规的 npm 发布流程,在根目录 npm publish,再依次在 npm/win32-x64、...多个平台包目录 npm publish 即可发布成功。
这里借助了 semantic-release 发布主包,因为发布多个包和 monorepo 的发布形式还不相同,所以不能使用 semantic-release-monorepo 插件。
使用了@semantic-release/exec,自己写了一套平台包发布逻辑。具体可以参考 .releaserc.js和 release.js。
release.js 中还包含更新主包 optionalDependencies 中平台包名称及版本号等逻辑,稍后详细解释其作用。
我们参考 esbuild 的安装逻辑,在 npm install grprogress 时,会自动安装对应平台的平台包,比如在 Windows 下 npm install grprogress 会自动安装 @grprogress/win32-x64 平台包。
怎样实现自动安装平台包并可执行呢?
-
在主包的 package.json 中,添加一个 scripts 字段,里面有 postinstall 字段,当使用者使用 npm install 时,就会触发主包中的 postinstall 脚本,这个脚本就是install.js。
-
install.js 脚本大致逻辑是 先下载平台包,然后移动二进制文件到主包中。
-
我们参考了 esbuild 的 install.js,分为三级策略。
-
第一级,会通过
require.resolve(
@grprogress/win32-x64/grprogress.exe)
自动触发 optionalDependencies 中平台包@grprogress/win32-x64 的安装,如果成功,则直接返回,不再执行第二级和第三级策略。 -
第二级,如果第一级失败,则在主包安装过程中,创建一个临时文件夹,再执行 npm install @grprogress/win32-x64 安装平台包,然后把平台包中的 grprogress 二进制文件移动到主包中,然后删除临时文件夹。如果成功,则直接返回,不再执行第三级策略。
-
第三级,如果第二级失败,则通过 npm 压缩包的形式手动使用 https 下载,过程是 下载压缩包 -> 解压压缩包 -> 删除压缩包 -> 移动指定的文件 -> 删除文件夹。使用 https 请求
https://registry.npmjs.com/@grprogress/win32-x64/-/win32-x64-1.6.0.tgz
,下载好压缩包后,使用 zlib 解压,然后把 grprogress 二进制文件移动到主包中,然后删除临时文件夹。 -
最终都会在主包中找到 grprogress 二进制文件。
-
我们封装好了使用 spawn 运行 grprogress 二进制文件的逻辑,此时引入 grprogress,执行 grprogress.update(0.8) 即可在控制台看到渐变色进度条。
这样就达到了 npm 包里并没有二进制文件,安装后可以运行二进制文件的逻辑。
我们使用 Go 来实现,其他语言也是类似逻辑,比如目前 Rust 语言来实现 Nodejs 的工具库,例如 rspack,采用的方式也是大同小异。@rspack/core 里依赖了@rspack/binding,@rspack/binding 分发了很多平台包,安装的逻辑在binding.js。
因为 Go 语言本身就支持跨平台,所以不需要用到 wasm,有一种情况需要特殊处理,就是 android,android 需要使用 wasm。
渐变进度条 Go 库 BubbleTea - 非常适合简单和复杂的终端应用
- 使用 Go 编写的 esbuild 是如何发布到 npm 的?
- esbuild 的安装逻辑是什么?
- esbuild 支持多平台的思路是什么?
- Golang 是怎么编译为 npm 库的?
- Go 是怎么编译为 npm 库的?
- Go 与 npm 开发是如何结合的?
- 使用 Golang 语言编写 Nodejs 扩展的开发工具
- 基于 npm 进行跨平台分发 Golang 二进制程序