# 1. 初始化项目
# 1.node 版本要求
node:v18.19.0
# 2.vscode 插件安装
Vue - Official,Prettier - Code formatter,ESlint
提示:确保开发vue2时的Vetur插件禁用
# 3. 创建项目
| npm create vite@latest vite-vue-ts-seed -- --template vue-ts |
提示:vscode 在文件中通过鼠标右击,选择使用... 格式化文档,将 prettier 插件设为默认值
# 4. 安装项目依赖
# 2. 配置 tsconfig
# 1. 新建 src/typings.d.ts(在 src 文件夹下新建)
| |
| declare interface Window { |
| eventBus: any; |
| } |
| |
| |
| declare module "*.vue" { |
| import { DefineComponent } from "vue"; |
| const component: DefineComponent<object, object, any>; |
| export default component; |
| } |
提示:遇到 ts 报错,有些时候是配置未生效,可以重启 vscode 或 ts 服务(vscode 快捷键 ctrl+shift+p 调出命令行,输入 Restart TS Server)
# 2. 修改 package.json
| "scripts": { |
| "ts": "vue-tsc -b", |
| }, |
运行 yarn run ts 即可查看文件是否有 ts 类型错误
# 3. 配置 src 路径别名
# 1. 安装
| pnpm install @types/node -D |
# 2. 修改 vite.config.ts
| import { defineConfig } from "vite"; |
| import vue from "@vitejs/plugin-vue"; |
| import path from "path"; |
| |
| |
| export default defineConfig({ |
| plugins: [vue()], |
| |
| resolve: { |
| alias: { |
| "@": path.resolve("./src"), |
| }, |
| }, |
| }); |
# 3. 修改 tsconfig.app.json
| { |
| "compilerOptions":{ |
| |
| "baseUrl": ".", |
| "paths": { "@/*": ["src/*"] }, |
| } |
| } |
# 4. 配置 ESLint 、prettier、stylelint
# 1. 安装
| |
| pnpm install eslint@^8.39.0 -D |
| |
| |
| pnpm install eslint-plugin-vue@^9.11.0 -D |
| |
| |
| pnpm install @typescript-eslint/parser@^6.19.0 -D |
| |
| |
| pnpm install @typescript-eslint/eslint-plugin@^6.19.0 -D |
| |
| |
| pnpm install eslint-plugin-prettier@^5.1.3 -D |
| |
| |
| pnpm install eslint-config-prettier@^9.1.0 -D |
| |
| |
| pnpm install prettier@^3.2.4 -D |
# 2. 新建.eslintrc.cjs
| module.exports = { |
| env: { |
| browser: true, |
| node: true, |
| es2021: true, |
| }, |
| parser: "vue-eslint-parser", |
| extends: [ |
| "eslint:recommended", |
| "plugin:vue/vue3-recommended", |
| "plugin:@typescript-eslint/recommended", |
| "plugin:prettier/recommended", |
| "eslint-config-prettier", |
| ], |
| parserOptions: { |
| ecmaVersion: "latest", |
| parser: "@typescript-eslint/parser", |
| sourceType: "module", |
| ecmaFeatures: { |
| jsx: true, |
| }, |
| }, |
| ignorePatterns: ["dist", "node_modules", ".eslintrc.cjs", "commitlint.config.cjs"], |
| plugins: ["vue", "@typescript-eslint", "prettier"], |
| rules: { |
| "vue/multi-word-component-names": "off", |
| "@typescript-eslint/no-explicit-any": "off", |
| "@typescript-eslint/no-this-alias": [ |
| "error", |
| { |
| allowedNames: ["that"], |
| }, |
| ], |
| "@typescript-eslint/ban-ts-comment": "off", |
| "@typescript-eslint/no-non-null-assertion": "off", |
| "no-console": [ |
| |
| "warn", |
| { |
| allow: ["warn", "error"], |
| }, |
| ], |
| "no-debugger": "warn", |
| }, |
| }; |
rules 更多配置:eslint.org/docs/latest…
# 3. 新建 .prettierrc
| { |
| "endOfLine": "auto", |
| "printWidth": 120, |
| "semi": true, |
| "singleQuote": true, |
| "tabWidth": 2, |
| "trailingComma": "all", |
| "bracketSpacing": true |
| } |
# 4. 新建 .prettierignore
# 5. 重启 vscode 使配置生效
# 6. 配置 package.json
可以看到 App.vue 文件在 import 处飘红,因为结尾没有使用分号
![B257E9EA9CBE491DA0258FF814C44658.jpg]()
修改 package.json
| "scripts": { |
| "lint": "eslint src --fix --ext .js,.ts,.vue --report-unused-disable-directives --max-warnings 0" |
| }, |
运行 yarn run lint,可以看到上述 eslint (prettier/prettier) 问题都将被修复
# 7. 配置 stylelint
官网
# 1. 安装依赖
执行
| pnpm create stylelint |
| |
| pnpm init stylelint |
将会生成 .stylelintrc.json
文件,自动安装 stylelint
和 stylelint-config-standard
依赖
# 2. 安装规则配置文件
执行
| pnpm add stylelint-config-recommended-less stylelint-config-standard-vue less -D |
stylelint-config-recommended-less
-- less
的推荐可共享配置规则stylelint-config-standard-vue
可识别 vue 文件
# 3. 配置
从 Stylelint v15 开始,所有与样式相关的规则都已弃用。如果您使用的是 v15 或更高版本,并且未使用这些弃用的规则,则 stylelint-config-prettier 插件不再是必要的。
新建文件 .stylelintrc.json
| |
| { |
| "extends": [ |
| "stylelint-config-standard", |
| "stylelint-config-recommended-less", |
| "stylelint-config-standard-vue" |
| ] |
| } |
# 5. 配置 husky、lint-staged、@commitlint/cli
husky:一个为 git 客户端增加 hook 的工具
lint-staged:仅对 Git 代码暂存区文件进行处理,配合 husky 使用
@commitlint/cli:让 commit 信息规范化
# 1. 创建 git 仓库
# 2. 安装
| pnpm install husky@9.1.3 -D |
| |
| pnpm install lint-staged@^15.2.7 -D |
| |
| pnpm install @commitlint/cli@^19.3.0 -D |
| |
| pnpm install @commitlint/config-conventional@^19.2.2 -D |
# 3. 生成 .husky 的文件夹
# 4. 修改.husky/pre-commit
| #!/usr/bin/env sh |
| npx --no-install lint-staged |
# 5. 修改.husky/commit-msg
| #!/usr/bin/env sh |
| npx --no-install commitlint --edit $1 |
# 6. 修改 package.json
| "lint-staged": { |
| "src/**/*.{js,ts,vue}": [ |
| "yarn run lint", |
| "prettier --write" |
| ] |
| } |
# 7. 新建 commitlint.config.cjs
提示:由于 package.json 的 "type": "module",需将 commonjs 文件显示声明为.cjs
| module.exports = { |
| extends: ['@commitlint/config-conventional'], |
| }; |
提交格式:
| git commit -m <type>[optional scope]: <description> //注意冒号后面有空格 |
| - type:提交的类型(如新增、修改、更新等) |
| - optional scope:涉及的模块,可选 |
| - description:任务描述 |
type 类型:
类别 | 含义 |
---|
feat | 新功能 |
fix | 修复 bug |
style | 样式修改(UI 校验) |
docs | 文档更新 |
refactor | 重构代码 (既没有新增功能,也没有修复 bug) |
perf | 优化相关,比如提升性能、体验 |
test | 增加测试,包括单元测试、集成测试等 |
build | 构建系统或外部依赖项的更改 |
ci | 自动化流程配置或脚本修改 |
revert | 回退某个 commit 提交 |
# 8. 示范(非规范提交,将提交失败)
| git commit -m 'feat: 增加 xxx 功能' |
| git commit -m 'bug: 修复 xxx 功能' |
# 6.vscode 保存自动格式化
在.vscode 下新建 settings.json
| { |
| "editor.codeActionsOnSave": { |
| "source.fixAll": "explicit" |
| } |
| } |
之后每次文件有修改,保存时,都会自动格式化
# 7. 配置 vue-router
# 1. 安装 vue-router
| npm install vue-router@next |
# 2. 路由实例
创建路由实例,顺带初始化静态路由,而动态路由需要用户登录,根据用户拥有的角色进行权限校验后进行初始化
| |
| import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'; |
| |
| export const Layout = () => import('@/layout/index.vue'); |
| |
| |
| export const constantRoutes: RouteRecordRaw[] = [ |
| { |
| path: '/redirect', |
| component: Layout, |
| meta: { hidden: true }, |
| children: [ |
| { |
| path: '/redirect/:path(.*)', |
| component: () => import('@/views/redirect/index.vue') |
| } |
| ] |
| }, |
| |
| { |
| path: '/login', |
| component: () => import('@/views/login/index.vue'), |
| meta: { hidden: true } |
| }, |
| |
| { |
| path: '/', |
| component: Layout, |
| redirect: '/dashboard', |
| children: [ |
| { |
| path: 'dashboard', |
| component: () => import('@/views/dashboard/index.vue'), |
| name: 'Dashboard', |
| meta: { title: 'dashboard', icon: 'homepage', affix: true } |
| } |
| ] |
| } |
| ]; |
| |
| |
| * 创建路由 |
| */ |
| const router = createRouter({ |
| history: createWebHashHistory(), |
| routes: constantRoutes as RouteRecordRaw[], |
| |
| scrollBehavior: () => ({ left: 0, top: 0 }) |
| }); |
| |
| |
| * 重置路由 |
| */ |
| export function resetRouter() { |
| router.replace({ path: '/login' }); |
| location.reload(); |
| } |
| |
| export default router; |
# 3. 全局注册路由实例
| |
| import router from "@/router"; |
| |
| app.use(router).mount('#app') |
# 4. 动态权限路由
路由守卫 src/permission.ts
,获取当前登录用户的角色信息进行动态路由的初始化
![img]()
最终调用 permissionStore.generateRoutes(roles)
方法生成动态路由
| |
| import { listRoutes } from '@/api/menu'; |
| |
| export const usePermissionStore = defineStore('permission', () => { |
| const routes = ref<RouteRecordRaw[]>([]); |
| |
| function setRoutes(newRoutes: RouteRecordRaw[]) { |
| routes.value = constantRoutes.concat(newRoutes); |
| } |
| |
| * 生成动态路由 |
| * |
| * @param roles 用户角色集合 |
| * @returns |
| */ |
| function generateRoutes(roles: string[]) { |
| return new Promise<RouteRecordRaw[]>((resolve, reject) => { |
| |
| listRoutes() |
| .then(({ data: asyncRoutes }) => { |
| |
| const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles); |
| setRoutes(accessedRoutes); |
| resolve(accessedRoutes); |
| }) |
| .catch(error => { |
| reject(error); |
| }); |
| }); |
| } |
| |
| return { routes, setRoutes, generateRoutes }; |
| }); |
接口获取得到的路由数据
![img]()
根据路由数据 (routes) 生成菜单的关键代码
src/layout/componets/Sidebar/index.vue | src/layout/componets/Sidebar/SidebarItem.vue |
---|
![img]() | ![image-20230326145836872]() |
# 8. 配置 Pinia
Pinia 是 Vue 的专属状态管理库,它允许你跨组件或页面共享状态。
参考:Pinia 官方文档
# 1. 安装依赖
# 2. main.ts
引入 pinia
| |
| import { createPinia } from "pinia"; |
| import App from "./App.vue"; |
| |
| createApp(App).use(createPinia()).mount("#app"); |
# 3. 定义 Store
根据 Pinia 官方文档 - 核心概念 描述 ,Store 定义分为 选项式
和 组合式
,先比较下两种写法的区别:
选项式 Option Store | 组合式 Setup Store |
---|
![img]() | ![img]() |
至于如何选择,官方给出的建议 : 选择你觉得最舒服的那一个就好
。
这里选择组合式,新建文件 src/store/counter.ts
| |
| import { defineStore } from "pinia"; |
| |
| export const useCounterStore = defineStore("counter", () => { |
| |
| const count = ref(0); |
| |
| const double = computed(() => { |
| return count.value * 2; |
| }); |
| |
| function increment() { |
| count.value++; |
| } |
| |
| return { count, double, increment }; |
| }); |
# 4. 父组件
| |
| <script setup lang="ts"> |
| import HelloWorld from "@/components/HelloWorld.vue"; |
| import { useCounterStore } from "@/store/counter"; |
| |
| const counterStore = useCounterStore(); |
| </script> |
| |
| <template> |
| <h1 class="text-3xl">vue3-element-admin-父组件</h1> |
| <el-button type="primary" @click="counterStore.increment">count++</el-button> |
| <HelloWorld /> |
| </template> |
# 5. 子组件
| |
| <script setup lang="ts"> |
| import { useCounterStore } from "@/store/counter"; |
| const counterStore = useCounterStore(); |
| </script> |
| |
| <template> |
| <el-card class="text-left text-white border-white border-1 border-solid mt-10 bg-[#242424]" > |
| <template #header> 子组件 HelloWorld.vue</template> |
| <el-form> |
| <el-form-item label="数字:"> </el-form-item> |
| <el-form-item label="加倍:"> </el-form-item> |
| </el-form> |
| </el-card> |
| </template> |
# 6. 效果预览
![img]()
# 9. 配置 SCSS
# 1. 安装依赖
# 2. 创建 variables.scss
变量文件
新建 src/styles/index.scss
添加变量 $bg-color
定义,注意规范变量以 $
开头
# 3. Vite
配置导入 SCSS
全局变量文件
配置 vite.config.ts
| |
| css: { |
| |
| preprocessorOptions: { |
| |
| scss: { |
| javascriptEnabled: true, |
| additionalData: `@use "@/styles/variables.scss" as *;` |
| } |
| } |
| } |
# 4. style
标签使用 SCSS
全局变量
| |
| <template> |
| <div class="box" /> |
| </template> |
| |
| <style lang="scss" scoped> |
| .box { |
| width: 100px; |
| height: 100px; |
| background-color: $bg-color; |
| } |
| </style> |
# 5. TypeScript
生效
上面导入的 SCSS
全局变量在 TypeScript
不生效的,需要创建一个以 .module.scss
结尾的文件
| |
| |
| |
| :export{ |
| bgColor:$bg-color |
| } |
TypeScript
使用 SCSS
全局变量
| |
| <script setup lang="ts"> |
| import variables from "@/styles/variables.module.scss"; |
| console.log(variables.bgColor) |
| </script> |
| |
| <template> |
| <div style="width:100px;height:100px" :style="{ 'background-color': variables.bgColor }" /> |
| </template> |
# 10. 配置 UnoCSS
UnoCSS 是一个具有高性能且极具灵活性的即时原子化 CSS 引擎 。
参考:Vite 安装 UnoCSS 官方文档
# 1. 安装依赖
# 2. vite.config.ts
配置
| |
| import UnoCSS from 'unocss/vite' |
| import { defineConfig } from 'vite' |
| |
| export default defineConfig({ |
| plugins: [ |
| UnoCSS({ |
| hmrTopLevelAwait: false, |
| }), |
| ], |
| }) |
# 3. 创建 uno.config.ts
文件
| |
| import { defineConfig, presetTypography, presetUno } from 'unocss'; |
| import presetRemToPx from '@unocss/preset-rem-to-px'; |
| |
| export default defineConfig({ |
| |
| content: { |
| pipeline: { |
| exclude: ['node_modules'] |
| } |
| }, |
| |
| presets: [ |
| |
| presetUno(), |
| |
| presetTypography(), |
| presetRemToPx({ |
| baseFontSize: 4 |
| }) |
| ], |
| |
| rules: [ |
| |
| |
| ], |
| |
| shortcuts: { |
| 'm-0-auto': 'm-0 ma', |
| 'wh-full': 'w-full h-full', |
| 'flex-center': 'flex justify-center items-center', |
| 'flex-x-center': 'flex justify-center', |
| 'flex-y-center': 'flex items-center', |
| 'text-overflow': 'overflow-hidden whitespace-nowrap text-ellipsis', |
| 'text-break': 'whitespace-normal break-all break-words' |
| } |
| }); |
# 4.main.ts 引入
virtual:uno.css`
# 5. 使用插件 @unocss/preset-rem-to-px
这个插件的作用就是将 unocss 的预设单位 rem 转换成 px
| pnpm add @unocss/preset-rem-to-px -D |
# 6. VSCode
安装 UnoCSS
插件
![img]()
再看下具体使用方式和实际效果:
代码 | 效果 |
---|
![img]() | ![image-20230222220856251]() |
如果 UnoCSS
插件智能提示不生效,请参考:VSCode 插件 UnoCSS 智能提示不生效解决。
# 11. 配置 SVG 图标
通过 vite-plugin-svg-icons 插件整合 Iconfont
第三方图标库实现本地图标
参考: vite-plugin-svg-icons 安装文档
# 1. 安装依赖
| pnpm install -D fast-glob@3.2.11 |
| pnpm install -D vite-plugin-svg-icons@2.0.1 |
# 2. 创建 src/assets/icons
目录
放入从 Iconfont 复制的 svg
图标
# 3. main.ts
引入注册脚本
| |
| import 'virtual:svg-icons-register'; |
# 4. vite.config.ts
配置插件
| |
| import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'; |
| |
| export default ({command, mode}: ConfigEnv): UserConfig => { |
| return ( |
| { |
| plugins: [ |
| createSvgIconsPlugin({ |
| |
| iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')], |
| |
| symbolId: 'icon-[dir]-[name]', |
| }) |
| ] |
| } |
| ) |
| } |
# 5.SVG 组件封装
| <!-- src/components/SvgIcon/index.vue --> |
| <script setup lang="ts"> |
| const props = defineProps({ |
| prefix: { |
| type: String, |
| default: "icon", |
| }, |
| iconClass: { |
| type: String, |
| required: false, |
| }, |
| color: { |
| type: String, |
| }, |
| size: { |
| type: String, |
| default: "1em", |
| }, |
| }); |
| |
| const symbolId = computed(() => `#${props.prefix}-${props.iconClass}`); |
| </script> |
| |
| <template> |
| <svg |
| aria-hidden="true" |
| class="svg-icon" |
| :style="'width:' + size + ';height:' + size" |
| > |
| <use :xlink:href="symbolId" :fill="color" /> |
| </svg> |
| </template> |
| |
| <style scoped> |
| .svg-icon { |
| display: inline-block; |
| outline: none; |
| width: 1em; |
| height: 1em; |
| vertical-align: -0.15em; |
| fill: currentColor; |
| overflow: hidden; |
| } |
| </style> |
# 6. 组件使用
| |
| <template> |
| <el-button type="info"><svg-icon icon-class="block"/>SVG 本地图标</el-button> |
| </template> |
# 12. 配置 Element Plus
# 1. 安装 unpligin 自动导入插件
| pnpm install unplugin-vue-components unplugin-auto-import -D |
# 2. 安装 Element Plus
| pnpm install element-plus |
# 3. 安装自动导入 Icon 依赖
| pnpm install unplugin-icons -D |
# 3. 配置 vite.config.ts
参考: element-plus-best-practices - vite.config.ts
| |
| import vue from "@vitejs/plugin-vue"; |
| import { UserConfig, ConfigEnv, loadEnv, defineConfig } from "vite"; |
| |
| import AutoImport from "unplugin-auto-import/vite" |
| import Compnents from "unplugin-vue-components/vite" |
| |
| import { ElementPlusResolver } from "unplugin-vue-components/resolvers"; |
| import Icons from "unplugin-icons/vite"; |
| import IconsResolver from "unplugin-icons/resolver"; |
| |
| import { resolve } from "path"; |
| |
| const pathSrc = resolve(__dirname, "src"); |
| export default ({ mode }: ConfigEnv): UserConfig => { |
| return { |
| resolve: { |
| alias: { |
| "@": pathSrc, |
| } |
| }, |
| plugins: [ |
| AutoImport({ |
| resolvers: [ |
| |
| |
| imports: ["vue", "@vueuse/core",], |
| resolvers: [ |
| |
| ElementPlusResolver(), |
| |
| IconsResolver({}), |
| ], |
| |
| vueTemplate: true, |
| |
| dts: false, |
| |
| }), |
| Components({ |
| resolvers: [ |
| |
| ElementPlusResolver(), |
| |
| IconsResolver({ |
| |
| enabledCollections: ["ep"], |
| }), |
| ], |
| |
| dirs: ["src/components", "src/**/components"], |
| |
| dts: false, |
| |
| }), |
| Icons({ |
| |
| autoInstall: true, |
| }), |
| ], |
| }; |
| }; |
# 4. 在 main.ts 引入
注意:按需引入时 element-plus 不需要在 main.ts 中引入,插件会自动挂载处理,可以在全局直接使用
这里在 main.ts 中引入 element-plus 样式与图标
| import * as ElementPlusIconsVue from '@element-plus/icons-vue'; |
| import 'element-plus/dist/index.css'; |
| |
| |
| |
| for (const [key, component] of Object.entries(ElementPlusIconsVue)) { |
| app.component(key, component); |
| } |
# 13. 配置 Element Plus 暗黑模式
Element Plus 2.2.0 版本开始支持暗黑模式,启用方式参考 Element Plus 官方文档 - 暗黑模式, 官方也提供了示例 element-plus-vite-starter 模版 。
这里根据官方文档和示例讲述如何使用 VueUse 的 useDark 方法实现暗黑模式的动态切换。
# 1. 导入 Element Plus 暗黑模式变量
| |
| import 'element-plus/theme-chalk/dark/css-vars.css' |
# 2. 切换暗黑模式设置
| |
| <script setup lang="ts"> |
| |
| import IconEpSunny from '~icons/ep/sunny'; |
| import IconEpMoon from '~icons/ep/moon'; |
| |
| /** |
| * 暗黑模式 |
| */ |
| const settingsStore = useSettingsStore(); |
| const isDark = useDark(); |
| const toggleDark = () => useToggle(isDark); |
| |
| </script> |
| |
| <template> |
| <div class="settings-container"> |
| <h3 class="text-base font-bold">项目配置</h3> |
| <el-divider>主题</el-divider> |
| |
| <div class="flex justify-center" @click.stop> |
| <el-switch |
| v-model="isDark" |
| @change="toggleDark" |
| inline-prompt |
| :active-icon="IconEpMoon" |
| :inactive-icon="IconEpSunny" |
| active-color="var(--el-fill-color-dark)" |
| inactive-color="var(--el-color-primary)" |
| /> |
| </div> |
| </div> |
| </template> |
# 3. 自定义变量
除了 Element Plus 组件样式之外,应用中还有很多自定义的组件和样式,像这样的:
![img]()
应对自定义组件样式实现暗黑模式步骤如下:
# 1. 新建 src/styles/dark.scss
| html.dark { |
| |
| .navbar { |
| background-color: #141414; |
| } |
| } |
# 2. 在 Element Plus 的样式之后导入它
| |
| import 'element-plus/theme-chalk/dark/css-vars.css' |
| import '@/styles/dark.scss' |
# 14. 配置 环境变量
新建 .env(所有环境生效).env.development(开发环境配置) .env.production(生产环境配置)
# 1. 定义变量
以 VITE_ 为前缀定义变量
| VITE_BASE_URL = '//127.0.0.1:9000/api' |
# 2. 定义变量 ts 类型
修改 vite-env.d.ts
| |
| interface ImportMetaEnv { |
| readonly VITE_BASE_URL: string; |
| } |
| |
| interface ImportMeta { |
| readonly env: ImportMetaEnv; |
| } |
# 3. 使用变量
| import.meta.env.VITE_BASE_URL |
# 4. 在 vite.config.ts 中使用环境变量
使用 loadEnv 读取环境变量
| import { defineConfig, loadEnv } from 'vite'; |
| |
| |
| export default ({ mode }) => { |
| console.log('mode', loadEnv(mode, process.cwd()).VITE_BASE_URL); |
| return defineConfig({ |
| |
| }); |
| }; |
使用 yarn dev 启动命令,读取.env 与 .env.development 的内容
修改 package.json
| "scripts": { |
| "test":"vite --mode test", |
| }, |
使用 yarn test 启动命令,读取.env 与 .env.test 的内容
# 15. 配置 Axios
Axios 基于 promise 可以用于浏览器和 node.js 的网络请求库
参考: Axios 官方文档
# 1. 安装
# 2.Axios 工具类封装
| |
| import axios, { InternalAxiosRequestConfig, AxiosResponse } from 'axios'; |
| import { useUserStoreHook } from '@/store/modules/user'; |
| |
| |
| const service = axios.create({ |
| baseURL: import.meta.env.VITE_APP_BASE_API, |
| timeout: 50000, |
| headers: { 'Content-Type': 'application/json;charset=utf-8' } |
| }); |
| |
| |
| service.interceptors.request.use( |
| (config: InternalAxiosRequestConfig) => { |
| const userStore = useUserStoreHook(); |
| if (userStore.token) { |
| config.headers.Authorization = userStore.token; |
| } |
| return config; |
| }, |
| (error: any) => { |
| return Promise.reject(error); |
| } |
| ); |
| |
| |
| service.interceptors.response.use( |
| (response: AxiosResponse) => { |
| const { code, msg } = response.data; |
| |
| if (code === '00000') { |
| return response.data; |
| } |
| |
| ElMessage.error(msg || '系统出错'); |
| return Promise.reject(new Error(msg || 'Error')); |
| }, |
| (error: any) => { |
| if (error.response.data) { |
| const { code, msg } = error.response.data; |
| |
| if (code === 'A0230') { |
| ElMessageBox.confirm('当前页面已失效,请重新登录', '提示', { |
| confirmButtonText: '确定', |
| type: 'warning' |
| }).then(() => { |
| localStorage.clear(); |
| window.location.href = '/'; |
| }); |
| }else{ |
| ElMessage.error(msg || '系统出错'); |
| } |
| } |
| return Promise.reject(error.message); |
| } |
| ); |
| |
| |
| export default service; |
# 16. 配置 反向代理
修改 vite.config.ts
| export default defineConfig({ |
| ... |
| server: { |
| host: '0.0.0.0', |
| port: 8080, |
| open: true, |
| https: false, |
| proxy: { |
| '/api': { |
| target: '要代理的地址', |
| changeOrigin: true, |
| ws: true, |
| rewrite: (path: string) => path.replace(/^\/api/, ''), |
| }, |
| }, |
| }, |
| }); |
# 17. 配置 打包
修改 vite.config.ts
# 1. 分包
通过 () => import () 形式加载的组件会自动分包,第三方插件需手动分包
| build: { |
| rollupOptions: { |
| output: { |
| manualChunks: { |
| vue: ['vue', 'pinia', 'vue-router'], |
| elementIcons: ['@element-plus/icons-vue'], |
| }, |
| }, |
| }, |
| }, |
# 2. 生成.gz 文件
- 安装
| pnpm install vite-plugin-compression -D |
- 修改 vite.config.ts
默认情况下插件在开发 (serve) 和生产 (build) 模式中都会调用,使用 apply 属性指明它们仅在 'build' 或'serve' 模式时调用
这里打包生成 .gz 插件仅需在打包时使用
| import viteCompression from 'vite-plugin-compression' |
| |
| plugins: [ |
| |
| { |
| ...viteCompression(), |
| apply: 'build', |
| }, |
| ], |
# 3.js 和 css 文件夹分离
| export default defineConfig({ |
| build: { |
| rollupOptions: { |
| output: { |
| chunkFileNames: "static/js/[name]-[hash].js", |
| entryFileNames: "static/js/[name]-[hash].js", |
| assetFileNames: "static/[ext]/[name]-[hash].[ext]", |
| }, |
| }, |
| }, |
| }); |
# 4. 分析生成包的大小
1. 安装
- 修改 vite.config.ts
| import { visualizer } from 'rollup-plugin-visualizer'; |
| |
| plugins: [ |
| |
| visualizer({ open: true }), |
| ], |
# 18.vite 与 webpack 使用区别
# 1. 静态资源处理
webpack:使用 require 处理
vite:使用 new URL (url, import.meta.url).href 处理
import.meta.url 包含了对于目前 ES 模块的绝对路径
new URL (url [, base]) 构造函数返回一个新创建的 URL 对象,如果 url 是相对 URL,则会将 base 用作基准 URL。如果 url 是绝对 URL,则无论参数 base 是否存在,都将被忽略
| new URL('../assets/images/home.png', import.meta.url).href |
| |
| |
| |
| |
| |
| |
# 2. 组件自动化注册
webpack
| <script> |
| const path = require('path'); |
| |
| const files = require.context('@/components/BaseEchartsModel', false, /\.vue$/); |
| const modules = {}; |
| files.keys().forEach((key) => { |
| const name = path.basename(key, '.vue'); |
| modules[name] = files(key).default || files(key); |
| }); |
| export default { |
| name: 'BaseEcharts', |
| components: modules, |
| }; |
| </script> |
vite
| <script setup lang="ts"> |
| |
| import.meta.glob('@/components/BaseEchartsModel/*.vue'); |
| </script> |