这个我是用deepseek搜索,堆砌而成,虽然中间遇到了无数次的"服务器繁忙,请稍后再试",但是经过不断问话以及结合豆包写了一份基础模板配置
功能包含
- Eslint检查
- Git提交校验
- 代码自动修复
- 图片压缩
- 别名配置
- 代码分割
- 开发可视化调试
- UI组件自动导入
- 打包分析
- html模板注入变量
- 移除无效日志
- 原子化css-tailwindcss
- Node版本控制
- 包选择器控制
- 像素转换
文件目录结构
【具体代码-👉】【vue-web-template】
vue-web-template/
├── .husky
│ ├── commit-msg
├── nginx/
│ ├── nginx.conf
├── public
├── src/
│ ├── app
│ │ ├── admin
│ │ │ ├── router
│ │ │ ├── view
│ │ │ ├── main.js
│ │ ├── web
│ │ │ ├── router
│ │ │ ├── view
│ │ │ ├── http
│ │ │ ├── main.js
│ └── packages
│ │ ├── assets
│ │ ├── components
│ │ ├── config
│ │ ├── helpers
│ │ ├── hooks
│ │ ├── http
│ │ ├── pinia
│ │ ├── style
│ │ ├── utils
│ │ ├── App.vue
│ │ ├── install.js
├── .env.development
├── .env.production
├── .env.staging
├── .gitignore
├── .npmrc
├── index.html
├── admin.html
├── eslint.config.js
├── package.json
├── postcss.config.js
├── preinstall.js
├── tailwind.config.js
├── vite.config.js
如果项目需要格式化执行
npm run eslint
Git结合Eslint拦截
如果没有.husky
目录,执行npx husky init
确保文件有可执行权限:chmod +x .husky/pre-commit
提交自动修复
添加一下属性,自动修复--fix --quiet
{
"lint-staged": {
"*.{js,jsx,ts,tsx,vue}": [
"eslint --fix --quiet"
]
}
}
规则校验
- feat: 新功能(feature)
- fix: 修补bug
- docs: 文档(documentation)
- style: 格式(不影响代码运行的变动)
- refactor: 重构(即不是新增功能,也不是修改bug的代码变动)
- chore: 构建过程或辅助工具的变动
- revert: 撤销,版本回退
- perf: 性能优化
- test:测试
- improvement: 改进
- build: 打包
- ci: 持续集成
启用Git校验规则
在当前项目根目录下执行此命令
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
安装包说明
@vueuse/gesture
手势组件,图片预览时的捏合缩放 ,导航栏的左右滑动切换 ,列表项的上拉加载更多 ,界面元素的触摸反馈 ,更复杂的滑动手势控制,如卡片翻转等
vconsole
移动端调试库
环境变量
/ 项目根目录
├── .env.development # 开发环境
├── .env.staging # 测试环境
├── .env.production # 生产环境
└── ...
区分PC端和移动端
在mobile模式下,会自定转UI尺寸为rem,以及会加入移动端调试,规则请看配置
VITE_DEVICE_TYPE = pc
VITE_DEVICE_TYPE = mobile
// 通过环境变量判断设备类型
const deviceType = import.meta.env.VITE_DEVICE_TYPE;
if (deviceType === 'pc') {
// PC 端逻辑
} else {
// 移动端逻辑
}
dotenvParseVariables
自定将环境的变量转换为适当的 JavaScript 数据类型,VITE_DEBUG=true,转换为布尔值 如果不做此过程得到是true的字符串
const env = loadEnv(mode, process.cwd(), "VITE_");
const parsedEnv = dotenvParseVariables(env);
createHtmlPlugin({
inject: {
data: {
...parsedEnv,
},
},
})
VueDevTools
无需浏览器安装在线调试面板工具,点击定位浏览器的元素,可定位到vscode的指定代码位置
import {defineConfig} from 'vite'
import VueDevTools from 'vite-plugin-vue-devtools'
export default defineConfig({
plugins: [
VueDevTools(),
vue(),
],
})
辅助函数
$navigateTo
禁止直接使用 router.push 或 window.location.href,必须通过 navigateTo 跳转
// 内部跳转(单页应用)
navigateTo('/user/123');
// 外部跳转(直接跳转)
navigateTo('https://github.com');
// 新标签页打开
navigateTo('https://github.com', true);
版本控制
控制Node版本和pnpm安装版本,确保开发者使用相同的工具链,避免因环境差异导致的各类问题
使用scripts 中的 preinstall 脚本会在执行 pnpm install 之前运行 preinstall.js 脚本来检查 Node 版本
{
"engines": {
"node": ">=18.0.0",
"pnpm": "^8.0.0"
},
"scripts": {
"preinstall": "node preinstall.js"
}
}
// 强制使用 pnpm 检查
if (!/pnpm/.test(process.env.npm_execpath)) {
console.error('\x1b[31m✖ 请使用 pnpm 安装依赖\x1b[0m');
console.log('\x1b[36m→ 解决方案:\x1b[0m');
console.log(' 1. 安装 pnpm: \x1b[33mnpm i -g pnpm\x1b[0m');
console.log(' 2. 安装依赖: \x1b[33mpnpm install\x1b[0m');
process.exit(1);
}
// Node.js 版本检查
const MIN_NODE_VERSION = 20;
const [currentMajor] = process.version
.slice(1)
.split('.')
.map(Number);
if (currentMajor < MIN_NODE_VERSION) {
console.error(
`\x1b[31m✖ 需要 Node.js v${MIN_NODE_VERSION}+,当前版本为 ${process.version}\x1b[0m`
);
console.log('\x1b[36m→ 解决方案:\x1b[0m');
console.log(' 1. 使用 nvm 切换版本:');
console.log(` \x1b[33mnvm install ${MIN_NODE_VERSION}\x1b[0m`);
console.log(` \x1b[33mnvm use ${MIN_NODE_VERSION}\x1b[0m`);
process.exit(1);
}
前端监控
使用的是Page Spy
,服务端安装好以后,添加以下脚本即可,管理面板安装完成即可有一个对应的ip地址和端口
<!-- PageSpy SDK -->
<script crossorigin="anonymous" src="http://172.16.26.150:6752/page-spy/index.min.js"></script>
<!-- 插件(非必须,但建议使用) -->
<script crossorigin="anonymous" src="http://172.16.26.150:6752/plugin/data-harbor/index.min.js"></script>
<script crossorigin="anonymous" src="http://172.16.26.150:6752/plugin/rrweb/index.min.js"></script>
<script>
window.$harbor = new DataHarborPlugin();
window.$rrweb = new RRWebPlugin();
[window.$harbor, window.$rrweb].forEach(p => {
PageSpy.registerPlugin(p)
})
window.$pageSpy = new PageSpy();
</script>
完整配置
import {defineConfig, loadEnv} from "vite";
import vue from "@vitejs/plugin-vue";
import path from "path";
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import {NaiveUiResolver} from "unplugin-vue-components/resolvers";
import tailwindcss from "tailwindcss";
import autoprefixer from "autoprefixer";
import eslintPlugin from "vite-plugin-eslint";
import {VantResolver} from "@vant/auto-import-resolver";
import viteImagemin from "vite-plugin-imagemin";
import postcssPxToRem from "postcss-pxtorem";
import {visualizer} from "rollup-plugin-visualizer"
import {fileURLToPath, URL} from "node:url";
import {resolve, dirname} from "node:path";
import history from "connect-history-api-fallback"
import {createHtmlPlugin} from "vite-plugin-html";
import dotenvParseVariables from "dotenv-parse-variables"
import VueDevTools from 'vite-plugin-vue-devtools'
export default defineConfig(({command, mode}) => {
const env = loadEnv(mode, process.cwd(), "VITE_");
const parsedEnv = dotenvParseVariables(env);
const cssPostcssPlugins = [tailwindcss, autoprefixer];
const isProduction = mode === "production";
if (env.VITE_DEVICE_TYPE === "mobile") {
cssPostcssPlugins.push(postcssPxToRem({
rootValue: 75,
unitPrecision: 3,
propList: ["*"],
selectorBlackList: [".ignore"],
replace: true,
mediaQuery: false,
minPixelValue: 0,
exclude: /node_modules/
}));
}
const autoImportOptions = {
imports: [
"vue",
{
"naive-ui": [
"useDialog",
"useMessage",
"useNotification",
"useLoadingBar"
]
}
],
resolvers: [VantResolver()],
};
const componentsOptions = {
resolvers: [
NaiveUiResolver(),
VantResolver()
]
};
const eslintOptions = {
include: ["src/**/*.js", "src/**/*.vue", "src/*.js", "src/*.vue"]
};
const imageminOptions = {
optipng: {
optimizationLevel: 7
},
pngquant: {
quality: [0.8, 0.9],
},
svgo: {
plugins: [
{name: "removeViewBox"},
{name: "removeEmptyAttrs", active: false}
]
}
};
const aliasConfig = {
"@": path.resolve(__dirname, "src"),
"__ROOT__": path.resolve(__dirname, ""),
};
const manualChunksFunction = (id) => {
if (id.includes("node_modules")) {
return id.toString().split("node_modules/")[1].split("/")[0];
}
};
const historyPlugin = () => {
return {
name: "vite-plugin-history",
configureServer(server) {
server.middlewares.use(history({
rewrites: [
{from: /\/admin/, to: "/admin.html"} // 解决页面 在history 刷新下会 404 的问题
],
htmlAcceptHeaders: ["text/html", "application/xhtml+xml"],
}))
}
}
}
return {
base: "./", // 公共基础路径(部署目录)
plugins: [
VueDevTools(),
vue(),
AutoImport(autoImportOptions),
Components(componentsOptions),
eslintPlugin(eslintOptions),
viteImagemin(imageminOptions),
// 打包分析插件
visualizer({
open: true,
gzipSize: true,
brotliSize: true
}),
historyPlugin(),
createHtmlPlugin({
inject: {
data: {
...parsedEnv,
},
},
})
],
define: {
__APP_ENV__: JSON.stringify(env.APP_ENV),
},
resolve: {
alias: aliasConfig,
extensions: [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json", ".vue"]
},
build: {
outDir: "dist",
sourcemap: false,
minify: "terser", // 生产环境移除 console 和 debugger
terserOptions: {
compress: {
drop_console: isProduction,
drop_debugger: isProduction,
},
},
rollupOptions: {
output: {
manualChunks: manualChunksFunction,
input: {
index: resolve(__dirname, "index.html"),
admin: resolve(__dirname, "admin.html")
}
}
}
},
css: {
postcss: {
plugins: cssPostcssPlugins
}
},
server: {
host: "0.0.0.0",
port: 5173,
open: false, // 自动打开浏览器
proxy: {
"/api": {
target: "http://127.0.0.1:1338",
changeOrigin: true,
},
},
}
};
});