搭一个属于自己的脚手架

1.什么是脚手架 

 

脚手架用于快速生成新项目的目录模板,并集成一系列体系化工具的安装,无需自己从零开始一步步配置,减少copy操作,有效提升开发体验和效率,尽管这些脚手架非常优秀,但是未必是符合我们的实际应用的,所以我们可以定制一个属于自己的脚手架,来提升自己的开发效率。平时我们在开发React-Native的时候就会使用到raect-native-cli这个脚手架为我们创建项目

 

脚手架的作用 

 

  1. 减少重复性的工作,不需要复制其他项目再删除无关代码,或者从零创建一个项目和文件。 
  2. 可以根据交互动态生成项目结构和配置文件。 
  3. 多人协作更为方便,不需要把文件传来传去。 

 

目前比较主流的脚手架 

 

  • React-Native脚手架 react-native-cli 
  • React.js脚手架 cract-react-app 
  • Vue.js脚手架 vue-cli 
  • Webpack脚手架 webpack-cli 

 

2.实现思路 

 

  1. 项目模板放在github上 
  2. 用户通过命令交互的方式下载不同的模版 
  3. 经过模版引擎渲染定制项目模版 
  4. 模版变动,只需更新模版即可,不需要用户更新脚手架 

 

设计模块知识点 

 

  1. commander.js命令行工具 
  2. download-git-repo: 用来下载远程模板 
  3. inquirer: 交互式命令行工具 
  4. ora: 显示loading动画 
  5. chalk: 修改控制台输出内容样式 
  6. log-symbols: 显示出 √ 或 × 等的图标 

 

3.项目初始化 

 

这里假设我们的脚手架名字是qiyitest-cli,以下都用这个名字。我们在命令行使用脚手架命令为qiyitest-cli

1.创建一个空项目qiyitest-cli 

 

mkdir qiyitest-cli
cd qiyitest-cli
npm init -y 

 

2.安装相关的依赖 

 

npm install babel-cli babel-env chalk commander download-git-repo ini inquirer log-symbols ora 

 

3. 新建一个bin文件夹里面添加 index.js

行首加入一行 #!/usr/bin/env node 指定当前脚本由node.js进行解析 

 

#! /usr/bin/env node
console.log('hello demo') 

 

4.配置package.js中的bin字段 (node.js 内置了对命令行操作的支持,package.json 中的 bin 字段可以定义命令名和关联的执行文件)

 

{
  "name": "qiyitest-cli",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "bin": {
    "qiyitest": "bin/index.js"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "babel-cli": "^6.26.0",
    "babel-env": "^2.4.1",
    "chalk": "^4.0.0",
    "commander": "^5.1.0",
    "download-git-repo": "^3.0.2",
    "ini": "^1.3.5",
    "inquirer": "^7.1.0",
    "log-symbols": "^3.0.0",
    "ora": "^4.0.4"
  }
}

 

这样当我们发布上npm,别人下载下来后,就可以直接使用qiyitest命令了。

 

5.目录结构

├── bin
│   └── index.js        //可执行文件
├── .babelrc             //babel配置文件
├── package.json
├── README.md

 

6.执行npm link链接命令到全局(npm unlin移除命令) 

执行bin中配置的命令测试。 

例如在终端输入: 

 

npm link
qiyitest

输出 

hello demo 

 

4.处理命令行 

 

使用commander处理控制台命令(github查看如何使用), commander提供解析命令行

在index.js文件下面添加 

 

#! /usr/bin/env node

// 使用Node开发命令行工具所执行JavaScript脚本必须在顶部加入 #! /usr/bin/env node

const { program } = require('commander');

program.version('1.0.0') // -v 或者 --versions输出版本号

program
  .command('init <template> <project>')
  .description('初始化项目模版')
  .action((templateName, projectName) => {
    console.log(templateName, projectName)
  })

program
  .command('list')
  .description('查看所有可用的模版')
  .action(() => {
    console.log(
      `template-A A模板
      template-B B模板
      template-C C模板`
     
    )
  })

