Clean Frontend Architecture:整洁前端架构 | clean-frontend
https://github.com/eduardomoroni/react-clean-architecture
https://github.com/im-a-giraffe/angular-clean-architecture
https://github.com/phodal/clean-frontend
https://github.com/android10/Android-CleanArchitecture
总结
权限控制怎么放置?路由首位中?
模块化,modules自动加载,如route/modules
通过配置化方式,路由能力的增强,meta.roles 可进行路由权限控制
vue-element-admin阅读
permission 有菜单路由级别,按钮级别,
https://github.com/PanJiaChen/vue-element-admin/tree/master/src
vue-vben-admin 项目阅读
router路由,路由配置和路由守护
enums 常量,
design 样式
api
logics
settings
hooks
store
utils
里面还各有各种分层,命名很清晰
**
**
看入口文件, main.ts
有点应用程序的感觉,
bootstarp()
,需要先启动加载很多部分,然后才能执行app.mount
加载渲染
// 核心的启动函数
async function bootstrap() {
const app = createApp(App);
// Configure store
setupStore(app);
// Initialize internal system configuration
initAppConfigStore();
// Register global components
registerGlobComp(app);
// Multilingual configuration
await setupI18n(app);
// Configure routing
setupRouter(app);
// router-guard
setupRouterGuard();
// Register global directive
setupGlobDirectives(app);
// Configure global error handling
setupErrorHandle(app);
// Mount when the route is ready
// https://next.router.vuejs.org/api/#isready
await router.isReady();
app.mount('#app', true);
}
void bootstrap();
入口文件 App.vue
<template>
<ConfigProvider :locale="getAntdLocale">
<AppProvider>
<RouterView />
</AppProvider>
</ConfigProvider>
</template>
AppProvider是做什么用?为何不直接使用一个Store来管理变量和逻辑?以及ConfigProvider的作用
核心路由 router 路由配置增强
- guard
- helper
- menus
- routes
- index.ts
路由守卫做了什么?
初始化逻辑,获取当前url状态在哪里?应该在store中,还是router中?
vscode中都是在一起,在bootstrap代码中,有restore逻辑
basicMenu中有 listenerRouteChange
中 setOpenKeys
,有设置菜单展开的逻辑,handleMenuChange中有 setOpenKeys
如果菜单变更, items
路由模块化,路由配置增强
写在modules中,会被自动加载
里面包含了很多逻辑,如权限控制的 meta.roles
路由配置还包含了各种配置项,
菜单文件 Menu 菜单初始化激活 菜单更变逻辑
layout的结构
也是层层结构,需要控制折叠展开,菜单根据打开的url初始化激活,
当前激活的菜单,如过url中有hash或path,需要初始化展开的菜单
初始化时,获取并激活当前应该展开的菜单
SimpleMenu.vue
和菜单变更的逻辑都集中化
const { setOpenKeys, getOpenKeys } = useOpenKeys(
1 展开相关的变化
watch(
() => props.collapse,
(collapse) => {
if (collapse) {
menuState.openNames = [];
} else {
// 激活当前应该展开的菜单
setOpenKeys(currentRoute.value.path);
}
},
{ immediate: true }
);
2 侧边栏items的变化
watch(
() => props.items,
() => {
...
setOpenKeys(currentRoute.value.path);
},
{ flush: 'post' }
)
// 路由相关的变化
listenerRouteChange((route) {
handleMenuChange(
if(...) {
setOpenKeys()
}
});
async function handleMenuChange(route?: RouteLocationNormalizedLoaded) {
async function handleSelect(key: string) {
网络请求 utils/http/axios
- index.ts 入口
**
初始化VAxios,传入业务
并通过transform定制了业务逻辑
transformRequestHooks
后hook,根据code统一的错误弹窗,登录超时的错误提示。
responseInterceptorsCatch
的调用checkStatus,根据网络错误code,展示友好的中文提示。
requestInterceptors
在header中添加token
- Axios.ts 封装了VAxios,
Axios做了什么?
对外暴露一些定制参数,如接口url,是否忽略重复请求等,背后添加的是辅助的能力
对外暴露了transform参数,抽象了请求过程的几个生命周期,供外部进行定制。
beforeRequestHook
请求前hook,transformRequestHook
,请求后hookrequestInterceptors
请求前拦截器,responseInterceptors
请求后拦截器requestInterceptorsCatch
请求前拦截错误处理,responseInterceptorsCatch
封装了upload上传文件的util
- checkStatus.ts
这个逻辑独立出来,在
Logics?
errorHandle
mitt/routeChange
theme
Hooks 很多逻辑都封装在这里
vue自身也使用了很多hooks的封装
import { useRouter } from 'vue-router';
setup(props, { attrs, emit }) {
// 这个currentRoute拿到的是响应式的变量
const { currentRoute } = useRouter();
}
event/useBreakPoint.ts
封装了窗口大小改变的自适应逻辑
一些有用的hooks
useModal
useTable
一些逻辑的封装hooks
顶上的折叠,展开侧边栏的功能。底层数据保存在 vuex
中的 appStore
中,但是不会通过 commit
等方式直接修改数据。而是通过useMenuSetting,提供了方便的get和set的相关API。
// HeaderTrigger
// SidebarTrigger
setup(){
const { getCollapsed, toggleCollapsed } = useMenuSetting();
return { getCollapsed, toggleCollapsed };
}
// useMenuSetting.ts
import { useAppStore } from '/@/store/modules/app';
const appStore = useAppStore();
const getCollapsed = computed(() => appStore.getMenuSetting.collapsed);
// Set menu configuration
function setMenuSetting(menuSetting: Partial<MenuSetting>): void {
appStore.setProjectConfig({ menuSetting });
}
function toggleCollapsed() {
setMenuSetting({
collapsed: !unref(getCollapsed),
});
}
权限 Permession逻辑
底层还是用vuex来存储,商城用usePermisson来提供hooks
// usePermission.ts
import { usePermissionStore } from '/@/store/modules/permission';
// 使用Permission的地方
import { usePermission } from '/@/hooks/web/usePermission';
const { changeRole, hasPermission, togglePermissionMode, refreshMenu } = usePermission();
store/modules/user.ts
login
有内层和外层的 try catch
的处理
logout
阅读的感受
入口清晰,整个应用的 main.ts
和 App.vue
,每个模块部分的 index.ts
逻辑清晰,由浅入深,抽象层次一致,如main.ts中的bootstrap()代码,如 router/guard/index.ts
中
封装,对外暴露参数,如 axios部分
,业务逻辑和定制部分,都在 index.ts
中可阅读到,其它部分都封装了依赖。
命名,分类清晰
**
// 配置,项目配置
import projectSetting from '/@/settings/projectSetting';
// app常亮
import { PermissionModeEnum } from '/@/enums/appEnum';
// 基础的路由
import { ERROR_LOG_ROUTE, PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic';
// sys系统相关的接口
import { getMenuList } from '/@/api/sys/menu';
import { getPermCode } from '/@/api/sys/user';
都模块化,方便维度router/routes/modules
router/menus/modules
store/modules
异常处理
见 LoginForm
和 userStore.login
的逻辑
有哪些缺点?
逻辑分散?
AppProvider 中有 createBreakpointListen 开启自行应逻辑
SimpleMenu 中有 菜单初始化激活,也许放一起更好?
业务逻辑都写在了store中,如userStore中的login包含了 登录请求,获取用户信息等复杂业务逻辑。是否应该有个service来做处理。
antd pro
登录逻辑,都在redux的effects中,
https://github.com/ant-design/ant-design-pro/blob/master/src/models/login.ts
modles/login
login()
logout()