1.什么是脚手架
脚手架用于快速生成新项目的目录模板,并集成一系列体系化工具的安装,无需自己从零开始一步步配置,减少copy操作,有效提升开发体验和效率,尽管这些脚手架非常优秀,但是未必是符合我们的实际应用的,所以我们可以定制一个属于自己的脚手架,来提升自己的开发效率。平时我们在开发React-Native的时候就会使用到raect-native-cli这个脚手架为我们创建项目
脚手架的作用
减少重复性的工作,不需要复制其他项目再删除无关代码,或者从零创建一个项目和文件。
可以根据交互动态生成项目结构和配置文件。
多人协作更为方便,不需要把文件传来传去。
目前比较主流的脚手架
React-Native脚手架 react-native-cli
React.js脚手架 cract-react-app
Vue.js脚手架 vue-cli
Webpack脚手架 webpack-cli
2.实现思路
项目模板放在github上
用户通过命令交互的方式下载不同的模版
经过模版引擎渲染定制项目模版
模版变动,只需更新模版即可,不需要用户更新脚手架
设计模块知识点
commander.js命令行工具
download-git-repo: 用来下载远程模板
inquirer: 交互式命令行工具
ora: 显示loading动画
chalk: 修改控制台输出内容样式
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 发包
打开npmjs.com官网
注册一个npm账号
在npm检索是否有重名的包
将package.json中的name修改发不到npm上的包名
打开控制台,执行npm login,在控制台登录npm
登录成功后,在项目下执行npm publish发布
发布成功,就可以在本地安装测试了
npm install -g qiyitest-cli
这时候就可以使用qiyitest命令了,至此一个简单的脚手架搭建过程结束了
项目地址
源码
原文链接:https://blog.youkuaiyun.com/sinat_17775997/article/details/109226966