program.parse(process.argv);

 

在控制台输入

qiyitest -V(或者qiyitest --version)

qiyitest -h(或者 qiyitest --help)

qiyitest init template-A A

qiyitest list 

输出结果如下:

搭一个属于自己的脚手架

 

5. 准备模版 

 

在github上面建立需要使用到的模版,这里分别建立了template-A template-B template-C三个模版(https://github.com/dongtaotao/-template-C) 

 

添加下载模版 

 

当输入qiyitest init template-A a-name时候下载基于template-A模版进行初始化

当输入qiyitest init template-B b-name时候下载基于template-B模版进行初始化

当输入qiyitest init template-C c-name时候下载基于template-A模版进行初始化 

注:项目名可以随便取 

 

模版下载地址 

 

const templates = {
  'template-A' : {
    url: 'https://github.com/dongtaotao/template-A',
    downloadUrl: 'http://github.com:dongtaotao/template-A#master',
    description: 'A模版'
  },
  'template-B' : {
    url: 'https://github.com/dongtaotao/template-B',
    downloadUrl: 'http://github.com:dongtaotao/template-B#master',
    description: 'B模版'
  },
  'template-C' : {
    url: 'https://github.com/dongtaotao/template-C',
    downloadUrl: 'http://github.com:dongtaotao/template-C#master',
    description: 'C模版'
  },
};

 

6 根据init指定的模版名和项目名下载生成到本地 

 

download-git-repo 支持从 Github、Gitlab 下载远程仓库到本地。 

 

const download = require('download-git-repo');
program
  .command('init <template> <project>')
  .description('初始化项目模版')
  .action((templateName, projectName) => {
    
    const {downloadUrl} = templates[templateName];
    //download 
    // 第一个参数: 仓库地址
    // 第二个参数: 下载路径
    download(downloadUrl, projectName, {clone: true}, (err) => {
      if(err) {
        console.log('下载失败')
      } else {
        console.log('下载成功')
      }
    })
  })

 

在控制台输入qiyitest init template-A ademo

这时候就会下载对应的template-A模版,同时在桌面上多了一个ademo的文件。

文件ademo里面的内容就是template-A模版的内容 

 

搭一个属于自己的脚手架

 

download函数

 

  • 第一个参数就是仓库地址
  • 端口号后面的'/'在参数中要改成':'
  • master 代表分之名

不同的模版可以放在不同的分枝中,更改分之便可以实现下载不同的模版文件了

 

第二个参数是路径

 

  • 上面我们直接在当前路径下创建一个ademo的文件存放模版。

 

 

7 命令行交互 

 

命令行交互功能可以在用户执行init命令之后,向用户提出问题,接收用户输入并作出相应的处理,这里使用inquirer.js来实现

安装:

npm install inquirer

 

const inquirer = require('inquirer');

inquirer.prompt([
{
  type: 'inpute',
  name: 'name',
  message: '请输入项目名称'
}]).then((answers) => {
  console.log(answers)
})

 

  • 问题就放在prompt()中
  • 问题的类型为input就是输入类型
  • name就是作为答案对象中的key
  • message就是问题了
  • 用户输入的答案就在answers中

 

8.模板引擎

 

  • 我们通过询问交互后,肯定内部做了些改变。这里我们可以把package.json的作者和描述简单改了
  • 可以使用handlebars,模板语法简单

 

在模版中准备package.json文件

{
	  "name": "{{name}}",
	  "version": "1.0.0",
	  "description": "{{description}}",
	  "main": "index.js",
	  "scripts": {
	    "test": "echo \"Error: no test specified\" && exit 1"
	  },
	  "keywords": [],
	  "author": "{{author}}",
	  "license": "ISC",
	  "dependencies": {
	   
	  }
	}

 

并在下载模版完成只有将用户输入的答案渲染到package.json中

 

const { program } = require('commander');
const download = require('download-git-repo');
const handlebars = require('handlebars');
const inquirer = require('inquirer');
const ora = require('ora');
const logSymbols = require('log-symbols');
const chalk = require('chalk');
const fs = require('fs');


const templates = {
  'template-A' : {
    url: 'https://github.com/dongtaotao/template-A',
    downloadUrl: 'http://github.com:dongtaotao/template-A#master',
    description: 'A模版'
  },
  'template-B' : {
    url: 'https://github.com/dongtaotao/template-B',
    downloadUrl: 'http://github.com:dongtaotao/template-B#master',
    description: 'B模版'
  },
  'template-C' : {
    url: 'https://github.com/dongtaotao/template-C',
    downloadUrl: 'http://github.com:dongtaotao/template-C#master',
    description: 'C模版'
  },
};


//qiyitest init template-A a-name基于template-A模版进行初始化
//qiyitest init template-B a-name基于template-A模版进行初始化


program.version('1.0.0') // -v 或者 --versions输出版本号


program
  .command('init <template> <project>')
  .description('初始化项目模版')
  .action((templateName, projectName) => {
    // 下载之前做loading提示
    const spinner = ora('正在下载模版...').start()
    
    const {downloadUrl} = templates[templateName];
    //download 
    // 第一个参数: 仓库地址
    // 第二个参数: 下载路径
    download(downloadUrl, projectName, {clone: true}, (err) => {
      if(err) {
        spinner.fail();
        console.log(logSymbols.error, chalk.red(err))
        return;
      }
      spinner.succeed(); // 下载成功提示
      // 把项目下的package.json文件读取出来
      // 使用向导的方式采集用户输入的数据解析导
      // 使用模板引擎把用户输入的数据解析到package.json 文件中
      // 解析完毕,把解析之后的结果重新写入package.json wenjianzhong 
      inquirer.prompt([
        {
          type: 'inpute',
          name: 'name',
          message: '请输入项目名称'
        },
        {
          type: 'inpute',
          name: 'description',
          message: '请输入项目简介'
        },
        {
          type: 'inpute',
          name: 'author',
          message: '请输入作者名称'
        }
    ]).then((answers) => {
      const packagePath = `${projectName}/package.json`
      const packageContent = fs.readFileSync(packagePath, 'utf8')
      const packageResult = handlebars.compile(packageContent)(answers);
      fs.writeFileSync(packagePath, packageResult)
      console.log(chalk.yellow('初始化模版成功'))
    })


    })
  })
program.parse(process.argv);

 

这里使用了node.js的文件模块fs, 将handlebars渲染后的模版重新写入到文件中

 

搭一个属于自己的脚手架

这时候打开demob 文件夹下面的package.json,可以看到里面的name,description,author变成我们输入的

 

搭一个属于自己的脚手架

 

9.视觉美化

 

在用户输入答案之后,开始下载模版,这时候使用ora来提示用户正在下载中。

 

安装:

npm install ora

 

使用

const ora = require('ora');

// 开始下载
const spinner = ora('正在下载模版...');
spinner.start();

// 下载失败
spinner.fail();

// 下载成功
spinner.succeed();

 

也可以使用chalk和logSymbols增加文本样式

chalk

 

这是用来修改控制台输出内容样式的,比如颜色啊,具体用法如下:

const chalk = require('chalk');
console.log(chalk.green('success'));
console.log(chalk.red('error'));

然后通过chalk来为打印信息加上样式,比如成功信息为绿色,失败信息为红色,这样子让用户更容易分辨,同事也让终端的显示更加好看。

这时候在控制台输入命令可以看出带了图标和颜色

 

搭一个属于自己的脚手架

 

10.npm 发包

 

  1. 打开npmjs.com官网
  2. 注册一个npm账号
  3. 在npm检索是否有重名的包
  4. 将package.json中的name修改发不到npm上的包名
  5. 打开控制台,执行npm login,在控制台登录npm
  6. 登录成功后,在项目下执行npm publish发布
  7. 发布成功,就可以在本地安装测试了
npm install -g qiyitest-cli

 

这时候就可以使用qiyitest命令了,至此一个简单的脚手架搭建过程结束了

 

项目地址

源码