本项目学习对象是优秀的后台系统模板vben-admin,期待通过学习解读该项目,熟悉前端项目工程化。
内容
- VUEX 状态管理工具
- Vue Router 路由管理
1. VUEX 状态管理工具
1.1 安装
npm install vuex@next --save
1.2 自动导入modules文件夹中写的module
src/store/index.ts
import type { App } from 'vue'
import { createStore } from 'vuex'
// 导出module目录中的内容
const modules: any = {}
const modulesFiles = import.meta.globEager('./module/*.ts')
Object.keys(modulesFiles).forEach((path) => {
const moduleName = path.split('/')[2].split('.')[0]
modules[moduleName] = modulesFiles[path].default
modules[moduleName].namespaced = true // 防止模块命名冲突 设置后调用mutations和action需要
})
// 创建vuex实例
const store = createStore({
modules
})
/**
* @description: 用于在main.js中注册vuex实例
* @param {App} app
* @return {*}
*/
export function setupStore(app: App<Element>) {
app.use(store)
}
/**
* @description: 抛出vuex实例,方便使用
* @param {*}
* @return {*}
*/
export function useStore() {
return store
}
1.3 例子
src/store/module/user.ts
import type { UserInfo } from '#/store.d'
export interface UserState {
userInfo: UserInfo | null
}
export default {
state: (): UserState => ({
userInfo: null
}),
mutations: {
SET_USER_INFO(state: UserState, info: UserInfo) {
state.userInfo = info
}
},
getters: {
getUserInfo(state: UserState) {
return state.userInfo
}
}
}
src/types/store.d.ts —用到的类型声明
export interface UserInfo {
userId: string | number
username: string
realName: string
}
1.4 使用
1.4.1 在main.ts入口文件中挂载
import { createApp } from 'vue'
import { setupStore } from '@/store'
import App from './App.vue'
const app = createApp(App)
// 挂载vuex
setupStore(app)
app.mount('#app')
src/App.vue
1.4.2 在App.vue中调用看看
<template>
{{ userinfo }}
</template>
<script lang="ts" setup>
import { useStore } from '@/store'
const Store = useStore()
const userinfo = Store.getters['user/getUserInfo']
console.log(userinfo)
</script>
控制台正确输入了null
2. Vue Router 路由管理
2.1安装
npm install vue-router@4 -S
2.2 router配置
src/router/index.ts —router的入口
import type { App } from 'vue'
import type { RouteRecordRaw } from 'vue-router'
import { createRouter, createWebHashHistory } from 'vue-router'
import { WHITE_NAME_LIST } from './constant'
import { basicRoutes } from './routes'
// 路由
export const router = createRouter({
// 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
history: createWebHashHistory(), // 使用无需对服务器进行设置的hash路由方式
routes: [...basicRoutes] as RouteRecordRaw[] // 传入路由列表
})
// 初始化挂载路由
export function setupRouter(app: App<Element>) {
app.use(router)
}
// 重置路由
export function resetRouter() {
router.getRoutes().forEach((route) => {
const { name } = route
// 如果路由存在且不在白名单中则删除
if (name && !WHITE_NAME_LIST.includes(name as string)) {
router.hasRoute(name) && router.removeRoute(name)
}
})
}
src/router/constant.ts —常量配置
export const REDIRECT_NAME = 'Redirect'
// 白名单
export const WHITE_NAME_LIST = [REDIRECT_NAME, 'Login']
export const LAYOUT = () => import('@/layout/index.vue')
export const EMPTY_LAYOUT = () => import('@/layout/Empty.vue')
export const EXCEPTION_COMPONENT = () => import('@/views/sys/exception/index.vue')
src/router/routes/index.ts — 路由列表的获取
import type { RouteRecordRaw } from 'vue-router'
import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from './basic'
const modules = import.meta.globEager('./modules/**/*.ts')
const routeModuleList: RouteRecordRaw[] = []
Object.keys(modules).forEach((key) => {
const mod = modules[key].default || {}
const modList = Array.isArray(mod) ? [...mod] : [mod]
routeModuleList.push(...modList)
})
export const RootRoute = {
path: '/',
name: 'Root',
redirect: '/dashboard',
meta: {
title: 'Root'
}
}
export const LoginRoute = {
path: '/login',
name: 'Login',
component: () => import('@/views/sys/login/index.vue'),
meta: {
title: '登录页'
}
}
export const basicRoutes = [LoginRoute, RootRoute, REDIRECT_ROUTE]
// 现在默认的异步路由(即接口获取的)是全部的路由
export const asyncRoutes = [PAGE_NOT_FOUND_ROUTE, ...routeModuleList]
src/router/routes/basic.ts —最基础的路由
import type { AppRouteRecordRaw } from '@/router/types'
import { REDIRECT_NAME, LAYOUT, EXCEPTION_COMPONENT } from '@/router/constant'
export const PAGE_NOT_FOUND_ROUTE: AppRouteRecordRaw = {
path: '/:path(.*)*',
name: 'ErrorPage',
component: LAYOUT,
meta: {
title: 'ErrorPage',
hideBreadcrumb: true
},
children: [
{
path: '/:path(.*)*',
name: 'ErrorPage',
component: EXCEPTION_COMPONENT,
meta: {
title: 'ErrorPage',
hideBreadcrumb: true
}
}
]
}
export const REDIRECT_ROUTE: AppRouteRecordRaw = {
path: '/redirect',
name: REDIRECT_NAME,
component: LAYOUT,
meta: {
title: REDIRECT_NAME,
hideBreadcrumb: true
},
children: [
{
path: '/redirect/:path(.*)',
name: REDIRECT_NAME,
component: () => import('@/views/sys/redirect/index.vue'),
meta: {
title: REDIRECT_NAME,
hideBreadcrumb: true
}
}
]
}
src/router/types.ts —路由相关的类型声明
import type { RouteRecordRaw } from 'vue-router'
import { defineComponent } from 'vue'
export type Component<T = any> = ReturnType<typeof defineComponent> | (() => Promise<typeof import('*.vue')>) | (() => Promise<T>)
export interface RouteMeta {
// title
title: string
type?: string
icon?: string
}
export interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {
name: string
meta: RouteMeta
component?: Component | string
components?: Component
children?: RouteRecordRaw[]
props?: any
fullPath?: string
src/App.vue
<template>
<div><router-view /></div>
</template>
<script lang="ts" setup></script>
src/views/sys/login/index.vue
<template>
<div>登录</div>
</template>
<script lang="ts">
export default { name: 'LoginPage' }
</script>
1.3 使用
src/main.ts
访问login页面