Search K
Appearance
前端的同学在搭建一个新项目的时候,往往要经历一个很蛋疼的过程:
vue-router
、pinia
、element-plus
、axios
等等然而,以上工作都是机械式的工作,但是每次都要重复这个过程,不仅毫无意义,而且耗费大量的时间。
那么,有没有一种方式,能够把这些工作都统一起来?比如提供一个工具,我使用这个工具创建项目的时候,生成的工程代码里就把以上的工作全做好了,而我只需要关注业务本身的开发即可,做到开箱即用。
答案当然是有的,我们也可以制作一个类似于 Vue CLI
这样的脚手架工具,我们通过它使用命令行就可以生成开箱即用的初始代码,我们把它命名为 @b-flash/cli。
我们先通过以下这张流程图理解一下 @b-flash/cli
的工作流程。
不难看出,整个流程非常简单:
b-flash create <项目名称>
整体工作就可以分成 两个部分:
@b-flash/cli
的流程代码确定好工作内容后,我们就可以开展工作了。
TIP
我们以中后台模板为例子,以下提到的模板都指中后台模板
代码模板实际上就是使用 vite 创建项目,把开始提到的蛋疼的过程全都集成到里面去,然后推送到代码仓库。最终 模板 的目录结构长这样
b-flash-admin
├── .env ———————————————————————————————— 所有环境通用配置文件
├── .env.prod ——————————————————————————— 生产环境配置文件
├── .env.sit ———————————————————————————— 测试环境配置文件
├── .eslintignore ——————————————————————— eslint 忽略文件
├── .eslintrc.cjs ——————————————————————— eslint 配置文件
├── .gitignore —————————————————————————— git 忽略文件
├── .husky —————————————————————————————— 代码提交前置脚本
├── .prettierrc.cjs ————————————————————— prettier 配置文件
├── .prettierignore ————————————————————— prettier 忽略文件
├── .vscode ————————————————————————————— vscode 配置文件
├── README.md ——————————————————————————— 必读!!!
├── index.html —————————————————————————— html 入口文件
├── package-lock.json ——————————————————— package-lock.json
├── package.json ———————————————————————— package.json
├── public —————————————————————————————— 不用经过打包的文件放这里
├── src ————————————————————————————————— 源代码文件夹
│ ├── api ——————————————————————————————— 接口调用写这里
│ │ ├── menu.js ————————————————————————— 菜单相关接口
│ │ └── user.js ————————————————————————— 用户相关接口
│ ├── app.vue ——————————————————————————— app.vue
│ ├── components ———————————————————————— 公共组件放这里
│ ├── layout ———————————————————————————— 布局组件
│ │ ├── b-aside.vue ————————————————————— 左侧栏组件
│ │ ├── b-header.vue ———————————————————— 顶部栏组件
│ │ ├── b-menu.vue —————————————————————— 左侧栏递归菜单组件
│ │ └── b-tabs.vue —————————————————————— 多标签页实现组件
│ ├── main.js ——————————————————————————— 应用入口文件
│ ├── pages ————————————————————————————— 所有页面放这里
│ │ ├── module1 ————————————————————————— 模块 1(示例)
│ │ │ ├── index.vue ————————————————————— 模块 1 的路由嵌套公共组件(示例)
│ │ │ ├── page1.vue ————————————————————— 模块 1 的页面 1(示例)
│ │ │ └── page2.vue ————————————————————— 模块 1 的页面 2(示例)
│ │ ├── module2 ————————————————————————— 模块 2(示例)
│ │ │ ├── index.vue ————————————————————— 模块 2 的路由嵌套公共组件(示例)
│ │ │ ├── page1.vue ————————————————————— 模块 2 的页面 1(示例)
│ │ │ └── page2.vue ————————————————————— 模块 2 的页面 2(示例)
│ ├── router ———————————————————————————— 路由写这里
│ │ └── index.js ———————————————————————— 路由文件
│ ├── store ————————————————————————————— 状态存储写这里
│ │ ├── tabs.js ————————————————————————— 标签页的状态存储
│ │ └── user.js ————————————————————————— 用户相关的状态储存
│ ├── style.less ———————————————————————— 全局样式定义写这里
│ ├── utils ————————————————————————————— 工具类库写这里
│ │ ├── request.js —————————————————————— 统一请求拦截文件
│ │ └── url-helper.js ——————————————————— url 处理相关的工具库
└── vite.config.js —————————————————————— vite 配置文件
b-flash-admin
├── .env ———————————————————————————————— 所有环境通用配置文件
├── .env.prod ——————————————————————————— 生产环境配置文件
├── .env.sit ———————————————————————————— 测试环境配置文件
├── .eslintignore ——————————————————————— eslint 忽略文件
├── .eslintrc.cjs ——————————————————————— eslint 配置文件
├── .gitignore —————————————————————————— git 忽略文件
├── .husky —————————————————————————————— 代码提交前置脚本
├── .prettierrc.cjs ————————————————————— prettier 配置文件
├── .prettierignore ————————————————————— prettier 忽略文件
├── .vscode ————————————————————————————— vscode 配置文件
├── README.md ——————————————————————————— 必读!!!
├── index.html —————————————————————————— html 入口文件
├── package-lock.json ——————————————————— package-lock.json
├── package.json ———————————————————————— package.json
├── public —————————————————————————————— 不用经过打包的文件放这里
├── src ————————————————————————————————— 源代码文件夹
│ ├── api ——————————————————————————————— 接口调用写这里
│ │ ├── menu.js ————————————————————————— 菜单相关接口
│ │ └── user.js ————————————————————————— 用户相关接口
│ ├── app.vue ——————————————————————————— app.vue
│ ├── components ———————————————————————— 公共组件放这里
│ ├── layout ———————————————————————————— 布局组件
│ │ ├── b-aside.vue ————————————————————— 左侧栏组件
│ │ ├── b-header.vue ———————————————————— 顶部栏组件
│ │ ├── b-menu.vue —————————————————————— 左侧栏递归菜单组件
│ │ └── b-tabs.vue —————————————————————— 多标签页实现组件
│ ├── main.js ——————————————————————————— 应用入口文件
│ ├── pages ————————————————————————————— 所有页面放这里
│ │ ├── module1 ————————————————————————— 模块 1(示例)
│ │ │ ├── index.vue ————————————————————— 模块 1 的路由嵌套公共组件(示例)
│ │ │ ├── page1.vue ————————————————————— 模块 1 的页面 1(示例)
│ │ │ └── page2.vue ————————————————————— 模块 1 的页面 2(示例)
│ │ ├── module2 ————————————————————————— 模块 2(示例)
│ │ │ ├── index.vue ————————————————————— 模块 2 的路由嵌套公共组件(示例)
│ │ │ ├── page1.vue ————————————————————— 模块 2 的页面 1(示例)
│ │ │ └── page2.vue ————————————————————— 模块 2 的页面 2(示例)
│ ├── router ———————————————————————————— 路由写这里
│ │ └── index.js ———————————————————————— 路由文件
│ ├── store ————————————————————————————— 状态存储写这里
│ │ ├── tabs.js ————————————————————————— 标签页的状态存储
│ │ └── user.js ————————————————————————— 用户相关的状态储存
│ ├── style.less ———————————————————————— 全局样式定义写这里
│ ├── utils ————————————————————————————— 工具类库写这里
│ │ ├── request.js —————————————————————— 统一请求拦截文件
│ │ └── url-helper.js ——————————————————— url 处理相关的工具库
└── vite.config.js —————————————————————— vite 配置文件
代码模板准备好之后,剩下的就是制作脚手架工具了。
脚手架工具目录结构:
b-flash-admin
├── bin ———————————————————————————————— 脚本目录
│ ├── index.js ————————————————————————— 程序入口
│ └── create.js ———————————————————————— create 命令入口
├── .gitignore ————————————————————————— 上传到git时要忽略的文件
├── README ————————————————————————————— 必读
└── package.json ——————————————————————— package.json
b-flash-admin
├── bin ———————————————————————————————— 脚本目录
│ ├── index.js ————————————————————————— 程序入口
│ └── create.js ———————————————————————— create 命令入口
├── .gitignore ————————————————————————— 上传到git时要忽略的文件
├── README ————————————————————————————— 必读
└── package.json ——————————————————————— package.json
index.js
#!/usr/bin/env node
import { createRequire } from 'module'
import { Command } from 'commander'
import create from './create.js'
import chalk from 'chalk'
const program = new Command()
const require = createRequire(import.meta.url)
const { version } = require('../package.json')
program
.name('b-flash')
.description(
chalk.green(`
██████╗ ███████╗██╗ █████╗ ███████╗██╗ ██╗
██╔══██╗ ██╔════╝██║ ██╔══██╗██╔════╝██║ ██║
██████╔╝█████╗█████╗ ██║ ███████║███████╗███████║
██╔══██╗╚════╝██╔══╝ ██║ ██╔══██║╚════██║██╔══██║
██████╔╝ ██║ ███████╗██║ ██║███████║██║ ██║
╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
`)
)
.version(version, '-v, --version', '输出版本号')
.helpOption('-h, --help', '输出帮助信息')
// 定义使用方法
program.usage('<command>')
program
.command('create <name>')
.description('创建项目')
.action((name) => {
create(name)
})
.helpOption('-h, --help', '创建项目')
program
.command('help [command]')
.description('显示帮助信息')
.action((cmd) => {
switch (cmd) {
case 'create':
console.log('碧桂园营销前端专用,创建项目工程起始代码')
break
default:
program.help()
}
})
// 错误命令时的显示帮助
program.parse(process.argv)
if (!program.args.length) {
program.help()
}
#!/usr/bin/env node
import { createRequire } from 'module'
import { Command } from 'commander'
import create from './create.js'
import chalk from 'chalk'
const program = new Command()
const require = createRequire(import.meta.url)
const { version } = require('../package.json')
program
.name('b-flash')
.description(
chalk.green(`
██████╗ ███████╗██╗ █████╗ ███████╗██╗ ██╗
██╔══██╗ ██╔════╝██║ ██╔══██╗██╔════╝██║ ██║
██████╔╝█████╗█████╗ ██║ ███████║███████╗███████║
██╔══██╗╚════╝██╔══╝ ██║ ██╔══██║╚════██║██╔══██║
██████╔╝ ██║ ███████╗██║ ██║███████║██║ ██║
╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
`)
)
.version(version, '-v, --version', '输出版本号')
.helpOption('-h, --help', '输出帮助信息')
// 定义使用方法
program.usage('<command>')
program
.command('create <name>')
.description('创建项目')
.action((name) => {
create(name)
})
.helpOption('-h, --help', '创建项目')
program
.command('help [command]')
.description('显示帮助信息')
.action((cmd) => {
switch (cmd) {
case 'create':
console.log('碧桂园营销前端专用,创建项目工程起始代码')
break
default:
program.help()
}
})
// 错误命令时的显示帮助
program.parse(process.argv)
if (!program.args.length) {
program.help()
}
create.js
import { exec } from 'child_process'
import { select, spinner } from '@clack/prompts'
import chalk from 'chalk'
const s = spinner()
const removeGit = (name) => {
let cmd = `cd ${name}`
switch (process.platform) {
case 'darwin':
cmd += '&& rm -rf .git'
break
case 'win32':
cmd += ' && rd /s /q .git'
break
}
exec(cmd, (e) => {
if (e) {
s.stop(
chalk.yellow('项目创建成功,但.git文件夹未清除,建议您手动清除')
)
console.log(chalk.yellow(e))
return
}
s.stop(chalk.green('项目创建成功,运行以下命令启动项目'))
console.log(
chalk.green(`
cd ${name}
npm install
npm run dev
`)
)
})
}
const create = async (name) => {
const projectType = await select({
message: chalk.green('你想创建什么类型的项目?'),
options: [
{
value: 'admin',
label: '中后台',
},
{
value: 'h5',
label: '移动端',
},
],
})
s.start('正在创建项目')
switch (projectType) {
case 'admin':
exec(
`git clone https://git.bgy.com.cn/bu00241/b-flash/b-flash-admin.git ${name}`,
(err) => {
if (err) {
s.stop(chalk.red('项目创建失败'))
console.error(err)
return
}
removeGit(name)
}
)
break
case 'h5':
exec(
`git clone https://git.bgy.com.cn/bu00241/b-flash/b-flash-h5.git ${name}`,
(err) => {
if (err) {
s.stop(chalk.red('项目创建失败'))
console.error(err)
return
}
removeGit(name)
}
)
break
}
}
export default create
import { exec } from 'child_process'
import { select, spinner } from '@clack/prompts'
import chalk from 'chalk'
const s = spinner()
const removeGit = (name) => {
let cmd = `cd ${name}`
switch (process.platform) {
case 'darwin':
cmd += '&& rm -rf .git'
break
case 'win32':
cmd += ' && rd /s /q .git'
break
}
exec(cmd, (e) => {
if (e) {
s.stop(
chalk.yellow('项目创建成功,但.git文件夹未清除,建议您手动清除')
)
console.log(chalk.yellow(e))
return
}
s.stop(chalk.green('项目创建成功,运行以下命令启动项目'))
console.log(
chalk.green(`
cd ${name}
npm install
npm run dev
`)
)
})
}
const create = async (name) => {
const projectType = await select({
message: chalk.green('你想创建什么类型的项目?'),
options: [
{
value: 'admin',
label: '中后台',
},
{
value: 'h5',
label: '移动端',
},
],
})
s.start('正在创建项目')
switch (projectType) {
case 'admin':
exec(
`git clone https://git.bgy.com.cn/bu00241/b-flash/b-flash-admin.git ${name}`,
(err) => {
if (err) {
s.stop(chalk.red('项目创建失败'))
console.error(err)
return
}
removeGit(name)
}
)
break
case 'h5':
exec(
`git clone https://git.bgy.com.cn/bu00241/b-flash/b-flash-h5.git ${name}`,
(err) => {
if (err) {
s.stop(chalk.red('项目创建失败'))
console.error(err)
return
}
removeGit(name)
}
)
break
}
}
export default create
代码很少,主要是定义了 create
命令,选择 中后台
还是 H5
应用,然后根据选择从 gitlab
下载对应的模板。以及定义帮助命令和版本号等辅助功能。
重点是使用了 commander 这个模块,超级强大的命令行工具库。