一、创建项目
1.1 技术栈
1.2 vite 项目初始化
npm init vite@latest vue3-element-admin --template vue-ts
1.3 src 路径别名配置
Vite 配置
配置 vite.config.ts
javascript
// https://vitejs.dev/config/
import { UserConfig, ConfigEnv, loadEnv, defineConfig } from 'vite'
import path from 'path'
import vue from '@vitejs/plugin-vue'
const pathSrc = path.resolve(__dirname, 'src')
export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
return {
// 路径别名
resolve: {
alias: {
'@': pathSrc
}
},
plugins: [vue()]
}
})
安装@types/node
import path from 'path'
编译器报错:TS2307: Cannot find module 'path' or its corresponding type declarations.
本地安装 Node 的 TypeScript 类型描述文件即可解决编译器报错
javascript
npm install @types/node --save-dev
TypeScript 编译配置
同样还是import path from 'path'
编译报错: TS1259: Module '"path"' can only be default-imported using the 'allowSyntheticDefaultImports' flag
因为 typescript 特殊的 import 方式 , 需要配置允许默认导入的方式,还有路径别名的配置
javascript
// tsconfig.json
{
"compilerOptions": {
"baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
"paths": { //路径映射,相对于baseUrl
"@/*": ["src/*"]
},
"allowSyntheticDefaultImports": true // 允许默认导入
}
}
1.4 unplugin 自动导入
Element Plus 官方文档中推荐 按需自动导入
的方式,而此需要使用额外的插件 unplugin-auto-import
和 unplugin-vue-components
来导入要使用的组件。所以在整合 Element Plus
之前先了解下自动导入
的概念和作用
概念
为了避免在多个页面重复引入 API
或 组件
,由此而产生的自动导入插件来节省重复代码和提高开发效率。
安装插件依赖
javascript
npm install -D unplugin-auto-import unplugin-vue-components
vite.config.ts - 自动导入配置
先创建好 /src/types
目录用于存放自动导入函数和组件的TS类型声明文件,再进行自动导入配置
javascript
// vite.config.ts
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import path from "path";
const pathSrc = path.resolve(__dirname, "src");
plugins: [
AutoImport({
// 自动导入 Vue 相关函数,如:ref, reactive, toRef 等
imports: ["vue"],
eslintrc: {
enabled: true, // 是否自动生成 eslint 规则,建议生成之后设置 false
filepath: "./.eslintrc-auto-import.json", // 指定自动导入函数 eslint 规则的文件
},
dts: path.resolve(pathSrc, "types", "auto-imports.d.ts"), // 指定自动导入函数TS类型声明文件路径
}),
Components({
dts: path.resolve(pathSrc, "types", "components.d.ts"), // 指定自动导入组件TS类型声明文件路径
}),
]
.eslintrc.cjs - 自动导入函数 eslint 规则引入
javascript
"extends": [
"./.eslintrc-auto-import.json"
],
tsconfig.json - 自动导入TS类型声明文件引入
javascript
{
"include": ["src/**/*.d.ts"]
}
自动导入效果
1.5 整合 Element Plus
安装 Element Plus
javascript
npm install element-plus
安装自动导入 Icon 依赖
javascript
npm i -D unplugin-icons
vite.config.ts 配置
javascript
// vite.config.ts
import vue from "@vitejs/plugin-vue";
import { UserConfig, ConfigEnv, loadEnv, defineConfig } from "vite";
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
import Icons from "unplugin-icons/vite";
import IconsResolver from "unplugin-icons/resolver";
export default ({ mode }: ConfigEnv): UserConfig => {
return {
plugins: [
// ...
AutoImport({
// ...
resolvers: [
// 自动导入 Element Plus 相关函数,如:ElMessage, ElMessageBox... (带样式)
ElementPlusResolver(),
// 自动导入图标组件
IconsResolver({}),
]
vueTemplate: true, // 是否在 vue 模板中自动导入
dts: path.resolve(pathSrc, 'types', 'auto-imports.d.ts') // 自动导入组件类型声明文件位置,默认根目录
}),
Components({
resolvers: [
// 自动导入 Element Plus 组件
ElementPlusResolver(),
// 自动注册图标组件
IconsResolver({
enabledCollections: ["ep"] // element-plus图标库,其他图标库 https://icon-sets.iconify.design/
}),
],
dts: path.resolve(pathSrc, "types", "components.d.ts"), // 自动导入组件类型声明文件位置,默认根目录
}),
Icons({
// 自动安装图标库
autoInstall: true,
}),
],
};
};
示例代码
javascript
<!-- src/components/HelloWorld.vue -->
<div>
<el-button type="success"><i-ep-SuccessFilled />Success</el-button>
<el-button type="info"><i-ep-InfoFilled />Info</el-button>
<el-button type="warning"><i-ep-WarningFilled />Warning</el-button>
<el-button type="danger"><i-ep-WarnTriangleFilled />Danger</el-button>
</div>
1.6 整合 SVG 图标
安装依赖
javascript
npm install -D fast-glob@3.2.11 vite-plugin-svg-icons@2.0.1
创建 src/assets/icons
目录 , 放入从 Iconfont 复制的 svg
图标
main.ts 引入注册脚本
javascript
// src/main.ts
import 'virtual:svg-icons-register';
vite.config.ts 配置插件
javascript
// 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格式
symbolId: 'icon-[dir]-[name]',
})
]
}
)
}
SVG 组件封装
javascript
<!-- 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; /* 因icon大小被设置为和字体大小一致,而span等标签的下边缘会和字体的基线对齐,故需设置一个往下的偏移比例,来纠正视觉上的未对齐效果 */
fill: currentColor; /* 定义元素的颜色,currentColor是一个变量,这个变量的值就表示当前元素的color值,如果当前元素未设置color值,则从父元素继承 */
overflow: hidden;
}
</style>
组件使用
javascript
<!-- src/components/HelloWorld.vue -->
<template>
<el-button type="info"><svg-icon icon-class="block"/>SVG 本地图标</el-button>
</template>
1.7 整合 SCSS
一款CSS预处理语言,SCSS 是 Sass 3 引入新的语法,其语法完全兼容 CSS3,并且继承了 Sass 的强大功能。
安装依赖
npm i -D sass
创建 variables.scss
变量文件,添加变量 $bg-color
定义,注意规范变量以 $
开头
// src/styles/variables.scss
$bg-color:#242424;
Vite
配置导入 SCSS
全局变量文件
javascript
// vite.config.ts
// 路径别名
resolve: {
alias: {
'@': pathSrc
}
},
css: {
// CSS 预处理器
preprocessorOptions: {
//define global scss variable
scss: {
javascriptEnabled: true,
additionalData: `@use "@/styles/variables.scss" as *;`
}
}
}
style
标签使用SCSS
全局变量
javascript
<!-- src/components/HelloWorld.vue -->
<template>
<div class="box" />
</template>
<style lang="scss" scoped>
.box {
width: 100px;
height: 100px;
background-color: $bg-color;
}
</style>
上面导入的 SCSS
全局变量在 TypeScript
不生效的,需要创建一个以 .module.scss
结尾的文件
javascript
// src/styles/variables.module.scss
// 导出 variables.scss 文件的变量
:export{
bgColor:$bg-color
}
TypeScript
使用 SCSS
全局变量
javascript
<!-- src/components/HelloWorld.vue -->
<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>
1.8 整合 UnoCSS
UnoCSS 是一个具有高性能且极具灵活性的即时原子化 CSS 引擎 。
安装依赖
javascript
npm install -D unocss
vite.config.ts 配置
javascript
// vite.config.ts
import UnoCSS from 'unocss/vite'
export default {
plugins: [
UnoCSS({ /* options */ }),
],
}
main.ts
引入 uno.css
javascript
// src/main.ts
import 'uno.css'
VSCode
安装 UnoCSS
插件
1.9 整合 Pinia
Pinia 是 Vue 的专属状态管理库,它允许你跨组件或页面共享状态。
安装依赖
javascript
npm install pinia
javascript
//持久化
npm i pinia-plugin-persistedstate
创建 stores/index.ts
javascript
// 创建大仓库
import { createPinia } from 'pinia'
// pinia持久化
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
// createPinia方法可以用于创建大仓库
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
// 对外暴露,安装仓库
export default pinia
main.ts引入
javascript
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import 'virtual:svg-icons-register'
import 'uno.css'
// 引入仓库pinia
import pinia from './stores/index.ts'
// 创建app
const app = createApp(App)
// 注册pinia
app.use(pinia)
// 挂载
app.mount('#app')
创建 stores/modules/counter.ts
javascript
import { defineStore } from 'pinia'
// defineStore方法执行会返回一个函数,函数的作用就是让组件可以获取到仓库数据
const counterStore = defineStore('counter', {
// 开启数据持久化
persist: {
// enabled: true, // true 表示开启持久化保存,默认localStorage
key: 'counter', // 默认会以 store 的 id 作为 key
storage: localStorage
},
// 可以通过为属性指定选项来配置持久化方式persist
// persist: {
// paths: ['isCollapse'],
// storage: sessionStorage
// },
// 存储数据state
state: (): any => {
return {
count: 5
}
},
// 该函数没有上下文数据,所以获取state中的变量需要使用this
actions: {
increment() {
this.count++
console.log(this.count)
}
},
// 计算属性,和vuex是使用一样,getters里面不是方法,是计算返回的结果值
getters: {
double(state) {
return state.count * 2
}
}
})
// 对外暴露方法
export default counterStore
在App.vue引入试用
javascript
import useCounterStore from '@/stores/modules/counter.ts'
const counterStore = useCounterStore()
<el-button type="primary" @click="counterStore.increment">count++</el-button>
2.0 环境变量
Vite 环境变量主要是为了区分开发、测试、生产等环境的变量
参考: Vite 环境变量配置官方文档
env配置文件
项目根目录新建 .env.development
、.env.production
环境变量智能提示
新建 src/types/env.d.ts
文件存放环境变量TS类型声明
javascript
// src/types/env.d.ts
interface ImportMetaEnv {
/**
* 应用标题
*/
VITE_APP_TITLE: string;
/**
* 应用端口
*/
VITE_APP_PORT: number;
/**
* API基础路径(反向代理)
*/
VITE_APP_BASE_API: string;
/**
* 接口地址
*/
VITE_APP_API_URL: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
使用自定义环境变量就会有智能提示,环境变量的读取和使用请看下一节的跨域处理中的 vite.config.ts
的配置。
2.1 反向代理解决跨域
跨域原理
浏览器同源策略: 协议、域名和端口都相同是同源,浏览器会限制非同源请求读取响应结果。
本地开发环境通过
Vite
配置反向代理解决浏览器跨域问题,生产环境则是通过nginx
配置反向代理 。
vite.config.ts
配置代理
javascript
// 路径别名
resolve: {
alias: {
'@': pathSrc
}
},
server: {
// 允许IP访问
host: "0.0.0.0",
// 应用端口 (默认:3000)
port: Number(env.VITE_APP_PORT),
// 运行是否自动打开浏览器
open: true,
proxy: {
/** 代理前缀为 /dev-api 的请求 */
[env.VITE_APP_BASE_API]: {
changeOrigin: true,
// 接口地址
target: env.VITE_APP_API_URL,
rewrite: (path) =>
path.replace(new RegExp("^" + env.VITE_APP_BASE_API), ""),
},
},
},
根目录nginx.conf
javascript
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
server {
listen 3000;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
# 代理转发请求至网关,prod-api标识解决跨域,vapi.youlai.tech 线上接口地址,注意后面/
location /prod-api/ {
proxy_pass https://web-test.digitmold.com:9901/;
}
# 代理转发请求至网关,prod-api标识解决跨域,vapi.youlai.tech 线上接口地址,注意后面/
location /prod-dme-api/ {
proxy_pass https://web-test2.digitmold.com:9901/;
}
}
}