当前位置:   article > 正文

vue切换路由页面数据缓存_基于数据驱动的前端系统(spa)页面路由设计

vue切换路由页面数据缓存

addbec07b6c60190ce565d3b8fe2d089.png
本文涉及的技术包括:react、react-router(4.0及其以上)、femo(数据管理库)。这些技术都能找到对应的替代。

前言

在大型前端系统里面,业务场景复杂,系统复杂度高,开发人员协同难度大。这样的系统里面需要做许多设计来降低系统的复杂度,提高开发效率,适应多变的使用场景其中路由设计是核心之一。

什么是大型前端系统?笔者认为(比较主观)直观地以代码量和团队规模来衡量:代码量超过5万,前端人员规模超过5人,二者达其一。

笔者经历过前端系统代码20万+,前端开发人员10+的项目,也经历过前端系统代码4万+,只有一个前端开发人员的项目。基于原则:降低系统的复杂度,提高开发效率,适应多变的使用场景,对路由设计做了一些初潜地总结。

数据驱动的路由

为什么是数据驱动?

  1. 避免不同开发人员对组件逻辑的修改,对数据的操作比去改组件逻辑来得快速和安全;
  2. 数据驱动符合现在主流开发框架(库)的理念。

定义路由数据结构

  1. interface Road {
  2. name: string; // 名字
  3. path: string; // 路径(同层唯一)
  4. component: React.ComponentType; // 对应的组件
  5. subRoads?: Road[]; // 下级路由
  6. permissions?: Set<string> | string[]; // 路由所需要的权限码。规定:如果是数组,权限码之间是且关系;如果是Set则是或关系。二者可互相嵌套,来表达复杂的逻辑关系。
  7. fallback?: (props: RouteComponentProps) => any; // 遇到无权限时,回退渲染的视图逻辑
  8. hasHeader?: boolean; // 是否有页面header
  9. hasSider?: boolean; // 是否有侧边菜单
  10. visible?: boolean; // 是否可见
  11. }

上面的数据结构经过简化,实际的项目中要复杂很多。

实际上路由数据不单单会给路由组件消费,路由周边的组件也会消费。比如页面的菜单栏、页面头部、页面文档的标题、面包屑导航、以及其他有导航功能的地方,一些页面区域的显示隐藏也和路由数据相关。这些页面的功能区域,可以根据同一份路由数据来实现。相反的,如果这些页面功能区需要的数据没有,那么要在路由数据结构里面新增

定义一个路由

  1. import { gluer } from 'femo';
  2. import { Road } from '../interface';
  3. import Homepage from './Homepage';
  4. import Cart from './Cart';
  5. const initRoad: Road = {
  6. name: '商城系统',
  7. path: '/shop',
  8. component: () => <Redirect to='/shop/homepage'/>,
  9. permissions: [], // 表示不需要权限
  10. hasHeader: true, // 页面头部展示 默认展示
  11. hasSider: true, // 页面菜单展示 默认展示
  12. visible: true, // 菜单项展示 默认展示
  13. subRoads: [{
  14. name: '首页',
  15. path: '/homepage',
  16. component: Homepage,
  17. }, {
  18. name: '购物车',
  19. path: '/cart',
  20. component: Cart,
  21. }]
  22. }
  23. const shopRoad = gluer(initRoad);
  24. export default shopRoad;

将路由集中

  1. import { gluer } from 'femo';
  2. import { Road } from './interface';
  3. import shopRoad from './Shop/road';
  4. const roadMap = gluer({
  5. shop: shopRoad(),
  6. })
  7. // 设置依赖
  8. // shopRoad的变化会同步到roadMap中
  9. roadMap.relyOn([shopRoad], (roads, state) => {
  10. return {
  11. ...state,
  12. shop: roads[0] as Road,
  13. }
  14. });
  15. export default roadMap;

文件目录结构如下

12f830520ce2c9f093c1429fad92cd53.png

效果如下

知乎视频​www.zhihu.com

下面举几个业务场景中使用的例子

场景一

