需要解决的问题
- 打包目录结构不变
- 可以复用 html 结构,打包结果为完整的 html
- 打包 js、css 、图片等资源文件,文件名不能改变,代码不要拆分和拼接
- 使用 tailwindcss
- build 结果可直接给php后端使用
项目目录
- src
- components
- style
- js
- css
- images
- lib 第三方库(jquery、swiper、video.js等)
- ...html 文件
- packages.json
- build.js
- vite.config.js
使用 vite-plugin-handlebars 插件实现开发复用 html
javascript
import { defineConfig } from "vite";
import handlebars from "vite-plugin-handlebars";
export default defineConfig({
root: "src",
plugins: [
handlebars({
// 组件目录
partialDirectory: "src/components",
}),
],
server: {
port: 3000,
},
});
html
// componentes - header
<header>aaa</header>
// index.html - 自动导入
{{> header }}
自定义打包,解决使用 vite 打包会修改资源文件目录以及文件名
javascript
// build.js - 简单的构建脚本
import fs from "fs";
import path from "path";
import { glob } from "glob";
import Handlebars from "handlebars";
const __dirname = path.dirname(new URL(import.meta.url).pathname);
// 注册 components
function registerComponents() {
const partialsDir = path.resolve(__dirname, "src/components");
if (!fs.existsSync(partialsDir)) return;
const partialFiles = glob.sync("src/components/**/*.hbs");
partialFiles.forEach((file) => {
const name = path.basename(file, ".hbs");
const content = fs.readFileSync(file, "utf8");
Handlebars.registerPartial(name, content);
});
}
// 编译 HTML 文件
function compileHtmlFiles() {
const htmlFiles = glob.sync("src/*.html");
const context = {};
// 确保 dist 目录存在
if (!fs.existsSync("dist")) {
fs.mkdirSync("dist", { recursive: true });
}
htmlFiles.forEach((file) => {
const content = fs.readFileSync(file, "utf8");
const template = Handlebars.compile(content);
const html = template(context);
const outputFile = path.join("dist", path.basename(file));
fs.writeFileSync(outputFile, html);
console.log(`✓ 编译完成: ${file} -> ${outputFile}`);
});
}
// 复制 styles 目录
function copyStyles() {
const stylesDir = path.resolve(__dirname, "src/styles");
const distStylesDir = path.resolve(__dirname, "dist/styles");
if (!fs.existsSync(stylesDir)) {
console.log("⚠️ src/styles 目录不存在");
return;
}
// 递归复制目录
function copyDir(src, dest) {
if (!fs.existsSync(dest)) {
fs.mkdirSync(dest, { recursive: true });
}
const entries = fs.readdirSync(src, { withFileTypes: true });
entries.forEach((entry) => {
const srcPath = path.join(src, entry.name);
const destPath = path.join(dest, entry.name);
if (entry.isDirectory()) {
copyDir(srcPath, destPath);
} else {
fs.copyFileSync(srcPath, destPath);
}
});
}
copyDir(stylesDir, distStylesDir);
console.log("✓ styles 目录复制完成");
}
// 清空 dist 目录
function cleanDist() {
if (fs.existsSync("dist")) {
fs.rmSync("dist", { recursive: true, force: true });
}
console.log("✓ 清空 dist 目录");
}
// 主构建函数
function build() {
console.log("🚀 开始构建...");
cleanDist();
registerComponents();
compileHtmlFiles();
copyStyles();
console.log("✅ 构建完成!");
}
build();
配置 packages.json 打包命令
json
{
"name": "phant-ai-vite",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "npx concurrently \"vite\" \"npx @tailwindcss/cli -i ./input.css -o ./src/styles/css/output.css --watch\"",
"tw": "npx @tailwindcss/cli -i ./input.css -o ./src/styles/css/output.css --watch",
"build": "npx @tailwindcss/cli -i ./input.css -o ./src/styles/css/output.css && node build.js"
},
"devDependencies": {
"@types/jquery": "^3.5.32",
"glob": "^11.0.2",
"handlebars": "^4.7.8",
"vite": "^6.3.5",
"vite-plugin-handlebars": "^2.0.0"
},
"dependencies": {
"@tailwindcss/cli": "^4.1.8",
"tailwindcss": "^4.1.8"
}
}