本文将以完整的 vite 前端开发视角,逐一解读增加的依赖和目录结构的细节。
依赖
在本次迭代中,项目增加了很多依赖。
在 根目录 下,总包的依赖有:
{
"dependencies": {
"vue-i18n": "^10.0.4" # 在根目录中安装该应用是为了激活 i18n ally 插件
},
"devDependencies": {
"@antfu/eslint-config": "^3.7.3", # 全局引用的 antfu 的 ESLint 预设
"concurrently": "^9.0.1", # 在同一个 shell session 中运行前后端应用并分别输出 log
"eslint": "^9.12.0", # elint 安装,为了命令行的运行
"lint-staged": "^15.2.10", # 检查已更改的文件
"simple-git-hooks": "^2.11.1", # git hooks 的触发器
"vite": "^5.4.9" # 前后端公用的 Vite 依赖
},
}
在前端子包 client 中的依赖有:
{
"dependencies": {
"@vueuse/core": "^11.1.0", # Vue 常用的组合式函数库
"pinia": "^2.2.4", # Pinia 官方推荐的一个状态管理
"vue": "^3.5.12", # Vue 的主体
"vue-router": "^4.4.5" # 路由
},
"devDependencies": {
"@eslint/eslintrc": "^3.1.0", # 导入 .eslintrc-auto-import.json 配置使用
"@unocss/eslint-plugin": "^0.63.4", # UnoCSS 的 ESLint 插件,会自动读取 UnoCSS 配置
"@unocss/preset-rem-to-px": "^0.63.4", # UnoCSS 的 rem 转 px 的预设
"@vitejs/plugin-legacy": "^5.4.2", # Vite 打包兼容旧版本浏览器插件
"@vitejs/plugin-vue": "^5.1.4", # Vite 的 Vue 插件
"unocss": "^0.63.4", # UnoCSS
"@iconify/json": "^2.2.261", # iconify 的图标集合
"sass": "^1.80.2", # sa(c)ss 样式的编译器
"unplugin-auto-import": "^0.18.3", # 自动导入依赖 API,可以减少写 import
"unplugin-imagemin": "^0.5.20", # 图片压缩插件
"unplugin-vue-components": "^0.27.4", # 自动导入 Vue 全局组件
"vite-plugin-html": "^3.2.2", # HTML 插件
"vite-plugin-mkcert": "^1.17.6", # 在开发环境中使用 HTTPS 链接的插件
"vite-plugin-obfuscator": "^1.0.5", # 打包代码混淆插件
"vite-plugin-vue-devtools": "^7.5.2" # 随页面的 Vue 开发工具插件
},
}
目录结构
合理地规划项目目录结构,可以更好地管理同类型的资源和代码,经过安装目录结构规划,前端项目的目录结构如下所示:
client # 前端项目根目录 packages/client
┣ public # 静态资源,在构建阶段不需要进行处理的资源
┃ ┗ vite.svg
┣ src # 源代码
┃ ┣ assets # 在构建阶段需要进行处理的资源
┃ ┃ ┗ vue.svg
┃ ┣ components # 公共组件,一般放置在项目中需要复用的组件
┃ ┃ ┣ global # 全局组件,通过 unplugin-vue-components 注册到全局
┃ ┃ ┃ ┗ Example.vue
┃ ┃ ┗ HelloWorld.vue
┃ ┣ composables # 自定义组合式函数
┃ ┃ ┗ index.js
┃ ┣ layouts # 布局组件,通过 unplugin-vue-components 注册到全局
┃ ┃ ┗ Layout.vue
┃ ┣ locales # i18n 多语言配置
┃ ┃ ┣ en
┃ ┃ ┃ ┗ index.json
┃ ┃ ┣ zh
┃ ┃ ┃ ┗ index.json
┃ ┃ ┗ index.js
┃ ┣ router # 路由
┃ ┃ ┣ guards # 路由守卫
┃ ┃ ┃ ┣ index.js
┃ ┃ ┃ ┗ title.js
┃ ┃ ┗ index.js
┃ ┣ store # 数据仓库,使用 Pinia
┃ ┃ ┣ modules
┃ ┃ ┃ ┣ app.js
┃ ┃ ┃ ┗ index.js
┃ ┃ ┗ index.js
┃ ┣ styles # 样式
┃ ┃ ┣ index.js
┃ ┃ ┗ style.css
┃ ┣ utils # 工具函数
┃ ┃ ┣ modules
┃ ┃ ┃ ┣ index.js
┃ ┃ ┃ ┣ is.js
┃ ┃ ┃ ┗ request.js
┃ ┃ ┗ index.js
┃ ┣ views # 页面组件
┃ ┃ ┣ Home.vue
┃ ┃ ┗ Login.vue
┃ ┣ App.vue # App 组件
┃ ┗ main.js # App 入口文件
┣ vite # Vite 配置的辅助文件
┃ ┣ helper.js
┃ ┣ index.js
┃ ┗ plugins.js
┣ .env # 环境变量
┣ .env.development
┣ .env.production
┣ .eslintrc-auto-import.json # unplugin-auto-import 为 ESLint 生成的全局配置文件
┣ .gitignore
┣ eslint.config.js # ESLint 在本目录下的配置文件
┣ index.html # 页面入口文件
┣ jsconfig.json
┣ package.json
┣ README.md
┣ uno.config.js # UnoCSS 配置文件
┗ vite.config.js # Vite 配置文件
同类型文件的集中管理
在项目目录下,同类型的文件集中在一起管理有利于提高开发效率,一般的模式,以 utils 为例,该文件夹主要放置一些公用的工具函数,比如请求封装、数据类型判断等等。一般的结构如下:
utils
┣ modules
┃ ┣ index.js
┃ ┣ is.js
┃ ┗ request.js
┗ index.js
utils/index.js 用以导出各个模块的命名导出,和一些临时和不太好分类的功能导出:
export * from './modules'
export function someUtil() {}
modules 目录用以分类放置各个不同的模块功能,用 modules/index.js 来收集这些功能供 utils/index.js 进行统一导出:
export * from './is'
export * from './request'
这样做的好处是在消费这些功能时,在导入语句编写时,只需要写一级的模块名,比如:
import { request, isNull, isObject } from '@/utils'
之所以不适用 Vite 的
import.meta.glob进行载入,是因为在增减功能时,必须去utils/index.js中具名修改对应的模块名称,比较繁琐。
Vite 的配置
Vite 的配置使用了一些辅助函数,放置在 client/vite 目录下,所以整个配置文件看起来像这样:
import { resolve } from 'node:path'
import { cwd } from 'node:process'
import { defineConfig, loadEnv } from 'vite'
import { generateEnv, generatePlugins, generateProxy } from './vite'
export default defineConfig(({ mode }) => {
const env = generateEnv(loadEnv(mode, cwd()))
const {
VITE_BASE,
VITE_HTTPS,
VITE_DROP_CONSOLE,
VITE_SOURCEMAP,
VITE_OPEN,
VITE_PORT,
VITE_PROXY,
} = env
return {
root: cwd(),
base: VITE_BASE || '/',
plugins: generatePlugins({ mode, env }),
resolve: {
alias: {
'@': resolve(cwd(), 'src'),
'~': resolve(cwd()),
},
},
server: {
host: true,
https: VITE_HTTPS,
open: VITE_OPEN,
port: VITE_PORT || 5173,
proxy: generateProxy(VITE_PROXY),
},
preview: {
port: VITE_PORT || 5173,
proxy: generateProxy(VITE_PROXY),
},
esbuild: {
pure: VITE_DROP_CONSOLE ? ['console.log', 'debugger'] : [],
},
build: {
target: 'esnext',
chunkSizeWarningLimit: 2000,
sourcemap: VITE_SOURCEMAP,
},
}
})
大部分的功能可以通过 .env 进行配置,同时也把比较集中的 plugins、proxy 等篇幅较大的功能提取到 vite 目录下单独管理。具体的 vite 目录文件,请点击查看。
从 main.js 出发
main.js 是整个前端程序的入口文件,经过 vite 和其他一系列工具配置,再加上同类型文件的集中管理,该文件看起来像这样:
import { setupI18n } from '@/locales'
import { setupRouter } from '@/router'
import { setupStore } from '@/store'
import App from './App.vue'
import '@/styles'
async function setupApp() {
const app = createApp(App)
setupStore(app)
setupRouter(app)
setupI18n(app)
app.mount('#app')
}
setupApp()
该文件显示了style、store、router和i18n 的初始化,style 直接将相关的样式文件在style/index.js中导入,而后三者则分别只导入了三个获取 app 实例的配置函数,将实例发送到对应的目录下管理各自的逻辑。
其中没有涉及到全局组件的注册,这是因为使用了 unplugin-vue-components ,直接将 components/global 下的 vue 文件注册为全局组件。配置如下:
import Components from 'unplugin-vue-components/vite'
Components({
dts: false,
globs: [
'./src/components/global/*.vue',
'./src/components/global/**/index.vue',
'./src/layouts/Layout.vue',
],
})
App.vue 主组件
因为有 vue-router 的参与,所以 App.vue 主组件的内容可以只放置一个 router-view:
<template>
<router-view />
</template>
具体细节可以直接参考 vue-router 官网,这里不再赘述。
store 和 i18n
store 采用了比较简单的 pinia 作为数据仓库管理工具。属于比较常规的应用,可以参考 pinia 官网 。
i18n 使用时需要注意,因为 i18n ally 插件是通过读取根目录下的 vue-i18n 依赖进行激活的,所以,该依赖需要装在主包中。同时,因为 i18n ally 只能编辑和修改 json 文件,所以 i18n 使用json 导入:
import en from './en/index.json'
import zh from './zh/index.json'
const i18n = createI18n({
locale: 'zh',
messages: {
zh,
en,
},
})
源码
前端部分的简要变更说明如有未尽之处,请参照源码。