??根据github 上的数据,去年最受欢迎的前端库为谷歌棋下的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`;
??运行成功。
$`command`
??执行指令的两个飘折号`` 等同于ES6语法。你可以在内部直接使用${变量名} 来嵌入命令。
var log = 'log';
await $`git ${log}`;
??这种使用方法需要注意的是,zx会自动给以${变量名} 方式嵌入的内容加上括号,作为一个字符串。比如我要创建一个名为测试 测试 的文件夹。在 bash 中直接执行是会失败的,如下:
mkdir 测试 测试
mkdir '测试 测试'
??而使用zx 则直接可以执行成功:
let name = '测试 测试';
await $`mkdir ${name}`;
??除了这点,如果插入的变量是数组,zx 还会自动的解析数组,如下:
let flags = [
'--oneline',
'--decorate',
'--color',
];
await $`git log ${flags}`;
??但是这个特性也会带来麻烦。当你直接键入命令的时候,zx 会强行解析。
let demo = 'git log';
await $`${demo}`;
??此时可以利用 zx 会解析数组的特性来这样操作。
let demo = `git log`;
await $`${demo.split(' ')}`;
??$`command` 的返回值是一个继承Promise的类。可以追踪该命令的输入 ,输出 ,错误 ,退出码 。还提供了两个方法,pipe 和kill 。
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 则可以直接杀死进程。
await $`cat file.txt`.pipe(process.stdout);
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 ?');
??question 还接受第二个参数,可以支持用户在输入的时候,使用 tab 键来补全。
let mustBeYse = await question('Do you like me ?', { choices: ['yes'] });
服务进程与子进程
??zx 最棒的地方就在于他可以方便的开多个子进程。下面我愉快的起三个服务,三个服务的输出都会转到主进程。
$`npx serve`;
$`npx serve`;
$`npx serve`;
??此时如果我退出主进程,子进程的服务也会全部停止。
??在上文中,使用$`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 执行文件,就可以敲击下回车清空所有的本地分支了。
|