Article directory
1 Introduction
This article uses Vue3 + Vite + Element Plus + Vue Router + Pinia + Typescript to build the project
2 Project initialization
npm init vue@latest
It is recommended to use pnpm, if pnpm is not installed, you can use npm
cd vue-project
pnpm i
pnpm dev
3 code style processing
- Change the file type of .prettierrc.json to .prettierrc.js, and replace the file content with:
module.exports = {
singleQuote: true, // 使用单引号
semi: false, // 行尾不需要有分号
trailingComma: 'none', // 行尾不需要有逗号
endOfLine: 'auto', // 换行符使用 lf
printWidth: 100 // 一行最多 100 字符
}
- .eslintrc.cjs adds rules to resolve conflicts between eslint and prettier
rules: {
'prettier/prettier': [
'warn',
{
singleQuote: true,
trailingComma: false
}
]
}
4 Dependency installation
- Functional dependencies
pnpm i element-plus axios
- Automatically import related dependencies
pnpm i unplugin-auto-import unplugin-vue-components unplugin-icons -D
5 Automatic import
- Automatically import vue, vue-router, element plus components and API, and subsequent development does not need to manually import ref, useRouter, etc.
- For details on automatic import of Element Plus, see Vue3 + Element Plus On-Demand Import - Automatic Import
vite.config.ts file add the following:
Add "types" to tsconfig.json file includes to avoid ts error找不到名称“ref”
"include": ["env.d.ts", "src/**/*", "src/**/*.vue", "types"]
6 Route interception
router/index.ts file add the following:
// 路由拦截
router.beforeEach((to, from, next) => {
const token: string | null = getUser('token')
// 已经登录过请求 login 界面会直接回到首页
if (token) {
to.path === '/login' ? next('/') : next()
return
}
// 请求需要权限的界面,会转到 login 界面
if (to.meta.requireAuth) {
next({
path: '/login',
replace: true
})
return
}
next()
})
7 Add environment variable configuration file
- Add a new .env.development file in the root directory
# 开发环境
# 配置文件路径
VITE_PUBLIC_PATH = /
# 接口代理
VITE_PROXY_DOMAIN = /api
# 后端地址
VITE_PROXY_DOMAIN_REAL = http://192.168.60.66:8000
- Add a new .env.production file in the root directory
# 生产环境
# 项目打包路径
VITE_PUBLIC_PATH = ./
# 后端地址
VITE_PROXY_DOMAIN_REAL = /api
8 axios package
Add utils/http/index.ts file
import axios from 'axios'
import type {
AxiosError, AxiosResponse } from 'axios'
import router from '@/router'
enum Msgs {
'操作成功' = 200,
'无权操作' = 401,
'系统内部错误' = 500
}
// 避免多个接口401弹出多个弹框
let isRefreshing = false
const {
DEV, VITE_PROXY_DOMAIN, VITE_PROXY_DOMAIN_REAL } = import.meta.env
// 创建http实例
const instance = axios.create({
baseURL: DEV ? VITE_PROXY_DOMAIN : VITE_PROXY_DOMAIN_REAL,
timeout: 2000,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
// 添加请求拦截器
instance.interceptors.request.use((config) => {
config.headers = config.headers || {
}
// const token = getToken()
// if (token) {
// config.headers['User-Token'] = token
// }
return config
})
// 添加响应拦截器
instance.interceptors.response.use(
(res: AxiosResponse) => res,
(err: AxiosError) => {
const {
response } = err
if (!response) return Promise.reject(err)
const errCodes = [401, 403, 500]
const code = response.status
if (errCodes.includes(response.status)) {
// removeUserInfo()
ElMessageBox({
message: Msgs[code],
title: '提示'
})
.then(() => {
router.push('Login')
})
.finally(() => (isRefreshing = false))
return Promise.reject(err)
}
ElMessage(Msgs[code] || '请求失败')
return Promise.reject(err)
}
)
const http = {
get: (url = '', params = {
}) => instance.get(url, {
params }),
post: (url = '', data = {
}, config = {
}) => instance.post(url, data, config),
put: (url = '', data = {
}) => instance.put(url, data),
delete: (url = '', data = {
}) => instance.delete(url, data),
patch: (url = '', data = {
}) => instance.patch(url, data)
}
export default http
use:
import http from '@/utils/http'
http.get('user').then((res) => {
console.log(res)
})
9 Startup and production packaging configuration
Modify the vite.config.ts file
import {
fileURLToPath, URL } from 'node:url'
import {
loadEnv, ConfigEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite' // 自动导入
import Components from 'unplugin-vue-components/vite' // 组件注册
import {
ElementPlusResolver } from 'unplugin-vue-components/resolvers' // elementPlus
import Icons from 'unplugin-icons/vite' // icon相关
import IconsResolver from 'unplugin-icons/resolver' // icon相关
export default ({
mode }: ConfigEnv) => {
const {
VITE_PUBLIC_PATH, VITE_PROXY_DOMAIN, VITE_PROXY_DOMAIN_REAL } = loadEnv(
mode,
process.cwd()
)
return {
base: VITE_PUBLIC_PATH, //打包路径
plugins: [
vue(),
AutoImport({
imports: ['vue', 'vue-router'],
dts: fileURLToPath(new URL('./types/auto-imports.d.ts', import.meta.url)),
resolvers: [
ElementPlusResolver(),
// 自动导入图标组件
IconsResolver({
prefix: 'Icon'
})
]
}),
Components({
dirs: ['src/views', 'src/layout', 'src/components'],
dts: fileURLToPath(new URL('./types/components.d.ts', import.meta.url)),
resolvers: [
ElementPlusResolver(),
IconsResolver({
enabledCollections: ['ep'] // 重点
})
]
}),
Icons({
autoInstall: true
})
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
// 启动服务配置
server: {
host: '0.0.0.0',
port: 8000,
open: false,
https: false,
proxy: {
[VITE_PROXY_DOMAIN]: {
target: VITE_PROXY_DOMAIN_REAL,
changeOrigin: true,
rewrite: (path: string) => path.replace(/^\/api/, '')
}
}
},
// 生产环境打包配置
build: {
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
},
rollupOptions: {
output: {
chunkFileNames: 'js/[name]-[hash].js',
entryFileNames: 'js/[name]-[hash].js',
manualChunks(id: string | string[]) {
if (id.includes('node_modules')) {
const arr = id.toString().split('node_modules/')
return arr[arr.length - 1].split('/')[0].toString()
}
}
}
}
}
}
}
10 Extremely fast construction
Use the yana-cli scaffolding, which provides a vue3 template and has built all the above content
npm i yana-cli
yana-cli create