脚手架: 一个用来快速搭建前端项目的工具,并基础的实现代码规范和 文件目录格式的统一。
目前常见的脚手架有 vue-cli
、create-react-app
等,他们都是通过简单的命令,拉取对应的项目模板,完成内容的快速构建。
今天先研究一下脚手架搭建的基础知识。
创建脚手架文件夹
mkdir test-cli
初始化文件夹
npm init -y
创建所需文件
README.md
.gitignore
- bin文件夹(存放脚手架入口文件)
- lib文件夹(存放脚手架不同命令的不同操作函数)
修改package.json
// 添加bin字段,值为脚手架的入口文件路径
"bin": "./bin/cli.js"
开启本地调试
npm link
npm link是一种把包链接到包文件夹的方式,即:可以在不发布npm模块的情况下,调试该模块,并且修改模块后会实时生效,不需要通过npm install进行安装
编写脚手架启动命令
安装命令行工具
npm install commander --save
自定义脚手架命令
./bin/cli.js
const commander = require('commander');
// 实例化一个命令行
const program = new commander.Command();
// create指令
program
.command('create <app-name>')
.description('create a new project')
.option('-f, --force', 'overwrite target directory if it exist')
.action((name, options) => {
require('../lib/create')(name, options);
console.log('name: ', name, '\r\n', 'options: ', options)
})
// 基础提示
program
.version(`v${reuqire('../package.json').version}`)
.usage('<command> [option]')
program.parse(process.argv);
console.log('test cli working ~~')
定义create命令执行函数
npm install fs-extra log-symbols --save
./lib/create.js
const path = require('path');
const fs = require('fs-extra');
// 给用户提示
const symbols = require('log-symbols');
module.exports = async function(name, options) {
// 获取当前路径
const curPath = process.cwd();
// 拼接用户传入项目名
const dirPath = path.join(curPath, name);
// 判断是否已存在同名文件夹
const dirExist = fs.existsSync(dirPath);
if (dirExist) {
// 如果已存在
// 判断用户是否有传入强制执行option、
if (options.force) {
// 如果传入
// 删除已存在文件
await fs.removeSync(dirPath);
// 创建新文件夹
await fs.mkdirSync(dirPath);
} else {
// 没有传入,提醒用户已存在相同文件
console.log('当前路径下,已存在与项目同名文件夹,请检查后重试')
}
} else {
// 创建新文件夹
await fs.mkdirSync(dirPath);
}
}
命令行提示样式美化
// chalk 修改命令行提示文字颜色
// figlet 艺术字效果
npm install chalk@4.0.0 figlet --save
命令行交互
npm install inquirer --save
./lib/inquirerPrompt.js
const inquirer = require('inquirer');
module.exports = async function inquirerPrompt(argv) {
return new Promise((resolve, reject) => {
inquirer.prompt([
{
type: 'input',
name: 'name',
message: 'project name',
default: name,
},
{
type: 'input',
name: 'version',
message: 'project version',
default: '1.0.0'
},
{
type: 'input',
name: 'author',
message: 'project author',
default: ''
},
{
type: 'list',
name: 'frame',
message: 'choose frame type',
choices: ['vue', 'react', 'express', 'nest']
}
]).then(aws => {
const { frame } = aws;
if (frame === 'vue') {
inquirer.prompt([
{
type: 'list',
name: 'ui-lib',
message: 'choose ui lib',
choices: ['Element']
},
]).then(aws2 => {
resolve(aws2);
}).catch(err => {
reject(err);
})
} else if (frame === 'react') {
inquirer.prompt([
{
type: 'list',
name: 'ui-lib',
message: 'choose ui lib',
choices: ['Antd']
}
]).then(aws2 => {
resolve(aws2);
}).catch(err => {
reject(err);
})
}
})
})
}
将方法引入create.js
中
// ...
const inquirerPrompt = require('./inquirerPrompt');
module.exports = async function(name, options) {
// ...
if (dirExist) {
// 如果已存在
// 判断用户是否有传入强制执行option、
if (options.force) {
// 如果传入
// 删除已存在文件
await fs.removeSync(dirPath);
// 创建新文件夹
await fs.mkdirSync(dirPath);
// new start --------------
inquirerPrompt({
name,
...options
})
// new end ----------------
} else {
// 没有传入,提醒用户已存在相同文件
console.log('当前路径下,已存在与项目同名文件夹,请检查后重试')
}
} else {
// 创建新文件夹
await fs.mkdirSync(dirPath);
// new start --------------
inquirerPrompt({
name,
...options
})
// new end ---------------
}
}
使用交互数据复制测试文件
npm install ejs --save
在根目录下创建template
文件夹,在里面创建index.html
和index.css
文件
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>
<!-- ejs 语法 -->
<%= name %>
</title>
</head>
<body>
<h1><%= name %></h1>
</body>
</html>
index.css
body {
padding: 20px
}
修改create.js
代码
// ...
const inquirerPrompt = require('./inquirerPrompt');
module.exports = async function(name, options) {
// ...
if (dirExist) {
// 如果已存在
// 判断用户是否有传入强制执行option、
if (options.force) {
// 如果传入
// 删除已存在文件
await fs.removeSync(dirPath);
// 创建新文件夹
await fs.mkdirSync(dirPath);
// new start --------------
const aws = inquirerPrompt({
name,
...options
})
// 获取样板文件路径
const tempPath = path.join(__dirname, '../template');
// 获取当前路径
const curPath = process.cwd();
// 读取template文件夹
fs.readdir(tempPath, (err, files) => {
// 使用ejs渲染替换变量
files.forEach(file => {
ejs.renderFile(path.join(tempPath, file), aws).then(data) => {
// 写入当前目录
fs.writefileSync(path.join(curPath, file), data, err => {
throw err;
})
})
})
})
// new end ----------------
} else {
// 没有传入,提醒用户已存在相同文件
console.log('当前路径下,已存在与项目同名文件夹,请检查后重试')
}
} else {
// 创建新文件夹
await fs.mkdirSync(dirPath);
// new start --------------
const aws = inquirerPrompt({
name,
...options
})
// new end ---------------
}
}
npm包总结
- commander 自定义命令行工具
- chalk 命令行提示文字样式修改
需要使用chalk@4.0.0,当前默认下载5.0.0,缺少commonjs规范
- figlet 艺术字效果
- fs-extra 文件操作
- log-symbols 命令行log
- inquirer 命令行交互
需要使用inquirer@8.0.0,当前默认下载inquirer包,缺少commonjs规范
- ejs 操作修改html文件