参考:
vue的官方脚手架
vuejs/create-vue: 🛠️ The recommended way to start a Vite-powered Vue project
脚手架仓库搭建总流程
第一步:初始化脚手架工程(CLI 外壳)
新建工程根目录:
在本地新建一个空目录
初始化 Node.js 项目并启用 ES Module:
在该目录下打开终端,运行 pnpm init 生成基础 package.json
打开 package.json,手动添加 "type": "module",以便直接使用现代 ES6 import 语法
安装交互与美化依赖:
运行:
pnpm add @clack/prompts picocolors
- @clack/prompts:负责在终端弹出单选、输入、确认等交互菜单与加载动画
- picocolors:负责在终端输出彩色高亮文字
第二步:构建纯净框架底座(Base Template)
规划并创建底座目录:
按"构建工具链 / 前端技术栈"的层级创建目录,例如:templates/electron-vite/react/
生成基础代码:
临时使用官方脚手架(如 pnpm create @quick-start/electron)在对应目录下生成初始工程。
底座清理:
清理多余文件
清除依赖缓存:清理完成后,删除该目录下的 node_modules,只保留源代码文件,确保模板库体积小、便于分发与版本控制
第三步:按需制作插件/组件增强层(Plugins 增量补丁)
创建插件目录:
在框架同级目录下创建 plugins/<功能名>/,例如:
- templates/electron-vite/plugins/tailwind/
- templates/electron-vite/plugins/shadcn/
按"最小增量原则"编写 package.json:
在插件目录下手动新建一个仅有几行的 package.json
里面只声明该插件专属的依赖(例如 Tailwind 插件只声明 tailwindcss 和 @tailwindcss/vite),
无需重复书写底座原有的 React、Electron 等依赖
放置补丁与配置文件(路径与底座 1:1 对齐):
新增文件:如果该插件需要新增文件(如 components.json、src/renderer/src/lib/utils.ts),按最终在项目里的相对路径存放
覆盖文件:如果该插件需要修改已有配置(如注入了插件的 electron.vite.config.ts、或者定义了设计变量的 globals.css),直接在此目录下存放配置完毕的完整文件,以便后续直接覆盖
第四步:编写核心调度引擎(index.js)
引入两大通用核心引擎函数:
renderTemplate(src, dest):负责遍历文件夹并将文件复制到目标项目;遇到普通文件直接覆盖,遇到 package.json 则转交给 deepMerge。同时阻止拷贝任何 lockfile 到新项目中
deepMerge(target, source):负责在不覆盖旧依赖的前提下,钻入大括号内部将插件的新依赖无缝融合进目标项目的 package.json
设计交互流程(使用 @clack/prompts):
依次收集用户输入:项目名称、前端框架、CSS 方案、UI 组件库、是否自动安装依赖。
按需拼装调用(像拼积木一样):
首先调用 renderTemplate(底座目录, 目标目录),生成基础骨架
更新目标项目的 package.json 名称为用户输入的项目名
如果用户勾选了某个功能(如 Tailwind),调用 renderTemplate(tailwind插件目录, 目标目录) 覆盖配置并合并依赖
如果用户勾选了组件库(如 shadcn),调用 renderTemplate(shadcn插件目录, 目标目录) 继续叠加
自动安装与交付提示:
若用户确认自动安装,通过 execSync('pnpm install', ...) 在目标目录下自动拉取依赖
终端打印进入项目与启动命令(cd <项目名>,pnpm dev),流程完毕
目录分工
使用基座 + 增量补丁的设计模式
框架底座
如 templates/electron-vite/react/
- 定位:一个最纯粹、能够独立运行的最小闭环项目。
- 内容:包含 Electron 的主进程、预加载脚本、最干净的空壳 App.tsx、基础 TypeScript 配置、以及脚本白名单。
- 原则:保持绝对干净,不要提前引入任何特定业务库(没有 UI 库、没有复杂状态管理)
插件增强层
如 templates/electron-vite/plugins/
- 定位:当用户在终端勾选某个选项时,像打补丁一样覆盖叠加到项目里
- 内容:只写引入该插件时"必须发生变化"的内容,不要多余文件
plugins/ 目录
package.json 只写真正新增的依赖(增量合并)
插件目录下的 package.json 不需要重复写 react、electron 等底座已有的包,只写它自己专属的依赖。
比如 plugins/tailwind/package.json 只有 4 行:
{
"devDependencies": {
"@tailwindcss/vite": "^4.1.18",
"tailwindcss": "^4.1.18"
}
}
index.js文件中,renderTemplate 函数会自动用 deepMerge 函数把这几行合并进底座的 package.json 里,底座原有的依赖完全不受影响
路径与底座项目保持 1:1 严格对齐
插件目录里的文件相对路径,要和底座项目最终放的位置分毫不差:
- 如果工具函数要在项目的 src/renderer/src/lib/utils.ts: 插件里就建:plugins/shadcn/src/renderer/src/lib/utils.ts。
- 如果配置文件要在根目录: 插件里就直接放在插件根目录:plugins/shadcn/components.json。 拷贝引擎会像"盖印章"一样,自动把它们分毫不差地投递到目标项目的对应层级。
需要修改的配置文件,放最终完整版直接覆盖
比如底座原本的 electron.vite.config.ts 没带 Tailwind 插件
在 plugins/tailwind/electron.vite.config.ts 里,就放已经注入好 Tailwind 插件的完整版本。叠加时,它会自动把底座原本的未配置版本替换掉
添加新插件
如果想给脚手架增加 Zustand 状态管理选项:
新建目录 templates/electron-vite/plugins/zustand/
手动里面放一个极简 package.json
{
"dependencies": {
"zustand": "^5.0.0"
}
}
可以放一个示例文件 src/renderer/src/store/useCounter.ts
在 index.js 交互问答里加一个选项
if (config.stateManagement === 'zustand') {
renderTemplate(path.join(PLUGINS_DIR, 'zustand'), targetDir)
}
测试
node <项目路径>\index.js
发布
发布到 npm
如果你希望任何人不需要克隆你的仓库,敲一行命令就能直接弹出这个菜单:
为脚手架声明全局命令:
在你的 <项目路径>/package.json中加上一个 bin 字段,并起一个名字
{
"name": "<名字>",
"version": "1.0.0",
"type": "module",
"bin": {
"<名字>": "./index.js"
}
}
在 index.js 第一行加环境标识:
#!/usr/bin/env node
注册 npm 账号并发布:
npm login
npm publish
之后,别人不用下载你的源码,只要打开终端敲:
npx <名字>
# 这个名字要 npm 规范敲,和npx的名字可能略有不同:
npm <npm规范敲的名字>
纯 GitHub 仓库管理
直接用 npx 远程运行 GitHub 仓库
npx github:<你的用户名>/<你的仓库名>
关于create-vue和本文的区别
我大致参考了create-vue,但仓库目录组织还是不同的
create-vue的官方仓库的 template/ 目录下,主要有以下几个文件夹:
create-vue/template/
│
├── base/ # 最简通用底座(所有项目必选,包含 index.html, vite.config 等)
├── bare/ # 极简模式(给 --bare 参数用的纯净空壳)
│
├── config/ # 重点!这就是我们所谓的"plugins(特性配置层)"!
│ ├── pinia/ # 只放 Pinia 的依赖与基础配置
│ ├── router/ # 只放 Vue Router 的依赖与配置
│ ├── typescript/ # 只放 TypeScript 的依赖与配置
│ ├── jsx/ # JSX 相关的配置
│ └── vitest/ # 单元测试 Vitest 的配置
│
├── entry/ # 入口文件的不同变体(main.js / main.ts)
│ ├── default/ # 最普通的 main.js
│ ├── router/ # 引入了 router 的 main.js
│ ├── pinia/ # 引入了 pinia 的 main.js
│ └── pinia-and-router/ # 既选了 pinia 又选了 router 时的 main.js
│
├── code/ # 示例组件(App.vue)的不同变体
│ ├── default/ # 普通 HelloWorld
│ └── router/ # 带有 <RouterView> 和导航栏的 App.vue
│
├── formatting/ # 代码格式化层(prettier / oxfmt)
│
├── linting/ # 代码检查层(eslint / oxlint)
│
└── tsconfig/ # tsconfig.json 的不同环境配置
create-vue拆得更细:
它的选项组合极多(TS/JS × Pinia × Router),所以它把 entry/(入口)和 config/(依赖配置)分开了
本文放在单独的包,比如tailwindcss的配置放在tailwind下,更加内聚