一、后台管理系统技术选型
主流后台框架技术对比:
技术方案 |
开发效率 |
扩展性 |
学习曲线 |
Vue2 + ElementUI |
高 |
高 |
低 |
React + Ant Design |
中高 |
极高 |
中 |
Angular + Material |
中 |
高 |
高 |
纯HTML+jQuery |
低 |
低 |
低 |
二、系统架构设计
1. 分层架构设计
展现层 → 业务逻辑层 → 状态管理层 → 服务层 → 接口层
↑ ↑ ↑ ↑ ↑
组件库 Vue组件逻辑 Vuex状态管理 API封装 Axios
2. 权限控制流程
用户登录 → 获取权限 → 路由过滤 → 菜单生成 → 按钮控制
↑ ↑ ↑ ↑ ↑
JWT验证 角色权限树 动态路由添加 递归组件渲染 指令级控制
三、核心模块实现
// src/permission.js
import router from './router'
import store from './store'
import { getToken } from '@/utils/auth'
const whiteList = ['/login']
router.beforeEach(async (to, from, next) => {
// 获取token
const hasToken = getToken()
if (hasToken) {
if (to.path === '/login') {
next({ path: '/' })
} else {
// 检查用户权限是否已获取
const hasRoles = store.getters.roles && store.getters.roles.length > 0
if (hasRoles) {
next()
} else {
try {
// 获取用户信息
const { roles } = await store.dispatch('user/getInfo')
// 基于角色生成可访问路由
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
// 动态添加路由
router.addRoutes(accessRoutes)
// 确保addRoutes完成
next({ ...to, replace: true })
} catch (error) {
// 获取信息失败则重置token并返回登录页
await store.dispatch('user/resetToken')
next(`/login?redirect=${to.path}`)
}
}
}
} else {
if (whiteList.indexOf(to.path) !== -1) {
next()
} else {
next(`/login?redirect=${to.path}`)
}
}
})
2. 动态路由生成器
// src/store/modules/permission.js
import { asyncRoutes, constantRoutes } from '@/router'
function hasPermission(roles, route) {
if (route.meta && route.meta.roles) {
return roles.some(role => route.meta.roles.includes(role))
} else {
return true
}
}
export function filterAsyncRoutes(routes, roles) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (hasPermission(roles, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
res.push(tmp)
}
})
return res
}
const state = {
routes: [],
addRoutes: []
}
const mutations = {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
}
const actions = {
generateRoutes({ commit }, roles) {
return new Promise(resolve => {
let accessedRoutes
if (roles.includes('admin')) {
accessedRoutes = asyncRoutes || []
} else {
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
}
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
四、高级功能实现
1. 动态表单生成器
// src/components/DynamicForm.vue
export default {
props: {
formConfig: {
type: Array,
required: true
},
formData: {
type: Object,
required: true
},
formRules: {
type: Object,
default: () => ({})
}
},
methods: {
validate() {
return this.$refs.dynamicForm.validate()
},
resetFields() {
this.$refs.dynamicForm.resetFields()
}
}
}
2. 高性能表格组件
// src/components/VirtualTable.vue
export default {
props: {
data: {
type: Array,
required: true
},
columns: {
type: Array,
required: true
},
rowKey: {
type: String,
required: true
},
rowHeight: {
type: Number,
default: 48
},
bufferSize: {
type: Number,
default: 10
}
},
data() {
return {
startIndex: 0,
endIndex: 0,
offsetY: 0
}
},
computed: {
visibleData() {
return this.data.slice(this.startIndex, this.endIndex)
}
},
mounted() {
this.initTable()
window.addEventListener('resize', this.handleResize)
},
beforeDestroy() {
window.removeEventListener('resize', this.handleResize)
},
methods: {
initTable() {
this.$nextTick(() => {
const height = this.$refs.tableWrapper.clientHeight
const headerHeight = this.$refs.tableHeader.clientHeight
this.$refs.tableBody.style.height = `${height - headerHeight}px`
this.updateVisibleData()
})
},
handleScroll() {
const scrollTop = this.$refs.tableBody.scrollTop
this.updateVisibleData(scrollTop)
},
updateVisibleData(scrollTop = 0) {
const { clientHeight } = this.$refs.tableBody
const visibleCount = Math.ceil(clientHeight / this.rowHeight)
this.startIndex = Math.max(
0,
Math.floor(scrollTop / this.rowHeight) - this.bufferSize
)
this.endIndex = Math.min(
this.data.length,
this.startIndex + visibleCount + this.bufferSize * 2
)
this.offsetY = this.startIndex * this.rowHeight
},
handleResize() {
this.initTable()
}
}
}
五、性能优化策略
1. 组件级优化
// 1. 函数式组件
Vue.component('functional-button', {
functional: true,
render(createElement, context) {
return createElement('button', {
on: {
click: context.listeners.click
}
}, context.children)
}
})
// 2. 延迟加载非关键组件
const LazyComponent = () => ({
component: import('./LazyComponent.vue'),
loading: LoadingComponent,
error: ErrorComponent,
delay: 200,
timeout: 3000
})
// 3. 优化v-for性能
Vue.component('optimized-list', {
props: ['items'],
data() {
return {
itemKey: 'id' // 指定唯一key
}
},
template: `
`
})
// 4. 冻结大型数据列表
export default {
data() {
return {
bigData: Object.freeze(bigDataArray)
}
}
}
2. 全局性能优化
// 1. 路由懒加载
const router = new VueRouter({
routes: [
{
path: '/dashboard',
component: () => import('./views/Dashboard.vue')
}
]
})
// 2. 生产环境关闭提示
Vue.config.productionTip = false
Vue.config.devtools = false
// 3. 优化Vuex使用
const store = new Vuex.Store({
strict: process.env.NODE_ENV !== 'production',
plugins: [
createPersistedState({
key: 'vuex',
storage: window.sessionStorage,
reducer: state => ({
user: state.user,
permission: state.permission
})
})
]
})
// 4. 关键CSS内联
// 在index.html中通过webpack插件内联关键CSS
六、实战案例:数据可视化平台
1. 主控台布局
import { mapGetters } from 'vuex'
export default {
name: 'Dashboard',
data() {
return {
indicatorCards: [
{ title: '总销售额', value: 126560, icon: 'money' },
{ title: '访问量', value: 8846, icon: 'user' }
],
tableColumns: [
{ prop: 'date', label: '日期' },
{ prop: 'name', label: '名称' }
]
}
},
computed: {
...mapGetters([
'salesData',
'regionRanking',
'tableData'
])
},
created() {
this.$store.dispatch('dashboard/fetchData')
}
}
2. 权限按钮控制
// src/directives/permission.js
import store from '@/store'
function checkPermission(el, binding) {
const { value } = binding
const roles = store.getters && store.getters.roles
if (value && value instanceof Array) {
if (value.length > 0) {
const permissionRoles = value
const hasPermission = roles.some(role => {
return permissionRoles.includes(role)
})
if (!hasPermission) {
el.parentNode && el.parentNode.removeChild(el)
}
}
} else {
throw new Error(`需要指定权限数组,如v-permission="['admin']"`)
}
}
export default {
inserted(el, binding) {
checkPermission(el, binding)
},
update(el, binding) {
checkPermission(el, binding)
}
}
// 在main.js中注册
import permission from './directives/permission'
Vue.directive('permission', permission)
// 使用示例
管理员按钮