手把手教你创建脚手架(一)

3,731 阅读5分钟

这篇文章试着聊明白一些看起来挺复杂的东西。在聊之前,大家要始终记得一句话:一切前端概念,都是纸老虎。

脚手架是我们经常使用的工具,也是团队提效的重要手段。所以系统性的掌握脚手架相关知识,对前端开发者来说是非常重要的,即使很多人今后不一定都会参与到各自部门或者公司的基建工作,但是系统性掌握好这个技能也可以方便我们后期的源码阅读。下面就一起来了解一下吧。

1. 前置知识

在搭建我们的脚手架之前,有一些常用的库,我们必需先进行一个简单了解。当然node的知识也是最基础的,如果不懂node的一些写法,可以先从node的api文档开始然后再来看我们这个教程。这里罗列了在开发脚手架常用的工具库,这里就不展开介绍了,大家可以自行了解。

名称简介
commander命令行自定义指令
inquirer命令行询问用户问题,记录回答结果
chalk控制台输出内容样式美化
ora控制台 loading 样式
download-git-repo下载远程模版
fs-extra系统fs模块的扩展,提供了更多便利的 API,并继承了fs模块的 API
cross-spawn支持跨平台调用系统上的命令
figlet控制台打印 logo

我们先给脚手架起一个名字,暂且就叫 memo-cli 吧。第一期我们先实现一个简单的脚手架工具,方便小伙伴们入门。因此需要实现的基本功能:

  1. 通过 memo-cli create 命令启动项目
  2. 询问用户需要选择需要下载的模板
  3. 远程拉取模板文件

接下来我们将一步步开始实现这些功能

2. 脚手架搭建步骤 🏗

首先我们先新建一个空文件夹,然后再初始化我们的项目并且新建入口文件

$ mkdir  memo-cli 
$ cd  memo-cli
$ npm init # 生成 package.json 文件
$ mkdir lib & cd lib & touch cli.js

在 package.json 文件中指定入口文件为 cli.js 👇

{
    ...
  "bin": {
    "memo-cli": "./lib/cli.js" // 手动添加入口文件为 cli.js
  }
}

npm link 链接到全局,这样本地就能使用 memo-cli 命令了

$ npm link

最后我们的结构就是这样的

memo-cli           
├─ lib                
│  └─ cli.js  # 启动文件               
└─ package.json  

2.1 创建脚手架启动命令

首先我们需要要想明白我们要做什么?借助 commander 依赖去实现这个需求,参照 vue-cli 常用的命令有 create。如果创建的项目存在,需要提示是否覆盖。

  1. 安装依赖

$ npm install commander --save

  1. 创建命令,打开cli.js进行编辑
#! /usr/bin/env node
const program = require('commander')
const packageJson = require("../package.json")

program
  .version(packageJson.version)
  .command("create <app-name>")
  .description("create a new project")
  .option("--f, --force", "overwrite target directory if it exist") // 是否强制创建,当文件夹已经存在
  .action((name, options) => {
    console.log(name, options)
  })
// 拿到命令行参数
program.parse(process.argv)

在命令行输入memo-cli,检查一下命令是否创建成功,出现这样的图片就到代表成功了 avatar 我们也可以校验我们的命令memo-cli create my-app ,在控制台中会打印my-app 代表成功拿到命令行输入信息 👍

2.2 执行命令

创建 lib 文件夹并在文件夹下创建 create.js

// lib/create.js

module.exports = async function (name, options) {
  // 验证是否正常取到值
  console.log('>>> create.js', name, options)
}

在 cli.js 中使用 create.js

// bin/cli.js
......
program
......
  .action((name, options) => {
    // 在 create.js 中执行创建任务
    require('./create.js')(name, options)
  })
......

执行一下 memo-cli create my-project,此时在 create.js 正常打印了我们出入的信息

在创建目录的时候,需要思考一个问题:目录是否已经存在?

  1. 如果存在

当 { force: true } 时,直接移除原来的目录,直接创建 当 { force: false } 时 询问用户是否需要覆盖

  1. 如果不存在,直接创建

这里用到了 fs 的扩展工具 fs-extra,先来安装一下

$ npm install fs-extra --save

我们接着完善一下 create.js 内部的实现逻辑

// lib/create.js
module.exports = async function (name, options) {
  // 执行创建命令
  // 当前命令行选择的目录
  const cwd  = process.cwd();
  // 需要创建的目录地址
  const targetAir  = path.join(cwd, name)
  // 目录是否已经存在?
  if (fs.existsSync(targetAir)) {
    // 是否为强制创建?
    if (options.force) {
      await fs.remove(targetAir)
    } else {
      // TODO:询问用户是否确定要覆盖
    }
  }
}

至此我们可以判断当前目录下有无重复的文件夹,有的话会直接与移除。接下来我们需要去询问用户是否需要覆盖已有的文件夹。

2.3 询问用户问题获取创建所需信息

这里召唤我们的老朋友 inquirer,让他来帮我们解决命令行交互的问题 接下来我们要做的:

  1. 一步遗留:询问用户是否覆盖已存在的目录
  2. 用户选择模板
  3. 用户选择版本
  4. 获取下载模板的链接

首选来安装一下 inquirer

$ npm install inquirer --save

然后询问用户是否进行 Overwrite

create 的代码可参考 create

大家可以自测一下,比如在项目文件夹下手动新建一个my-app的项目,再用脚手架新建memo-cli create my-app 在命令行就会显示下面的命令: avatar

2.4 获取模版信息

我们在 lib 目录下创建一个 http.js 专门处理模板和版本信息的获取,通过axios去获取github上的模版。

获取模版的代码 : (http)[git.xiaojukeji.com/kledwu/memo…]

2.5 用户选择模版

有了模版信息之后,我们专门新建一个 Generator.js 来处理项目创建逻辑 这块的代码可参考 Generator

测试一下,看看现在是个什么样子 avatar

avatar

2.6 下载远程模板

下载远程模版需要使用 download-git-repo 工具包,实际上它也在我们上面列的工具菜单上,但是在使用它的时候,需要注意一个问题,就是它是不支持 promise的,所以我们这里需要使用 使用 util 模块中的 promisify 方法对其进行 promise 化

$ npm install download-git-repo --save

下载远程模版代码地址 Generator

完成这块,一个简单的脚手架就完成了 ✅

来试一下效果如何,执行 memo-cli create my-project

avatar 这个时候,我们就可以看到模板就已经创建好了 👏👏👏

代码参考 gitlab仓库链接

第一阶段只是简单的介绍下脚手架如何响应用户输入和一些常用库的使用,在这第二阶段非常有帮助,后续会在这基础上结合webpack再给大家分享一些比较深入的内容。比如脚手架是如何对项目进行打包的,并且如何定制一些脚手架的命令,比如memo-cli build