脚手架的本质就是帮助开发者减少重复性工作,提升开发效率
许多时候,我们在日常开发中都有现成的cli可以使用,像vue的vue create hello-world,react的npx create-react-app my-app,大部分公司也都设计了符合自己业务的脚手架。
这些脚手架的最终目的都是帮助开发者减少低级重复劳动,专注业务提高开发效率。
在开始开发一个脚手架前,我们需要先来了解下几个常用的库
- chalk 可以在终端显示颜色
- commander 提供了命令行输入和参数解析,简化命令行开发
- inquirer 交互式命令行工具,用来收集用户填入表单
- ora 终端加载动画效果,增加趣味性
- shelljs 通过在代码中编写shell命令实现功能
- puppeteer 主要用来启动无头浏览器生成网站缩略图
- download-git-repo 用来下载远程模板
目标
预计通过这个脚手架可以做到以下几点:
// 初始化一个项目
llscw create project-name
// 初始化配置信息
llscw init
开始搭建
1. 初始化 cli
接下来我们开始从头来搭建一个脚手架
mkdir llscw-cli && cd llscw-cli
npm init -y
我们想要的情况是在我们运行llscw命令的时候,执行对应的 node 文件,因此我们可以在package.json文件中指明bin来配置入口:
{
"name": "-llscw-react-cli",
"version": "1.0.1",
"description": "llscw cli",
"private": false,
"main": "index.js",
"bin": {
"llscw": "./bin/index.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/lqt0327/-llscw-react-cli.git"
},
"keywords": [],
"author": "",
"license": "ISC"
}
对应的./bin/index.js文件中,需要写入以下声明:
#!/usr/bin/env node
上面这段声明的意思是,指定这个脚本文件采用node执行,而/usr/bin/env的作用是告诉系统可以在PATH目录中查找。配置#!/usr/bin/env node的目的就是为了解决不同用户node路径不同的问题,让系统动态的去查找node来执行你的脚本文件。
接下来我们来测试一下我们的脚手架,在当前目录执行npm link来增加一个软连接。
再在我们的package.json文件中增加一行命令:
"scripts": {
"llscw": "llscw"
}
这样,一个简单的cli就完成了。
2. create 模版、组件
我们接下来先实现一个创建模版的功能。这里就需要用到上面提到的commander库来进行命令行输入参数的解析:
program
.command('create [template]')
.description('生成 llscw 模板')
.action(function(template){
generate(template);
});
通过上面这段代码,我们就可以实现通过命令llscw create 模版名称来从远程git仓库里拉取我们事先编辑好的项目的代码。
不过在那之前,还需要将generate函数进行完善。
const path = require('path');
const fs = require('fs');
const chalk = require('chalk');
const download = require('download-git-repo');
const ora = require('ora');
async function generate(name) {
const config = await getTemplateName();
const targetDir = path.join(targetRootPath, name);
//创建新模块文件夹
fs.mkdirSync(targetDir);
readAndCopyFile()
console.log(chalk.green(`生成模板 "${name}" 完成!`));
}
async function readAndCopyFile() {
const spinner = ora('开始下载模版...').start();
await downLoadTemplate(`github:lqt0327/react-template#main`, name, true, spinner);
spinner.succeed('模版下载完成');
console.log();
console.info('初始化文件配置信息...');
console.log();
console.log(chalk.green(`你的项目 ${name} 已创建成功!`));
console.log();
}
async function downLoadTemplate(repository, projectName, clone, spinner) {
await new Promise((resolve, reject) => {
download(
repository,
projectName,
{
clone
},
(err) => {
if (err) {
spinner.fail()
return reject(err)
}
resolve();
}
);
});
}
在上面的代码中,我们通过download-git-repo这个npm包来拉取远程仓库的代码,并通过ora和chalk来处理交互信息。
这样一来,我们就可以尝试通过llscw create project-name命令来初始化一个项目了。
3. 初始化配置文件
我们接下来先实现一个初始化配置文件的功能。初始化配置工作的目的是对一些不是用llscw create创建的项目,进行配置文件的初始化工作,这里同样需要用到上面提到的commander库来进行命令行输入参数的解析:
program
.version(pkg.version,'-v, --version')
.command('init')
.description('初始化 llscw config 配置文件')
.action(initial);
然后来完善initial函数,通过检测当前目录下是否已经存在llscw.config.js来给用户进行是否覆盖的提示:
const path = require('path');
const fs = require('fs');
const chalk = require('chalk');
const figlet = require('figlet');
const inquirer = require('inquirer');
function copyLlscwConfigJS(){
figlet('llscw cli', function(err, data) {
if(err){
console.log(chalk.red('Some thing about figlet is wrong!'));
}
console.log(chalk.yellow(data));
let targetFilePath = path.resolve('llscw.config.js');
let templatePath = path.join(__dirname,'../tpl/llscw.config.js');
let contents = fs.readFileSync(templatePath,'utf8');
fs.writeFileSync(targetFilePath,contents,'utf8');
console.log(chalk.green('初始化配置成功 \n'));
process.exit(0);
});
}
module.exports = function(){
// 配置文件如果存在则提示是否覆盖
if(fs.existsSync(path.resolve('llscw.config.js'))){
// 连续提问
inquirer.prompt([
{
name:'init-confirm',
type:'confirm',
message:`llscw.config.js 已经存在,是否覆盖?`,
validate: function(input){
if(input.lowerCase !== 'y' && input.lowerCase !== 'n' ){
return 'Please input y/n !'
}
else{
return true;
}
}
}
])
.then(answers=>{
if(answers['init-confirm']){
copyLlscwConfigJS();
}
else{
process.exit(0);
}
})
.catch(err=>{
console.log(chalk.red(err));
})
}
else{
copyLlscwConfigJS();
}
};
通过figletnpm包来在终端中展示自定义的签名,这段代码核心就是通过node向本地写入脚手架对应的配置文件。
到此,一个简易的脚手架完成了,可以看到,脚手架本质其实就是把你编写好的项目模版,在需要使用的时候方便的copy下来,避免每次重新搭建或手动复制粘贴啥的
项目git地址:github.com/lqt0327/-ll…
上面这些代码是我早期入门写的,在草稿箱里搁了半年多了2333,比较简单粗糙,不过想了想,也算是自我成长的记录,而且简单点才适合初学者看嘛,就不重写了
后期完善项目,git地址:github.com/lqt0327/-ll…
集成了以下脚手架(持续迭代完善中……):
- vue2 预渲染
- react17 同构
- react17 + router v6
感兴趣的可以先看看,后期也会单独沉淀相关文章