产品小王提出:用户进入“首页”时,页面的头部隐藏掉。此时前端开发小李快速地敲了几下键盘就搞定了,代码如下:

  1. import { gluer } from 'femo';
  2. import { Road } from '../interface';
  3. import Homepage from './Homepage';
  4. import Cart from './Cart';
  5. const initRoad: Road = {
  6. name: '商城系统',
  7. path: '/shop',
  8. component: () => <Redirect to='/shop/homepage'/>,
  9. permissions: [], // 表示不需要权限
  10. hasHeader: true, // 页面头部展示 默认展示
  11. hasSider: true, // 页面菜单展示 默认展示
  12. visible: true, // 菜单项展示 默认展示
  13. subRoads: [{
  14. name: '首页',
  15. path: '/homepage',
  16. component: Homepage,
  17. hasHeader: false, // 隐藏页面头部(改了这里)
  18. }, {
  19. name: '购物车',
  20. path: '/cart',
  21. component: Cart,
  22. }]
  23. }
  24. const shopRoad = gluer(initRoad);
  25. export default shopRoad;

效果如下:

知乎视频​www.zhihu.com

场景二

产品小王隔了几天,突发奇想:希望用户进入购物车的时候,在左侧导航栏增加一个菜单项“历史记录”,但这个“历史记录”菜单项在“首页”的时候又没有。前端小李表示很奇怪,这是什么需求!但最终还是做了,过了大概几分钟,实现了如下效果。产品小王满意地点了点头。

效果如下:

知乎视频​www.zhihu.com

核心改动如下:

在 购物车 挂载时,新增一个history的路由配置

  1. useEffect(() => {
  2. // 挂载的时候去新增一个history路由配置
  3. shopRoad((_data, state) => {
  4. const subPaths: Road[] = [...(getSafe(state, 'subPaths') || [])];
  5. const target = subPaths.find((item) => getSafe(item, 'path') === '/history');
  6. if (target) return state;
  7. const history: Road = {
  8. name: '历史记录',
  9. path: '/history',
  10. component: History,
  11. };
  12. subPaths.push(history);
  13. const tmpState = { ...state };
  14. tmpState.subPaths = subPaths;
  15. return tmpState;
  16. });
  17. }, []);

在 首页 挂载时,从路由配置中删除history

  1. useEffect(() => {
  2. // 挂载的时候去删除history路由配置
  3. shopRoad((_data, state) => {
  4. const subPaths: Road[] = [...(getSafe(state, 'subPaths') || [])];
  5. subPaths.find((item, i) => {
  6. const flag = getSafe(item, 'path') === '/history';
  7. if (flag) {
  8. subPaths.splice(i, 1);
  9. }
  10. return flag;
  11. });
  12. const tmpState = { ...state };
  13. tmpState.subPaths = subPaths;
  14. return tmpState;
  15. });
  16. }, []);

场景三

又没过几天,产品小王又来找前端小李,没错又是一个新需求。产品小王希望在一个商品详情的页面,加一个“返回”按钮。这个返回按钮是一个下拉菜单,里面的菜单项与商城系统的菜单项一致。前端小李想了想,共用一下路由数据,生成一个下拉菜单就行了,小意思。

一会就有了下面的效果

知乎视频​www.zhihu.com

核心代码如下:

  1. const [road] = useModel<Road>(shopRoad);
  2. const pathPrefix = getSafe(road, 'path');
  3. const subPaths: Road[] = getSafe(road, 'subPaths') || [];
  4. const overlay = (
  5. <Menu>
  6. {
  7. subPaths.map((s) => {
  8. const path = getSafe(s, 'path');
  9. const name = getSafe(s, 'name');
  10. const visible = getSafe(s, 'visible');
  11. if (visible === false) return null;
  12. return (
  13. <Menu.Item key={path}>
  14. <Link to={`${pathPrefix}${path}`}> {name} </Link>
  15. </Menu.Item>
  16. );
  17. })
  18. }
  19. </Menu>
  20. );

在今后的日子里,类似上面的场景还会不断在产品小王和前端小李之间发生。

从上面的几个例子中可以看到一些数据驱动好处:操作简单、适用性广。数据本身也具有比组件更好的弹性:增加属性或者改变结构来适配业务。

上面的例子并没有详细说明,侧边菜单栏、页面顶部、路由组件是如何消费数据的,因为篇幅限制加上这部分实现的方式比较灵活。

除了上面的例子,还有许多对页面访问相关的需求,比如不同入口进来看见不同菜单和页面、对灰度的要求、对用户不同身份看见的页面不同等。这些需求一般在系统入口处做,属于比较上层。原理都一样,就是操作路由数据

一些思考

结合业务场景和业务开发,可以总结出一些数据模型,这些数据模型具有某些业务通用性。而透过总结的这些数据模型,可以体会到数据是核心,一切展示和交互都是数据变化和流动。进而能引发思考:如何管理好数据。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/68350
推荐阅读
相关标签
  

闽ICP备14008679号