模板CLI工作原理:整合base&CDN模板项目
概述
本节通过 Node.js 完成一个模板项目的最小闭环,学习 CLI 工具的核心工作原理。核心问题:如何将 base 基础项目与 CDN 配置模板进行合并,形成最终的项目模板?关键在于处理 package.json 依赖合并和 vite.config.ts 配置注入。
CLI 合并模板的核心流程
用户选择模板 → CLI 读取 base 项目 → 合并 CDN 配置 → 生成最终项目
具体步骤:
1. 将 CDN 相关的 package.json 依赖合并到 base 项目
2. 将 CDN 相关的 vite.config.ts 配置注入到 base 项目
3. 执行 npm install 安装依赖
4. 输出最终可用的项目
text
模板项目结构
templates/
├── base/ # 基础模板项目
│ ├── package.json # 基础依赖
│ ├── vite.config.ts # 基础配置
│ ├── src/
│ └── ...
└── cdn/ # CDN 配置模板
├── package.json # CDN 相关依赖(vite-plugin-cdn2 等)
└── vite.config.ts # CDN 插件配置
text
难点一:package.json 合并
package.json 是标准 JSON 数据,合并相对简单:
// utils/merge-package.ts
import type { Recordable } from '../types'
/**
* 合并两个 package.json 的依赖项
*/
export function mergePackageJson(
basePkg: Recordable,
cdnPkg: Recordable
): Recordable {
return {
...basePkg,
dependencies: {
...(basePkg.dependencies || {}),
...(cdnPkg.dependencies || {}),
},
devDependencies: {
...(basePkg.devDependencies || {}),
...(cdnPkg.devDependencies || {}),
},
}
}
ts
难点二:vite.config.ts 配置注入
vite.config.ts 是 TypeScript 代码文件,无法像 JSON 那样简单合并。解决方案:
方案一:占位符替换法
在 base 项目的 vite.config.ts 中预置占位符,CLI 用字符串替换注入配置:
// base/vite.config.ts(带占位符)
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [
vue(),
/* __CDN_PLUGINS_PLACEHOLDER__ */
],
/* __CDN_CONFIG_PLACEHOLDER__ */
})
ts
// cli/inject-cdn-config.ts
import fs from 'node:fs'
export function injectCdnConfig(configPath: string, cdnConfig: string): void {
let content = fs.readFileSync(configPath, 'utf-8')
// 替换占位符
content = content.replace(
'/* __CDN_PLUGINS_PLACEHOLDER__ */',
cdnConfig
)
fs.writeFileSync(configPath, content)
}
ts
方案二:函数化配置(推荐)
将配置项提取为独立函数,通过参数控制是否启用:
// base/vite.config.ts(函数化配置)
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
interface Options {
cdn?: boolean
}
// export 一个函数,CLI 可以直接调用或拼接
export function getConfig(options: Options = {}) {
const plugins = [vue()]
if (options.cdn) {
// CDN 配置将在合并时注入
}
return defineConfig({
plugins,
})
}
// 默认导出
export default getConfig()
ts
CLI 完整实现思路
1. 项目初始化
// cli/index.ts
import fs from 'node:fs'
import path from 'node:path'
interface TemplateOptions {
projectName: string
template: 'base' | 'base+cdn'
targetDir: string
}
export function createTemplate(options: TemplateOptions) {
const { projectName, template, targetDir } = options
const templatesDir = path.resolve(__dirname, '../templates')
// 1. 复制 base 项目
copyDir(path.join(templatesDir, 'base'), targetDir)
// 2. 如果选择了 CDN,合并 CDN 配置
if (template === 'base+cdn') {
mergeCdnTemplate(targetDir, templatesDir)
}
// 3. 更新 package.json 中的项目名称
updateProjectName(targetDir, projectName)
console.log(`✅ 项目 ${projectName} 创建成功!`)
}
ts
2. CDN 合并逻辑
// cli/merge-cdn.ts
import fs from 'node:fs'
import path from 'node:path'
export function mergeCdnTemplate(targetDir: string, templatesDir: string) {
const cdnDir = path.join(templatesDir, 'cdn')
// 1. 合并 package.json
const basePkg = JSON.parse(
fs.readFileSync(path.join(targetDir, 'package.json'), 'utf-8')
)
const cdnPkg = JSON.parse(
fs.readFileSync(path.join(cdnDir, 'package.json'), 'utf-8')
)
const mergedPkg = {
...basePkg,
devDependencies: {
...basePkg.devDependencies,
...cdnPkg.devDependencies,
},
}
fs.writeFileSync(
path.join(targetDir, 'package.json'),
JSON.stringify(mergedPkg, null, 2)
)
// 2. 注入 vite.config.ts CDN 配置
const viteConfigPath = path.join(targetDir, 'vite.config.ts')
injectCdnConfig(viteConfigPath)
}
ts
3. 处理 data.ts 文件
模板中的数据文件需要正确处理路径和引用:
// templates/base/src/data.ts
// 可以直接 export 一个对象,也可以用函数动态生成
// 推荐:直接导出对象,CLI 可以解构使用
export const data = {
plugins: [],
dependencies: {},
}
// 或者使用函数
export function getData() {
return {
plugins: [],
dependencies: {},
}
}
ts
精简 vite.config.ts
CLI 合并前,先将 CDN 模板的 vite.config.ts 精简到只保留 CDN 相关配置:
// templates/cdn/vite.config.ts(精简版,仅 CDN 相关)
import { cdn } from 'vite-plugin-cdn2'
// CDN 插件配置
export const cdnPlugin = cdn({
modules: [
{
name: 'vue',
var: 'Vue',
url: 'https://unpkg.com/vue@3/dist/vue.global.prod.js',
},
{
name: 'element-plus',
var: 'ElementPlus',
url: 'https://unpkg.com/element-plus',
},
],
})
ts
模板维护与版本管理
模板项目维护策略:
├── Git 仓库管理
│ ├── templates-base 仓库
│ ├── templates-cdn 仓库
│ └── 独立版本号(semver)
├── CI/CD 自动化
│ ├── 模板变更自动测试
│ └── 发包后自动更新 CLI 中的模板引用
└── 用户使用
├── npx create-vue-template
└── 自动拉取最新模板
text
实践要点
- CLI 合并模板的核心在于
package.json依赖合并和vite.config.ts配置注入 package.json是标准 JSON,合并简单;vite.config.ts是代码文件,需要占位符替换或函数化方案- 精简 CDN 模板的
vite.config.ts,只保留 CDN 相关配置,减少合并复杂度 - 模板项目应通过 Git 仓库管理,支持版本号和自动化测试
- 测试完成后可删除临时文件和调试代码,使用
export导出配置对象供 CLI 调用
↑