聊聊去年最火的前端库zx

  根据github上的数据,去年最受欢迎的前端库为谷歌棋下的zx。今天我们就来聊一聊这个去年最火的前端库。
聊聊去年最火的前端库zx

zx是什么

  zx 是谷歌实现的一个能在 node 中写 bash 的库。就像这样:

await $`echo "hello world"`;

  使用$``框起想要执行的命令,就可以直接执行 bash

  这个库的最大的便捷在于,node 和标准的 bash 都是我们非常熟悉的东西。只需要知道最基本的库的使用,就可以很快上手,写出功能很强大的工具。

基本使用

  只需要安装 zx 库,就可以便捷的在项目中使用了。

mkdir zx-demo
cd zx-demo
npm init -y
npm install zx --save
touch index.mjs

  需要一提的是,zx 命令执行的文件,需要以 .mjs 命名。

  在 .mjs 结尾的文件中,zx 可以允许在最外层使用 await,并提供了一系列常用的 node 库和函数,可以直接使用。

  现在打开刚刚创建的 index.mjs 文件,输入下面的指令,然后运行 npx zx index.mjs

await $`pwd`;
/*
输出
$ pwd
/Users/tang/Desktop/zx-demo
*/

  运行成功。

$`command`

  执行指令的两个飘折号``等同于ES6语法。你可以在内部直接使用${变量名}来嵌入命令。

var log = 'log';
await $`git ${log}`;

  这种使用方法需要注意的是,zx会自动给以${变量名}方式嵌入的内容加上括号,作为一个字符串。比如我要创建一个名为测试 测试的文件夹。在 bash 中直接执行是会失败的,如下:

mkdir 测试 测试
# 失败,创建了一个名为 测试 的文件夹,然后报错 mkdir: 测试: File exists
mkdir '测试 测试'
# 成功

  而使用zx则直接可以执行成功:

let name = '测试 测试';
await $`mkdir ${name}`;
// 输出:$ mkdir $'测试 测试' 成功

  除了这点,如果插入的变量是数组,zx 还会自动的解析数组,如下:

let flags = [
  '--oneline',
  '--decorate',
  '--color',
];
await $`git log ${flags}`;

  但是这个特性也会带来麻烦。当你直接键入命令的时候,zx 会强行解析。

let demo = 'git log';
await $`${demo}`; // 相当于在 bash 中执行 'git log'
/*
报错
$ $'git log'
/bin/bash: git log: command not found
Error: /bin/bash: git log: command not found
    at file:///Users/tang/Desktop/zx-demo/index.mjs:2:8
    exit code: 127 (Command not found)
*/

  此时可以利用 zx 会解析数组的特性来这样操作。

let demo = `git log`;
await $`${demo.split(' ')}`;
// 执行成功

  $`command`的返回值是一个继承Promise的类。可以追踪该命令的输入输出错误退出码。还提供了两个方法,pipekill

class ProcessPromise<T> extends Promise<T> {
  readonly stdin: Writable
  readonly stdout: Readable
  readonly stderr: Readable
  readonly exitCode: Promise<number>
  pipe(dest): ProcessPromise<T>
  kill(signal = 'SIGTERM'): Promise<void>
}

  其中 pipe 可以将进程的输出重定向,类似于 bash 中的管道语法。而 kill 则可以直接杀死进程。

// pipe
await $`cat file.txt`.pipe(process.stdout);
// kill
await $`npx service`.kill();

全局能力

  前文也提到过,zx 在执行以 .mjs 结尾的文件中,提供了便捷的能力,以及一些常用的函数和库。可以直接使用的库有:

  • chalk
  • fs
  • globby
  • os
  • path
  • minimist

关于这些库的作用和使用方法可以自行去 npm 上面查找,本文不做赘述。

  接下来重点聊一聊 zx 提供的两个非常有用的函数。

cd

  当你执行 npx zx index.mjs 时,默认的执行路径会是 index.mjs 文件的路径。而在文件中使用的每一个 bash,都会是以子进程的形式来执行的。所以,当使用 bash 来进行路径变更时,变更的是对应的子进程的路径,主进程的路径是不变的。

await $`pwd`; // 文件路径
await $`cd ../../`;
await $`pwd`; // 仍然是文件路径

  函数cd则可以改变主进程的路径。

await $`pwd`; // 文件路径
cd('../');
await $`pwd`; // ../文件路径

question

  question 函数是 readline 的包装,可以停住程序,等待用户输入敲击回车才完成。

let mustBeYse = await question('Do you like me ?');
// Do you like me ?

  question 还接受第二个参数,可以支持用户在输入的时候,使用 tab 键来补全。

// @type { choices: string[] }
let mustBeYse = await question('Do you like me ?', { choices: ['yes'] });

服务进程与子进程

  zx最棒的地方就在于他可以方便的开多个子进程。下面我愉快的起三个服务,三个服务的输出都会转到主进程。

$`npx serve`;
$`npx serve`;
$`npx serve`;
/*
输出
npx: 90 安装成功,用时 8.892 秒
npx: 90 安装成功,用时 8.957 秒
npx: 90 安装成功,用时 9.047 秒
INFO: Accepting connections at http://localhost:3000
INFO: Accepting connections at http://localhost:52857
INFO: Accepting connections at http://localhost:52858
*/

  此时如果我退出主进程,子进程的服务也会全部停止。

  在上文中,使用$`npx serve`的时候,没有加上 await。是因为类似于服务这种需要手动关闭的进程,如果使用 await 将永远执行不到下一步。

await $`npx serve`;
console.log(1); // 此时这一行永远不会执行到。

  如果你需要监听一个服务进程启动成功了,再去执行下一步的操作,可以使用for await 去监听日志信息。

let serve = $`npx serve -p 5000`;
for await (let chunk of serve.stdout) {
  // 启动服务成功
  if (chunk.includes('Accepting connections')) break;
}
// 拉取服务信息
await $`curl http://localhost:5000`;
serve.kill('SIGINT');

尝试实践

  了解了初步的使用方法之后,来实现一个小小的脚本。在实际的按迭代的开发工作中,每次开发之后都会清理掉所有的本地迭代分支。就写一个清空除了master 分支以外分支的脚本。

  首先需要获取到本地所有需要删除的分支名。首先需要执行 git branch 获取到所有的本地分支,获取的信息是一个字符串。
字符串中的分支名按照换行符隔开的,因此可以使用 split('\n') 把字符串按照分支分割成数组。

  接着需要摘除分支中的 master 分支和当前所在的分支。因为当前所在的分支是无法使用 git branch -d 来进行删除的。

const branchBash = await $`git branch`;
const localBranchNames =
  branchBash
    .stdout
    .split('\n')
    .filter(branchName => !branchName.includes('master') && !branchName.includes('*'))
    .map(filteredBranchName => filteredBranchName.trim());

  最后执行删除分支的命令,然后抓到无法删除分支的错误信息,提示给用户。

  try {
    const deleteBranch = await $`git branch -d ${localBranchNames}`;
  } catch(p) {
    let remainBranch = p.stderr.match(/\'(\S+?)\'/gi);
    console.log(`remain local branches: ${remainBranch}`);
  }

  最后使用 zx 执行文件,就可以敲击下回车清空所有的本地分支了。

上一篇:vue2.0类似keep-alive的可控数据持久化的组件@anywo/vue-data-cache 2022-02-22


下一篇:Windows系统下,GoLand中选定Git Bash终端,中文乱码的解决对策