文章源于 lambdas.devgit
每当咱们想要建立一个基于 NodeJS 的命令行工具时,就会衍生出一堆问题须要解决,好比如何准备开发环境,如何打包转译代码,如何使代码在转译后保持可调用的状态同时尽量的压缩体积, 以及怎样设计项目分配 Command
与 Option
等等,这会浪费巨大的时间,并且并不是必定有成果。这时你能够注意到社区几乎全部的命令行工具都是自成一派, 并无严谨的框架或约定约束,也无所谓的最佳实践,这使想要特别是第一次想要开发命令行工具的开发者望而却步,或是几番努力最后却不尽如人意。github
举个例子来讲,腾讯的 omi
是一个有众多使用者的框架,但其命令行工具 omi
/ omi-cli
却让人贻笑大方。仅一些简单的下载和建立模板的任务,造出长篇大论的文件不说,下载 时依赖数千,包的体积巨大,总体项目毫无设计几乎是为所欲为、天马行空,这就是开发者自己并不擅长此道,只学会了 糊屎。(何谓 糊屎,参阅 JS 优雅指南 2)npm
func 的出现就是为了解决这些问题。func
自己的实现参阅了社区内诸多基于 NodeJS 的命令行工具的优秀实现,与流行的框架设计思路相结合,以优雅的设计、小体积、高性能 等为目标, 同时关注开发者体验,大幅度的提高了命令行工具项目的可扩展性与可读性,几乎是现在 NodeJS 社区中开发命令行工具的最优解。咱们能够尝试使用 func
构建一个命令行工具。c#
在之前流行的一些命令行参数解析的库中,咱们在构建项目前须要准备大量的脚本与配置,甚至还要解决文件权限、bin
、代码转译等等问题,但使用 func
,咱们能够仅经过一行命令 初始化项目:bash
npm init func
复制代码
项目初始化后进入文件夹,随机使用 npm install
或 yarn
安装依赖,如今就能够正式开发了。 能够注意到,func
的项目模板中为咱们准备了 start
与 build
2 个脚本,它们都是由 func-service
驱动的,帮助你一键切换开发与生产模式,咱们所要作的就是专一于 命令行逻辑自己,实现逻辑就够了。框架
咱们能够随意的建立一个类,当它被加上 Command
注解时这就是一个命令,而被加上 Option
注解时就会转变为一个选项:异步
import { Command } from 'func'
@Command({ name: 'test' })
export class Test {
}
复制代码
在命令行中运行 <YOUR NAME> test
时,Test
类将会被调用。选项也是同理。你能够在 constructor
中开始本身的代码,这和你在任何地方写 NodeJS 没有什么 不一样,当你以为差很少的时候,运行 npm build
就能够将它打包,一切就是这么简单。ide
有时候,当咱们须要使用到命令行携带的参数时,好比处理 <NAME> something params -option
这样复杂的输入,你能够直接在类中注入命令行参数,对,就是像 IoC 那样:函数
@Command({ name: 'test' })
export class Test {
constructor( private args: CommandArgsProvider, ) {
}
}
复制代码
CommandArgsProvider
其实是一个 class
类型,当你标记一个参数为此类型时,func
会在运行时为你注入全部的命令参数, 一样的也支持 OptionArgsProvider
、RegisterProvider
等等,你能够在 官方的文档 阅读它们的具体类型。工具
运行 npm build
能够获得一个打包后的文件,这是由 ncc
编译后的文件,一般它只有一个 (若是携带 extra 可能会有多个,但它们会自动连接), 同时为你连接好了 bin
,你要作的惟一一件事就是将包发布出去。为何 func
使用这种方式发布呢?
咱们知道当你在安装一个包或是使用 npx
执行包时 (这在使用命令行工具的人群中很常见),NPM 所花费的时间大约在 3 个部分,即对比包的依赖,下载包,执行。 首先咱们知道 func
的项目足够的小,可以大量介绍下载时间,同时也有足够好的性能,如今要解决的就是在大量依赖时的对比分析问题,将文件打包成单文件不依赖 外部环境时会极大的减小所需时间,若是你再将全部的依赖移入 devDependencies
中,几乎可以在一瞬间完成 分析 - 下载 - 运行 这三个步骤。这样的体验是不可思议的。 是的,这里推荐你把全部的依赖当作开发依赖处理,这彷佛违背了 NPM Package 的开发哲学,但在使用 func
构建命令行应用时这样作却大有裨益。
在运行 func
build 完成的包时,咱们注意到几乎无需任何依赖,这是由于在单个文件中已经 bundle
了全部的所需资源,也就意味着用户在运行 .js
文件时, 堆栈中真的就只有 .js
文件内的内容,不会引用其余,不会加载任何可有可无的东西。此时咱们也就无需用户关心 dependencies
,甚至能够移除它们,这样一来, 下载或即时运行时就直接跳过了 对比依赖版本 这一步,这其中省略了无数的请求也就会会极大的增长速度,npm init func
可以在 1 秒左右马上开始安装也是这样的道理。
如今你已经知道了怎样快速的构建一个合格且优雅的命令行工具,那怎样作的更好呢?一般来讲你须要遵循这几点:
不由于小功能引入巨大的包,不引入依赖爆炸的包。
举例来讲,download-git-repo
是一个很不错的包,它可以为你节约不少时间,但请注意它依赖了 download
,若是你仅为了下载单个文件或只有不多的下载需求时, 这就显得有些大材小用,download
会为你增长约 450 kb 的重量,却只作了一件你 5 分钟能够搞定的事情。一样你的用户也会为此付出巨大的时间代价。
不要显示错误堆栈信息。
在多数状况下咱们都须要尽量的显示堆栈或是引用的错误信息便于 debug,可是在命令行工具中这样作只会使你的用户很是困惑。这主要归结于命令行中不能很好高亮 的显示代码块,大量的代码信息会使用户不知所措。建议你始终构建一个错误处理模块来解决问题,同时为用户提供良好的反馈,最后能够提供相似于 --debug
的 选项让开发者调试。
不要太依赖同步操做。
在 NodeJS 与其社区流行的 I/O 库中,咱们一般会有异步和同步函数两种选择,如 readFile
与 readFileSync
,虽然同步函数能够为你节约一些开发时间, 但也会阻塞你的代码,不少状况下会有难以理解的问题。好比当你设置定时器显示一个 Loading 图标的同时操做了同步 API,那么你的 Loading 图标就会由于阻塞 而没法运动 (由于没法 render 到终端),或是你同时操做多个文件,同步的 API 会使你花费巨大的时间。
不要发布无用信息。
命令行工具不少时候的角色是充当复杂的脚本,性能和体验是相当重要的,发布无用的信息在你的 package 中会使下载时间更长。(使用 files
来约束发布的文件)
不要修改临时文件夹与配置区之外的信息。
对于命令行工具来讲,运行时的权限是巨大的,但不要所以弄脏用户的系统。你可使用 require('os').tmpdir()
获取用户操做系统的临时文件夹目录,不管什么时候, 你都拥有这里的写权限。