【Fantastic-admin 技术揭秘】妙用Vue动态组件,让客制化更容易
《Fantastic-admin 技术揭秘》系列将带你了解 Fantastic-admin 这款框架各种功能的设计与实现。通过了解这些技术细节,你不光可以更轻松地使用 Fantastic-admin 这款框架,也可以在其他项目中使用这些技术。
你可以点击 这里 查看本系列的所有文章,也欢迎你在评论区留言告诉我你感兴趣的内容,或许下一篇文章就会带你揭秘其中的奥秘。
需求分析
作为一款面向开发者的后台系统框架,一直在尽可能满足各种业务场景的需求,但也确实没办法做到将各种业务需求都集成进去。既然很多功能框架无法直接满足,那就得思考如何降低客制化的成本,让开发者可以更轻松的进行扩展。
比如有这样一个需求,需要在左侧导航区域 Logo 下方增加一个组织切换的按钮,大部分开源的后台系统的解决方案就是找到导航对应的文件 → 阅读源码 → 编写业务代码 → 测试功能,这就涉及到了几个难点:
- 需要阅读并修改系统源码。对开发者能力有要求,不求能看懂源码,但至少不能改出bug
- 系统功能和业务功能的代码耦合。维护成本增加,后续维护的人也得重复第1点,还得区分出哪些是业务代码
- 系统稳定性。任何直接对系统源码的修改,都无法100%保证绝对没问题,除非你完全熟悉系统源码
实现方案
使用Vue的动态组件,加上Vue插槽的设计理念,就可以很巧妙的解决这个问题。
只需在对应位置提供预留的“插槽”(这里的“插槽”实际上是Vue动态组件),然后按照约定的文件名去创建组件,然后在组件内就可以写业务代码,这个组件就会出现在对应的“插槽”位置上。
核心代码如下:
// src/slots/index.ts
import { pascalCase } from 'scule'
type Slots =
'header-start' | 'header-after-logo' | 'header-after-menu' | 'header-end' |
'main-sidebar-top' | 'main-sidebar-after-logo' | 'main-sidebar-after-menu' | 'main-sidebar-bottom' |
'sub-sidebar-top' | 'sub-sidebar-after-logo' | 'sub-sidebar-after-menu' | 'sub-sidebar-bottom' |
'tabbar-start' | 'tabbar-end' |
'toolbar-start' | 'toolbar-end' |
'free-position'
function tryLoadComponent(name: Slots) {
const componentMap = import.meta.glob('./*/index.vue', { eager: true })
const path = `./${pascalCase(name as unknown as string)}/index.vue`
const component = componentMap[path as keyof typeof componentMap]
if (!component) {
return {
default: defineComponent({
name: 'SlotsInvalidComponent',
render: () => null,
}),
}
}
return component
}
export function useSlots(name: Slots) {
const component = tryLoadComponent(name)
return defineComponent((component as any).default)
}
然后在需要的地方使用:
<component :is="useSlots('main-sidebar-after-logo')" />
这样一个基于插槽理念的动态组件就完成了,当需要使用这个插槽的时候,只需要创建 src/slots/MainSidebarAfterLogo/index.vue
文件即可。
在 Fantastic-admin 中,提供了许多这样的插槽,比如:
实现原理
阅读上面的核心代码,就可以发现其实现原理并不复杂,主要就是通过 import.meta.glob
动态导入 Vue 组件,然后通过 useSlots
函数返回组件。
同时需要注意下,当组件文件不存在时,会返回一个空的组件,这样不会影响到布局。