前端架构师都在用的目录生成神器:mkfolder 全面解析
❝🧰 引言:你是不是也这样? 作为一名前端开发者,你有没有经历过下面这些场景? 如果你点头了,那这篇博客就是为你准备的! 今天我要分享的是我自己开发的一个 CLI 工具 —— catalog_file_......
❞
🧰 引言:你是不是也这样?
作为一名前端开发者,你有没有经历过下面这些场景?
❝“又来一个新模块?好,先去 copy 个模板,改改名字…… 再写点基本结构……”
❞
❝“这个组件结构每次都差不多,能不能别让我手敲了?”
❞
❝“团队里每个人写的 Vue 文件格式都不一样,review 起来头大……”
❞
如果你点头了,那这篇博客就是为你准备的!
今天我要分享的是我自己开发的一个 CLI 工具 —— 「mkfolder」,它能让你:
✅ 通过一个配置文件,一键生成 Vue 页面和组件; ✅ 支持多种模板类型(Vue2、Vue3、script setup); ✅ 自动创建多层级目录结构; ✅ 统一项目风格,提升团队协作效率;
一句话总结:「它是一个 “造房子” 的工具,你只管画设计图,它帮你盖楼。」
🔨 它到底能干啥?
场景一:批量创建模块?一行命令搞定!
你想创建 a、b、c、d 这几个模块,每个模块下都有 index.vue、edit.vue,甚至还有子目录 components。
传统做法是:新建目录 ➜ 拷贝模板 ➜ 改名 ➜ 修改内容 ➜ 循环重复……
用 mkfolder 的话,只需要一个配置文件:{
"a": {
"index.vue": { "field": {}, "template": "v2" },
"edit.vue": { "field": {}, "template": "v2" }
},
"b": {
"index.vue": { "field": {}, "template": "v2" },
"edit.vue": { "field": {}, "template": "v2" }
},
"c": {
"index.vue": { "field": {}, "template": "v2" },
"edit.vue": { "field": {}, "template": "v2" },
"info.vue": { "field": {}, "template": "v2" }
},
"d": {
"components": {
"tool.vue": { "field": {}, "template": "v2" }
},
"index.vue": { "field": {}, "template": "v2" }
}
}
然后执行命令:mk generate -c config.json -o src/views
Boom!目录结构瞬间就建好了!
场景二:统一代码风格?模板说了算!
团队协作中,最怕的就是风格不统一。有人喜欢用 <script setup>,有人偏爱 Vue2 的 Options API。
怎么办?用 mkfolder,直接在配置里指定模板路径:{
"user": {
"index.vue": {
"field": {},
"template": "/templates/vue3sTemp.vue"
}
}
}
所有生成的文件都使用同一个模板,风格一致,review 不头疼。
场景三:快速搭建 MVP?几分钟搞定几十个页面!
创业、内部孵化、临时需求…… 时间紧任务重?
用这个工具,几分钟就能搭出几十个页面结构,把精力留给真正重要的功能逻辑。
场景四:支持 EJS 模板引擎 —— 真正实现「模板驱动开发」
「mkfolder」 引入了对 「EJS 模板」 的完整支持。这意味着你可以:
使用变量动态填充内容 编写逻辑判断、循环等复杂结构 实现高度定制化的代码生成 一次编写模板,多项目复用
示例:自定义 EJS 模板生成带业务逻辑的页面
1. 创建一个 EJS 模板文件:templates/vue3sTemp.vue.ejs<template>
<el-table :data="tableData" border>
<% for (let column of columns) { %>
<el-table-column
prop="<%= column.prop %>"
label="<%= column.label %>"
<% if (column.width) { %> width="<%= column.width %>" <% } %>
></el-table-column>
<% } %>
<el-table-column label="操作">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</template>
<script setup>
import { ref } from 'vue';
const tableData = ref([
<% for (let row of defaultData) { %>
{
<% for (let col of columns) { %>
<%= col.prop %>: '<%= row[col.prop] || '' %>',
<% } %>
},
<% } %>
]);
function handleEdit(index, row) {
console.log('编辑行:', index, row);
}
function handleDelete(index, row) {
console.log('删除行:', index, row);
}
</script>
<el-table :data="tableData" border>
<% for (let column of columns) { %>
<el-table-column
prop="<%= column.prop %>"
label="<%= column.label %>"
<% if (column.width) { %> width="<%= column.width %>" <% } %>
></el-table-column>
<% } %>
<el-table-column label="操作">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</template>
<script setup>
import { ref } from 'vue';
const tableData = ref([
<% for (let row of defaultData) { %>
{
<% for (let col of columns) { %>
<%= col.prop %>: '<%= row[col.prop] || '' %>',
<% } %>
},
<% } %>
]);
function handleEdit(index, row) {
console.log('编辑行:', index, row);
}
function handleDelete(index, row) {
console.log('删除行:', index, row);
}
</script>
这个模板会根据传入的参数决定是否渲染表单区域。
2. 配置文件中指定该模板路径:{
"user": {
"list.vue": {
"field":{
"columns": [
{ "prop": "date", "label": "日期", "width": "150" },
{ "prop": "name", "label": "姓名", "width": "120" },
{ "prop": "address", "label": "地址" }
],
"defaultData": [
{
"date": "2024-07-01",
"name": "张三",
"address": "北京市朝阳区"
},
{
"date": "2024-07-02",
"name": "李四",
"address": "上海市浦东新区"
}
],
}
"template": "templates/vue3sTemp.vue.ejs"
}
}
}
"user": {
"list.vue": {
"field":{
"columns": [
{ "prop": "date", "label": "日期", "width": "150" },
{ "prop": "name", "label": "姓名", "width": "120" },
{ "prop": "address", "label": "地址" }
],
"defaultData": [
{
"date": "2024-07-01",
"name": "张三",
"address": "北京市朝阳区"
},
{
"date": "2024-07-02",
"name": "李四",
"address": "上海市浦东新区"
}
],
}
"template": "templates/vue3sTemp.vue.ejs"
}
}
}
3. 执行命令生成模块:
mk generate -c config.json -o src/views
4. 生成结果:<template>
<el-table :data="tableData" border>
<el-table-column prop="date" label="日期" width="150"></el-table-column>
<el-table-column prop="name" label="姓名" width="120"></el-table-column>
<el-table-column prop="address" label="地址"></el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</template>
<script setup>
import { ref } from 'vue';
const tableData = ref([
{
date: '2024-07-01',
name: '张三',
address: '北京市朝阳区'
},
{
date: '2024-07-02',
name: '李四',
address: '上海市浦东新区'
}
]);
function handleEdit(index, row) {
console.log('编辑行:', index, row);
}
function handleDelete(index, row) {
console.log('删除行:', index, row);
}
</script>
<el-table :data="tableData" border>
<el-table-column prop="date" label="日期" width="150"></el-table-column>
<el-table-column prop="name" label="姓名" width="120"></el-table-column>
<el-table-column prop="address" label="地址"></el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</template>
<script setup>
import { ref } from 'vue';
const tableData = ref([
{
date: '2024-07-01',
name: '张三',
address: '北京市朝阳区'
},
{
date: '2024-07-02',
name: '李四',
address: '上海市浦东新区'
}
]);
function handleEdit(index, row) {
console.log('编辑行:', index, row);
}
function handleDelete(index, row) {
console.log('删除行:', index, row);
}
</script>
💡 小贴士:如何快速创建自己的 EJS 模板?
在项目根目录下创建 /templates文件夹;添加 .ejs后缀的模板文件;在配置中使用绝对路径 /templates/xxx.vue.ejs即可;
❝注意 模板的变量名称要与配置文件的变量名称对应好,其中:pageName 与 pagePath 是插件内置属性,分别表示文件路径与文件名称
❞
🚀 怎么安装和使用?
安装方式:npm install -g mkfolder
安装完成后,输入:mk --help
你会看到如下命令:Usage: mk [options] [command]
Commands:
module 交互式生成模块(支持不同文件选择不同模板 + 输入中文内容)
generate 根据配置文件生成模块结构(支持 .js/.json)
Options:
-V, --version 输出版本号
-h, --help 显示帮助信息
🛠️ 命令详解
1. 交互式创建模块:mk module
适用于临时新增模块,比如用户管理、订单页等。
示例命令:mk module -n user,order
参数说明:
-n--name | |
--files | index |
-o--output | ./dist |
执行后会进入交互流程:
「选择模板类型」:Vue2 / Vue3 / script setup / 自定义路径; 「输入中文描述」:自动替换模板中的占位符(如 #name、#content);
2. 配置文件生成结构:mk generate
适合一次性批量生成多个模块。
示例命令:mk generate -c config.json -o src/modules
参数说明:
-c--config | .json 或 .js) |
-o--output | ./dist |
📦 支持的模板类型一览
"v2" | ||
"/templates/vue3sTemp.vue" | ||
"../custom-templates/form.vue" |
🧪 小试牛刀:试试看!
示例输出结构:
运行完命令后,会在 src/modules/ 下生成如下结构:深色版本
src/modules/
├── user/
│ ├── index.vue
│ ├── edit.vue
│ └── detail.vue
└── order/
├── index.vue
├── edit.vue
└── detail.vue
每个 .vue 文件都会根据你选择的模板和内容自动填充内容,无需手动编写。
⚙️ 如何封装到自己的项目脚本中?
你可以封装一个 Node.js 脚本来调用这个 CLI,方便集成到你的项目中。
创建 scripts/mk-page.js:#!/usr/bin/env node
const { spawn } = require('child_process');
const chalk = require('chalk');
const path = require('path');
const [name] = process.argv.slice(2);
if (!name) {
console.error(chalk.red('❌ 请提供模块名称,例如:npm run cf:page UserPage'));
process.exit(1);
}
const cliEntry = path.resolve(__dirname, '../node_modules/mkfolder/cli.js');
const args = [
'module',
'-n', name,
'--files', 'index',
'-o', 'src/components'
];
const child = spawn('node', [cliEntry, ...args], { stdio: 'inherit' });
child.on('error', (err) => {
console.error(chalk.red(`❌ 子进程启动失败:${err.message}`));
process.exit(1);
});
child.on('close', (code) => {
if (code === 0) {
console.log(chalk.green(`✅ 模块【${name}】创建成功!`));
} else {
console.error(chalk.red(`❌ 创建失败,退出码:${code}`));
}
});
const { spawn } = require('child_process');
const chalk = require('chalk');
const path = require('path');
const [name] = process.argv.slice(2);
if (!name) {
console.error(chalk.red('❌ 请提供模块名称,例如:npm run cf:page UserPage'));
process.exit(1);
}
const cliEntry = path.resolve(__dirname, '../node_modules/mkfolder/cli.js');
const args = [
'module',
'-n', name,
'--files', 'index',
'-o', 'src/components'
];
const child = spawn('node', [cliEntry, ...args], { stdio: 'inherit' });
child.on('error', (err) => {
console.error(chalk.red(`❌ 子进程启动失败:${err.message}`));
process.exit(1);
});
child.on('close', (code) => {
if (code === 0) {
console.log(chalk.green(`✅ 模块【${name}】创建成功!`));
} else {
console.error(chalk.red(`❌ 创建失败,退出码:${code}`));
}
});
在 package.json 中添加脚本:"scripts": {
"mk:page": "node scripts/mk-page.js"
}
"mk:page": "node scripts/mk-page.js"
}
使用方式:npm run mk:page UserPage
🌟 总结:为什么你应该试试它?
🎉 结语:别让工具牵着你走,要让它为你服务!
mkfolder 不只是一个脚手架工具,它更像是一位 “代码建筑师”,你只需要告诉它你要什么结构,剩下的交给它就行。
下次当你又要手写第 10 个 Vue 文件的时候,不妨试试这个工具,让你从重复劳动中解放出来,去做更有价值的事!
📌 「GitHub 仓库链接 (github.com/zy1992829/m…[1])」
❝👉 我已经打包发布到了 npm,你可以直接使用
❞npm install -g mkfolder安装。
📌 「想扩展功能?」
❝👉 欢迎 fork、PR、提 issue,我们一起打造更强大的前端代码生成器!
❞
🎯 「最后送大家一句话:」
❝“程序员的价值不是写多少行代码,而是让代码尽可能少地写。”
❞
用工具解放双手,才是真正的 “高效编程”。
✨ 「觉得有帮助的话,记得点个赞、收藏、转发哦~」
💬 「欢迎留言交流你日常开发中遇到的重复性工作,我们可以一起想办法自动化解决!」
AI编程资讯AI Coding专区指南:https://aicoding.juejin.cn/aicoding