编程领域的脚手架主要为了完成新项目的启动和搭建,能够帮助开发者提升效率和开发体验。对于前端来说,从零开始建立一个项目是复杂的,因此也就存在了较多类型的脚手架:
-
Vue/React 框架类脚手架
-
Webpack 等构建配置类脚手架
-
混合脚手架,比如大家熟悉的 Vue-cli 或者 create-react-app
这一讲我们就深入这些脚手架的原理进行讲解。
命令行工具原理和实现
比如 Webpack、Babel、npm、Yarn 等都是典型的命令行。此外,流畅的命令行能够迅速启动一个脚手架,实现自动化和智能化流程。这一部分,我们就使用 Node.js 来开发一个命令行。
我们先来看几个开发命令行工具的关键依赖。
-
、、:可以处理复杂的用户输入,完成命令行输入交互。
-
、:使终端可以输出彩色信息文案。
-
:可以让命令行出现好看的 Spinners。
-
:可以在命令行中画出 Boxes 区块。
-
:可以在命令行中画出进度列表。
-
、:可以进行基础的命令行参数解析。
-
、:可以进行更加复杂的命令行参数解析。
我们的目标是支持以下面这种启动方式,建立我们的项目,如下代码:
npm 6.1 及以上版本,我们都可以使用或来启动我们的项目,比如下面两个命令就是等价的:
启动命令行项目
下面开始进入开发,首先我们创建项目:
接着进入文件中,创建目录及文件,文件内容如下:
接下来,为了使我们的命令行可以在终端执行,我们新建目录,并在其下创建一个文件,代码为:
上述代码中,我们使用了 模块,这样就可以在其他文件中使用关键字,即 ESM 模块规范了。我们在该入口文件中,引入并将命令行参数传给函数执行。
当然,为了能够正常使用 模块,我们需要先安装,执行。
此时 package.json 内容如下:
这里需要注意的是 字段,我们注册了两个可用命令:一个是带有 npm 命名 scope 的,一个是常规的命令。
为了调试方便,我们使用命令进行调试,在终端中项目目录下执行:
上述命令可以在全局范围内添加一个软链到当前项目中。我们执行:
就会得到下面这样的输出:
该输出,就对应了代码中的。
解析处理命令行输入
在解析处理命令行输入之前,我们需要设计命令行支持的几个选项,如下。
-
:支持默认的几种模板类型,用户可以通过 select 进行选择。
-
:等同于去创建一个新的 Git 项目。
-
:支持自动下载项目依赖。
-
:跳过命令行交互,直接使用默认配置。
我们利用使得命令行支持用户交互,同时使用来解析命令行参数,安装相关依赖命令:
接下来编写命令行参数解析逻辑,在中添加:
上述代码很好理解,我已经加入了相关注释。接下来,我们实现使用默认配置和交互式配置选择逻辑,如下代码:
这样一来,我们就可以获取到类似:
相关的配置了。
下面我们需要完成下载模板到本地的逻辑,我们事先准备好两种名为和的模板,并将相关的模板存储在项目的根目录中。当然你在实际开发应用中,可以内置更多的模板。
我们使用包实现跨平台递归拷贝文件,使用做个性化输出。安装相关依赖如下:
在目录下,创建新的文件,代码如下:
接下来,我们需要完成的初始化以及依赖安装工作,这时候需要用到以下内容。
-
:允许开发中使用类似的外部命令。
-
:使用或安装依赖。
-
:给出当前进度 progress。
执行安装依赖:
更新为:
这样一来,我们的命令行就大功告成了。
接下来我们主要谈谈模板维护问题,上述实现中,模板维护在了本地。为了更大范围的合作,模板可以共享到 GitHub 中。我们可以在 package.json 文件中声明 files 字段,如下所示:
以此来声明哪些文件可以被出去。
另外一种做法是将模板单独维护到一个 GitHub 仓库当中。在创建一个项目时,我们使用 download-git-repo来下载模板。
从命令行到万能脚手架
前面我们分析了一个命令行的实现和开发原理,这些内容并不复杂。但如何从一个命令行升级到一个万能脚手架呢?我们继续探讨。
使用命令行启动并创建一个基于模板的项目只能说是一个脚手架的雏形。对比大家熟悉的、、、等,我们还需要从可伸缩性、用户友好性方面考虑:
-
如何使模板支持版本管理
-
模板如何进行扩展
-
如何进行版本检查和更新
-
如何自定义构建
下面我们分别来讨论。
模板支持版本管理可以使用 npm 维护模板,这样借助 npm 的版本管理,我们可以天然地支持不同版本的模板。当然在脚手架的设计中,要加入对版本的选择和处理。
如前文所说,模板扩展可以借助中心化手段,集成开发者力量,提供模板市场。这里需要注意的是,针对不同模板或功能区块的可插拔性是非常重要的。下面我们会具体展开。
我们具体来看,使用脚手架初始化一个项目的过程,本质是根据输入信息进行模板填充。比如,如果开发者选择使用 TypeScript 以及英语环境构建项目,并使用 rollup 进行构建。那么核心流程中在初始化 rollup.config.js 文件时,我们读取 rollup.js.tmpl,并将相关信息(比如对 TypeScript 的编译)填写到模板中。
类似的情况还有初始化 .eslintrc.ts.json、package.json、CHANGELOG.en.md、README.en.md,以及 doc.en.md 等。
所有这些文件的生成过程都需要可插拔,更理想的是,这些插件是一个独立的运行时。因此我们可以将每一个脚手架文件(即模板文件)的初始化视作一个独立的应用,由命令行统一指挥调度。
比如 jslib-base 这个库对于 rollup 构建的处理,支持开发者传入 option,由命令行处理函数,结合不同的配置版本进行自定义分配。具体代码如下:
}
. = {
init: init,
}
如上代码,根据用户输入,使用了不同版本的 rollup 构建内容。
相信你了解了这些内容,对于实现一个自己的 create-react-app、vue-cli 会更有心得和启发。
总结
这一讲我们从开发一个命令行入手,分析了实现一个脚手架的方方面面。实现一个企业级脚手架需要不断打磨和优化,不断增强用户体验和可操作性,比如处理边界情况、终端提示等。更重要的是,对构建逻辑的抽象和封装,根据业务需求,不断扩展命令和模板。
本讲内容总结如下:
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/d445c3d802cdcf39218c606ec2a12976.png#pic_center)精选评论
*哲:
执行 npm link 时出错:npm ERR! path /usr/local/lib/node_modules/create-project/bin/create-projectnpm ERR! errno -2npm ERR! enoent ENOENT: no such file or directory, chmod ‘/usr/local/lib/node_modules/create-project/bin/create-project’
讲师回复:
从报错来看,是找不到该路径对应的文件,先确认一下改路径下是否有文件,再看一下是否有创建文件的权限
*斌:
有 github repo 吗?