从零开始搭建本身的前端脚手架(一)

(如下阅读将花费10分钟)javascript

前言

平常开发中,咱们都只专一在业务上的开发,拿起一套开箱即用的模板项目就直接开搞了,不知道你们有没有思考过,平时咱们使用的脚手架里面到底作了什么,而且若是是本身来搭一套脚手架,应该怎么去搭呢? css

在本博客中,将记录做者搭建脚手架的过程,总体将分为两个部分,第一部分是cli的搭建,第二部分是模板项目的搭建;java

cli的搭建

在搭建脚手架cli以前,咱们首先思考一下,一个cli,须要什么能力呢?答案是初始化能力,那么初始化的功能须要怎样去实现呢,这里,咱们先梳理一下思路。node

思路

咱们在使用其余cli时,会发现,它们有问询的功能,好比询问项目名称,项目描述等;而后还能够选择模板项目将要使用什么css预处理器等的问题;因此,cli首先就要具有问询功能,用以获取定制化信息;
问询结束后,咱们的cli会得到即将建立的项目的基本信息,接下来咱们就须要以一个模板项目做为模板去建立,模板项目将会在第二部分讲解,这里一笔带过~;OK,既然有了模板项目,cli就须要下载这个项目,而后复制这个项目,同时将前面问询所得的定制化信息写入项目配置中,因此cli还须要有下载复制,写入模板功能;
到此,一个模板项目基本已经建立成功了,那后面咱们还但愿脚手架能够帮忙进行git初始化以及安装依赖的功能,因此最终,还须要添加git初始化安装依赖的功能;
总结一下,实现一个cli的初始化功能咱们须要有以下能力:
问询 ==> 下载模板 ==> 复制,写入模板 ==> git初始化 ==> 安装依赖git

准备

好了,为了实现上述思路,cli将会引入以下依赖去实现对应的功能:github

const program = require('commander');  // commander负责读取命令
const inquirer = require('inquirer');   // inquirer负责问询
const download = require('download-git-repo');   // download-git-repo负责下载对应模板项目的git仓库
const fse = require('fs-extra');   // fs-extra负责文件的复制
const memFs = require('mem-fs');
const editor = require('mem-fs-editor');   // mem-fs-editor负责模板的复制以及嵌入模板字符串,它须要依赖mem-fs
const { exec } = require('child_process');   // child_process负责执行命令行

固然除了上述必须的依赖外,为了更好的交互体验,还引入了以下依赖:shell

const chalk = require('chalk');   // 改变命令行输出样式
const ora = require('ora');   // 一个优雅地命令行交互spinner

至此,准备的工做已经完毕,下面,就开始一步步来编写cli了npm

cli的编写

万事开头难,有了思路后,实现思路才是真正的开始,下面将介绍如何组织cli项目json

项目组织

首先看一下,工程的组织数组

├── bin                   
|   ├── zero
|   ├── zero-init 
├── src                   
|   ├── constants.js     
|   ├── project.js         
|   └── utils.js              
├── .gitignore                   
├── .npmrc
├── README.md
└── package.json

第一步,解析命令

bin/zero中引入commander,而且声明init命令,commander会在同级目录中寻找zero-init文件

const program = require('commander');

program
  .usage('<command> [options]')
  .command('init [name]', 'init a project')
  .parse(process.argv);

第二步,问询

有了第一步的解析后,咱们能够在zero-init文件中编写建立逻辑,这里,咱们再抽象project文件到src目录底下,以便更好地分离职责,bin文件只负责解析命令就好啦~
下面就看看project文件中,如何实现问询

inquirer.prompt([{
      type: 'input',
      name: 'projectName',
      message: '请输入项目名:',
      validate(input) {
        if (!input) {
          return '项目名不能为空';
        }
        if (fse.existsSync(input)) {
          return '当前目录已存在同名项目,请更换项目名';
        }
        return true;
      }
    }]);

inquirer提供prompt函数来实现问询,其参数为数组,问询顺序将按照数组的顺序来进行;

第三步,下载仓库

在问询结束后,咱们基本知道须要建立的项目的名称、描述等信息了,下一步,就是下载仓库了,下面咱们来看看核心代码:

const downloadPath = path.join(projectPath, '__download__');
download(TEMPLATE_GIT_REPO, downloadPath, { clone: true }, (err) => {
  // 拷贝
  // 此处省略若干代码
  // 拷贝完成后删除临时文件
  fse.remove(downloadPath);
}

这里须要注意的是,模板工程所在的仓库,即TEMPLATE_GIT_REPO,最好是public的

第四步,复制文件,将信息写入模板

下载仓库只是把模板工程存放在一个临时文件夹内,真正的工程文件须要等信息写入模板后再生成;
这里须要注意的是,模板写入时遵循ejs规范

const memFs = require('mem-fs');
const editor = require('mem-fs-editor');

// 这里须要mem-fs进行内存优化
const store = memFs.create();
this.memFsEditor = editor.create(store);

// 这里source表示源文件,dest表示目标文件,data表示须要写入的数据
this.memFsEditor.copyTpl(
  source,
  dest,
  data
);

在调用copyTpl时,data字段中的key-value将被写入到模板中,举个栗子:

// 模板文件中,字段是这样的
{
  name: "<%= projectName %>"
}

// data就要这样去写
{
  projectName: 'this is your project name'
}

第五步,进行git init和npm install

这两步的核心都是使用nodejs提供的child_process中exec方法去执行命令来实现,exec能帮助咱们在命令行中执行shell命令,exec函数有对应的回调方法来让咱们判断命令执行是否成功,核心代码以下:

exec('npm install', (error, stdout, stderr) => {
  if (error) {
    // 出错了
  } else {
    // 成功了
  }
})

是否是很简单呢,想了解更多的操做能够搜索官方文档查看哈,这里就不详解了。

调试和发布

到这里,咱们的cli已经实现的差很少了,这里咱们总结一下如何测试和发布吧

npm link  // 本地调试
npm publish  // 发布

经过npm link命令,便可以在命令行工具测试你的cli了,注意在package.jsonbin字段中定义好入口命令和文件

// package.json
{
  "bin": {
    "zero": "bin/zero"
  }
}

小结

OK,文章主要总结了编写一个脚手架中init功能的思路以及一些实现的方法,具体一些细节仍有优化的空间,欢迎你们讨论!
文章的具体代码能够参考个人仓库zero-cli

相关文章
相关标签/搜索