vite + vue3 + element-plus + ts搭建一个后台管理系统架子
文章目录[隐藏]
这是我参与更文挑战的第2天
前言
- vite 作为
vue祖师爷尤大
的又一神作。值得我们使用 - 这篇文章主要通过 vite + vue3 + element-plus + ts搭建一个后台管理系统架子
1、安装
- 通过vite脚手架搭建我们第一个项目
yarn create @vitejs/app my-vue-app(自己项目的名称) --template vue-ts
复制代码
- 这串命令可以让我们生成一个基于TS的项目
- 目录结构如下
2、集成vue-router@next
- 安装支持
vue3
语法的vue-router
yarn add vue-router@next
复制代码
- 接下来我们在src目录下创建routes文件夹,并在routes文件夹下创建index.ts文件,写入一段测试路由信息
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
const routes: RouteRecordRaw[] = [
{
path: '/login',
component: () => import("../views/login/index.vue")
},
{
path: '/',
component: () => import("../views/home/index.vue")
},
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
复制代码
- 现在我们在路由文件中添加了登录组件和首页组件的路由
- 然后我们去main.ts中把导出的路由挂载到app中,并修改app.vue
//main.ts
import { createApp } from 'vue'
//引入我们导出的路由
import router from './routes'
import App from './App.vue'
createApp(App)
// 通过use的方法把路由信息挂载到app中
.use(router)
.mount('#app')
// app.vue
<template>
<router-view />
</template>
复制代码
- 做完这些就可以写login.vue和home.vue的测试组件了
// login.vue
<template>
<div>login</div>
</template>
// home.vue
<template>
<div>home</div>
</template>
复制代码
- 我们启动项目访问
localhost:3000/login
看到login字段,就证明路由已经集成成功了,我们直接输入localhost:3000
就可以进入到首页了
3、集成vuex@next
- 安装支持
vue3
语法的vuex
yarn add vuex@next
复制代码
- 接下来我们在src目录下创建store文件夹,并在store文件夹下创建index.ts文件,写入一段测试测试数据
- 举个?,我们实现一个累加的功能,在登录组件点击自增,在home组件中展示
import { createStore } from 'vuex'
const store = createStore({
state() {
return {
count: 0
}
},
mutations: {
// 累加功能
increment(state) {
state.count++
}
}
})
export default store
复制代码
- 这个时候我们会看到TS类型报错,这样修改即可
import { createStore } from 'vuex'
//定义一个state的接口
export interface State {
count: number
}
const store = createStore<State>({
state() {
return {
count: 0
}
},
mutations: {
// 累加功能
increment(state) {
state.count++
}
}
})
export default store
复制代码
- 接下来我们修改该main.ts
import { createApp } from 'vue'
//引入我们导出的路由
import router from './routes'
// 引入我们导出的vuex
import store from './store'
import App from './App.vue'
createApp(App)
// 通过use的方法把路由信息挂载到app中
.use(router)
// 通过use的方法把vuex信息挂载到vue中
.use(store)
.mount('#app')
复制代码
- 接下来我们在home组件中获取vuex中的内容
<template>
<div>vuex中的count:{{count}}</div>
</template>
<script setup>
import { useStore } from 'vuex'
const store = useStore()
const count = store.state.count
</script>
复制代码
- 接下来,我们在login组件中设置自增操作
<template>
<div>
login
<button @click="handleClickIncrement">累加</button>
<router-link to="/">去首页</router-link>
</div>
</template>
<script setup>
import { useStore } from "vuex";
const store = useStore();
function handleClickIncrement() {
store.commit("increment");
}
</script>
复制代码
- 最后我们启动项目,?看看效果图是啥样的
- 首先我们进入首页 count是0,然后我们进入login页面点击自增,再点击去首页,那么首页中的count就会实现累加的功能,由0变为1,实现了vuex的数据共享
4、集成element-plus
- 安装
yarn add element-plus
复制代码
- 引入方式 – 完整引入
import { createApp } from 'vue'
//引入我们导出的路由
import router from './routes'
// 引入我们导出的vuex
import store from './store'
// elementPlus完整引入
import ElementPlus from 'element-plus';
import 'element-plus/lib/theme-chalk/index.css';
import App from './App.vue'
createApp(App)
// 通过use的方法把路由信息挂载到app中
.use(router)
// 通过use的方法把vuex信息挂载到vue中
.use(store)
.use(ElementPlus)
.mount('#app')
复制代码
- 修改login的代码
<el-button type="primary" @click="handleClickIncrement">累加</el-button>
复制代码
- 这个时候
element-plus
就已经引入成功了 - 引入方式 – 按需加载
- 因为我们的项目是基于vite搭建的,不能使用阿里基于webpack
plugin-babel-import
这个按需加载包,奈何社区总有轮子哥vite-plugin-babel-import
一个基于vite的按需加载包 - 安装
yarn add vite-plugin-babel-import -D
复制代码
- 修改
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// 按需加载包
import vitePluginBabelImport from 'vite-plugin-babel-import'
export default defineConfig({
plugins: [vue(),
vitePluginBabelImport([
{
libraryName: 'element-plus',
libraryDirectory: 'es',
ignoreStyles: [],
style(name) {
return `element-plus/lib/theme-chalk/${name}.css`;
},
}
])]
})
复制代码
- 按需加载已经配置完成,接下来我们就把main.ts中的全局引入方式相关代码注释掉
// import ElementPlus from 'element-plus';
// import 'element-plus/lib/theme-chalk/index.css';
// .use(ElementPlus)
复制代码
- 接下来我们仍旧修改login组件中的代码
<template>
<div>
login
<el-button type="primary" @click="handleClickIncrement">累加</el-button>
<router-link to="/">去首页</router-link>
</div>
</template>
<script setup>
import { ElButton } from 'element-plus'
import { useStore } from "vuex";
const store = useStore();
function handleClickIncrement() {
store.commit("increment");
}
</script>
复制代码
- 这个时候我们再看效果图,依旧是没问题的
5、vite相关配置
- 支持
vue-jsx
语法
// 安装
yarn add vite-plugin-babel-import -D
//引包
// vuejsx支持
import vueJsx from "@vitejs/plugin-vue-jsx"
// 添加到plugin中
vueJsx()
复制代码
- 支持别名
// 安装 path 和 @types/node, @types/node是为了防止在vite.config.js中使用__dirname报错
yarn add path
yarn add @types/node -D
import { defineConfig } from "vite";
import path from 'path'
// vue支持
import vue from "@vitejs/plugin-vue";
// vuejsx支持
import vueJsx from "@vitejs/plugin-vue-jsx";
// 按需加载包
import vitePluginBabelImport from "vite-plugin-babel-import";
const resolve = dir => path.resolve(__dirname, dir)
export default defineConfig({
resolve: {
alias: {
"@": resolve("./src")
},
},
plugins: [
vue(),
vueJsx(),
vitePluginBabelImport([
{
libraryName: "element-plus",
libraryDirectory: "es",
ignoreStyles: [],
style(name) {
return `element-plus/lib/theme-chalk/${name}.css`;
},
},
]),
],
});
复制代码
这样我们就可以在项目中使用@
符号了
server
相关配置
import { defineConfig } from "vite";
import path from "path";
// vue支持
import vue from "@vitejs/plugin-vue";
// vuejsx支持
import vueJsx from "@vitejs/plugin-vue-jsx";
// 按需加载包
import vitePluginBabelImport from "vite-plugin-babel-import";
const resolve = (dir) => path.resolve(__dirname, dir);
export default defineConfig({
resolve: {
alias: {
"@": resolve("./src"),
},
},
server: {
port: 8080, //项目启动端口
open: true, //项目启动时是否自动打开浏览器
proxy: {
// 代理
"/foo": "http://localhost:4567/", //代理方式 /foo --> http://localhost:4567/foo
// 选项写法
"/api": {
target: "http://123.456.com", //代理方式 /api --> http://123.456.com
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
},
build: {
outDir: resolve("./dist1"), // 打包输出目录, 默认dist
},
plugins: [
vue(),
vueJsx(),
vitePluginBabelImport([
{
libraryName: "element-plus",
libraryDirectory: "es",
ignoreStyles: [],
style(name) {
return `element-plus/lib/theme-chalk/${name}.css`;
},
},
]),
],
});
复制代码
6、css预编译(less)
- 安装
yarn add less -D
复制代码
- 如果是用的是单文件组件,可以通过
<style lang="sass">
(或其他预处理器)自动开启。 - Vite 为 Sass 和 Less 改进了 @import 解析,以保证 Vite 别名也能被使用。另外,url() 中的相对路径引用的,与根文件不同目录中的 Sass/Less 文件会自动变基以保证正确性。
7、Glob导入
- 现在我们来看一个问题,当页面达到了一定的规模,routes/index.ts中的页面路由会非常的多,这个时候我们就会根据不同的模块去拆分成不同的文件存放对应的路由信息,然后再统一引入index.ts中,这个时候我们就在想能不能通过一种方式,去扫描某一个文件夹下的指定规则的文件,按照这种规则把当前文件夹下的文件动态的引入到当前组件,实现一个自动挂载的操作。如果大家对
node
中的fs
模块熟悉的话,就可以实现这样的功能(常见的栗子有nextjs、egg、nuxt这些基于约定由于配置的开发)。但我们前端的工程化项目最终是要打包在浏览器中的,没法利用node的fs模块,vite
是基于原生浏览器模块开发的。Vite 支持使用特殊的 import.meta.glob 函数从文件系统导入多个模块, 我们可以利用这一特性实现,前端项目动态引包的功能。 - 首先我们改造一下页面结构
- 这个时候我们对路由进行了拆分
-接下老我们把modules下所有TS后缀名的文件动态的引入routes/index.ts中
// 加载当前文件夹下modules文件夹下所有的ts结尾的文件
const modules = import.meta.glob("./modules/*.ts")
// 遍历 modules 对象的 key 值来访问相应的模块
for (const path in modules) {
const mod = await modules[path]()
console.log(mod);
}
复制代码
- 改造好后启动页面,会发现控制台报错
- 原因是
vite-plugin-babel-import
这个插件2.0.5版本不支持顶级await
的写法
解决方案就是把 vite-plugin-babel-import
插件版本降级到2.0.2
-这时vite.config.ts
又报错了
- 我们需要把
ignoreStyles
参数删掉就好了
- 再次启动项目,我们打开控制台可以看到信息routes/index.ts的打印信息了
- 我们展开module会看到default数组中存放的就是我们module文件夹下login和home TS文件中导出的数据了,
既然能拿到数据,那么我们就来改造下routes/index.ts吧
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
// 加载当前文件夹下modules文件夹下所有的ts结尾的文件
const modules = import.meta.glob("./modules/*.ts")
const routes: RouteRecordRaw[] = []
// 遍历 modules 对象的 key 值来访问相应的模块
for (const path in modules) {
const mod = await modules[path]()
// 数据添加到routes中
routes.push(...mod.default)
}
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
复制代码
-这个时候我们再次访问 login 和首页都是没问题的
- 到这个时候vite+vue基本架子就搭好了
8、后台管理系统-登录
- 登录的相关操作比较简单,我就直接贴代码了
<template>
<el-form
status-icon
label-width="100px"
class="login-form"
>
<el-form-item label="用户名">
<el-input
v-model="form.name"
></el-input>
</el-form-item>
<el-form-item label="密码">
<el-input
type="password"
v-model="form.pwd"
></el-input>
</el-form-item>
<el-form-item>
<el-button class="block" type="primary" @click="submitForm"
>提交</el-button
>
</el-form-item>
</el-form>
</template>
<script lang="ts" setup>
import { reactive } from "vue";
import { useRouter, useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
interface Form {
name: string,
pwd: string
}
const form = reactive<Form>({
name: "",
pwd: ""
});
const router = useRouter()
const route = useRoute()
function submitForm() {
const { name, pwd } = form
if(name && pwd) {
// 定时器模拟登录
setTimeout(()=>{
// 模拟信息存储到本地
sessionStorage.setItem("user",JSON.stringify({name,pwd}))
// 跳转到上一次访问的页面 如果用户直接进入登录页面,那么就跳转到 '/'首页
const path: string= (route.query as {url?: string}).url || "/"
router.push(path)
})
}else {
ElMessage.error("用户名密码不能?♀️为空")
}
}
</script>
<style lang="less">
.login-form{
width: 500px;
margin: 200px auto;
}
.block{
width:100%
}
</style>
复制代码
- 页面就这样的,还挺好看的,哈哈
9、首页改造
<template>
<div>
<el-row>
<el-col :span="24"
><div class="layout-head pdlr28">{{ userName || "--" }}</div></el-col
>
</el-row>
<el-row>
<el-col :span="4">
<el-menu
:uniqueOpened="true"
default-active="2"
class="menu-wrap"
background-color="#545c64"
text-color="#fff"
active-text-color="#ffd04b"
>
<el-submenu index="1">
<template #title>
<i class="el-icon-location"></i>
<span>导航一</span>
</template>
<el-menu-item-group>
<template #title>分组一</template>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template #title>选项4</template>
<el-menu-item index="1-4-1">选项1</el-menu-item>
</el-submenu>
</el-submenu>
<el-menu-item index="2">
<i class="el-icon-menu"></i>
<template #title>导航二</template>
</el-menu-item>
<el-menu-item index="3" disabled>
<i class="el-icon-document"></i>
<template #title>导航三</template>
</el-menu-item>
<el-menu-item index="4">
<i class="el-icon-setting"></i>
<template #title>导航四</template>
</el-menu-item>
<el-submenu index="5">
<template #title>
<i class="el-icon-location"></i>
<span>导航一</span>
</template>
<el-menu-item-group>
<template #title>分组一</template>
<el-menu-item index="5-1">选项1</el-menu-item>
<el-menu-item index="5-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="5-3">选项3</el-menu-item>
</el-menu-item-group>
</el-submenu>
</el-menu>
</el-col>
<el-col :span="20" class="pdl28">
<el-breadcrumb separator="/" class="pdt28 pdb28">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item><a href="/">活动管理</a>