React 系列 - 写出优雅的路由
前言
自前端框架风靡以来,路由一词在前端的热度与日俱增,他是几乎所有前端框架的核心功能点。不同于后端,前端的路由往往需要表达更多的业务功能,例如与菜单耦合、与标题耦合、与“面包屑”耦合等等,因此很少有拆箱即用的完整方案,多多少少得二次加工一下。
1. UmiJS 简述
优秀的框架可以缩短 90% 以上的无效开发时间,蚂蚁的 UmiJS 是我见过最优雅的 React 应用框架,或者可以直接说是最优雅的前端解决方案(欢迎挑战),本系列将逐步展开在其之上的应用,本文重点为“路由”,其余部分后续系列继续深入。
2. 需求概述
动码之前先构想下本次我们要实现哪些功能:
- 路由需要耦合菜单,且需要对菜单的空节点自动往下补齐;
- 路由中总要体现模板的概念,即不同的路由允许使用不用的模板组件;
- 模板与页面的关系完全交由路由组合,不再体现于组件中;
- 需要实现从路由中获取当前页面的轨迹,即“面包屑”的功能;
- 实现从路由中获取页面标题;
上述每一点的功能都不复杂,若不追求极致,其实默认的约定式路由基本能够满足需求(详情查询官方文档,此处不做展开)。
3. 开码
3.1 菜单
先从菜单出发,以下应当是一个最简洁的目录结构:
const menu = [
{
name: '父节点',
path: 'parent',
children: [{
name: '子页面',
path: 'child'
}]
}
];
使用递归补齐 child 路径:
const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
const formatMenu = (data, parentPath = `${define.BASE_PATH}/`) => {
return data.map((item) => {
let { path } = item;
if (!reg.test(path)) {
path = parentPath + item.path;
}
const result = {
...item,
path
};
if (item.children) {
result.children = formatMenu(item.children, `${parentPath}${item.path}/`);
}
return result;
});
}
菜单的子节点才是真正的页面,所以若当前路径是父节点,我们期望的是能够自动跳转到父节点写的第一个或者特定的页面:
const redirectData = [];
const formatRedirect = item => {
if (item && item.children) {
if (item.children[0] && item.children[0].path) {
redirectData.push({
path: `${item.path}`,
redirect: `${item.children[0].path}`
});
item.children.forEach(children => {
formatRedirect(children);
});
}
}
};
const getRedirectData = (menuData) => {
menuData.forEach(formatRedirect);
return redirectData
};
3.2 路由组装
而后便是将自动跳转的路径组装入路由节点:
const routes = [
...redirect,
{
path: define.BASE_PATH,
component: '../layouts/BasicLayout',
routes: [
{
path: `${define.BASE_PATH}/parent`,
routes: [
{
title: '子页面',
path: 'child',
component: './parent/child',
}
],
},
{
component: './404',
}
]
}
];
路由配置最后需要注入配置文件 .umirc.js:
import { plugins } from './config/plugins';
import { routes } from './config/routes';
export default {
plugins,
routes
}
3.3 模板页
import { Layout } from 'antd';
import React, { PureComponent, Fragment } from 'react';
import { ContainerQuery } from 'react-container-query';
import DocumentTitle from 'react-document-title';
import { query } from '@/utils/layout';
import Footer from './Footer';
import Context from './MenuContext';
const { Content } = Layout;
class BasicLayout extends PureComponent {
render() {
const {
children,
location: { pathname }
} = this.props;
const layout = (
<Layout>
<Layout>
<Content>
{children}
</Content>
<Footer />
</Layout>
</Layout>
);
return (
<Fragment>
<DocumentTitle title={this.getPageTitle(pathname)}>
<ContainerQuery query={query}>
{params => (
<Context.Provider>
{layout}
</Context.Provider>
)}
</ContainerQuery>
</DocumentTitle>
</Fragment>
);
}
}
export default BasicLayout;
结合路由与菜单获取面包屑:
getBreadcrumbNameMap() {
const routerMap = {};
let path = this.props.location.pathname;
if (path.endsWith('/')) {
path = path.slice(0, path.length - 1);
}
const mergeRoute = (path) => {
if (path.lastIndexOf('/') > 0) {
const title = this.getPageTitle(path);
if (title) {
routerMap[path] = {
name: title,
path: path
};
}
mergeRoute(path.slice(0, path.lastIndexOf('/')));
}
};
const mergeMenu = data => {
data.forEach(menuItem => {
if (menuItem.children) {
mergeMenu(menuItem.children);
}
routerMap[menuItem.path] = {
isMenu: true,
...menuItem
};
});
};
mergeRoute(path);
mergeMenu(this.state.menuData);
return routerMap;
}
从路由中获取 PageTitle:
getPageTitle = (path) => {
if (path.endsWith('/')) {
path = path.slice(0, path.length - 1);
}
let title;
this.props.route.routes[0].routes.forEach(route => {
if (route.path === path) {
title = route.title;
return;
}
})
return title;
};
结语
此篇随笔比较混乱,写作脉络不对,还是应该简述下在 umijs 之上的架构设计,再往下深入探讨应用点,缺的部分会在后续系列中补上~ 请关注公众号:
React 系列 - 写出优雅的路由的更多相关文章
- 如何写出优雅的CSS代码 ?(转)
对于同样的项目或者是一个网页,尽管最终每个前端开发工程师都可以实现相同的效果,但是他们所写的代码一定是不同的.有的优雅,看起来清晰易懂,代码具有可拓展性,这样的代码有利于团队合作和后期的维护:而有的混 ...
- 如何写出优雅的css代码 ?
如何写出优雅的css代码 ? 对于同样的项目或者是一个网页,尽管最终每个前端开发工程师都可以实现相同的效果,但是他们所写的代码一定是不同的.有的优雅,看起来清晰易懂,代码具有可拓展性,这样的代码有利于 ...
- 如何写出优雅的JavaScript代码 ? && 注释
如何写出优雅的JavaScript代码 ? 之前总结过一篇<如何写出优雅的css代码?>, 但是前一段时间发现自己的js代码写的真的很任性,没有任何的优雅可言,于是这里总结以下写js时应当 ...
- 【原创】怎样才能写出优雅的 Java 代码?这篇文章告诉你答案!
本文已经收录自 JavaGuide (59k+ Star):[Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识. 本文比较简短,基本就是推荐一些对于写好代码非常有用的文章或者 ...
- 如何写出优雅的 Golang 代码
原文: https://draveness.me/golang-101.html Go 语言是一门简单.易学的编程语言,对于有编程背景的工程师来说,学习 Go 语言并写出能够运行的代码并不是一件困难的 ...
- 深入了解Promise对象,写出优雅的回调代码,告别回调地狱
深入浅出了解Promise 引言 正文 一.Promise简介 二.Promise的三种状态 三.函数then( ) 四.函数catch( ) 五.函数finally( ) 六.函数all( ) 七. ...
- 如何写出优雅兼备可读性的javascript代码
即或是最简单的需求,不同的程序员也会写出不一样的代码: 需求:充值程序过虑不符合条件的充值金额,即只能充入100.200.500.1000金额,其它过虑: 1.菜鸟程序员可能会这样写,虽然可读性强,代 ...
- 写出优雅又地道的pythonic代码(转自网络)
本文是Raymond Hettinger在2013年美国PyCon演讲的笔记(视频, 幻灯片). 示例代码和引用的语录都来自Raymond的演讲.这是我按我的理解整理出来的,希望你们理解起来跟我一样顺 ...
- 如何写出优雅的Python代码?
有时候你会看到很Cool的Python代码,你惊讶于它的简洁,它的优雅,你不由自主地赞叹:竟然还能这样写.其实,这些优雅的代码都要归功于Python的特性,只要你能掌握这些Pythonic的技巧,你一 ...
随机推荐
- 跨域调用接口的方法之一:$.ajaxSetup()
跨域查询接口的数据,之前在公司时有发生过,产生的原因是,本地请求的域名或IP地址不一致,解除方法,也是修改域名和IP地址.比如: 接口中的数据来自IP地址:192.168.1.23/get.php 如 ...
- Electron开发笔记—electron-builder打包流程
该文章说明基于win平台下,mac及linux没有实验 关于electron-builder打包可以有两种方案: 1. 打包成文件夹及绿色免安装: electron-builder --dir(依赖w ...
- Jenkins 安装 on centos7
本文演示如何在CentOS7上安装jenkins. 1 准备工作 1.1 选择安装节点 因为在DevOps实践环境搭建规划中,Jenkins的任务需要执行docker swarm的相关命令,简单起见, ...
- MSSQL sql server order by 1,2 的具体含义
转自:http://www.maomao365.com/?p=5416 摘要: order by 1,2 的含义是对表的第一列 按照从小到大的顺序进行排列 然后再对第二列按照从小到大的顺序进行排列 ...
- Spring基于注解注入的两种方式
1.@Autowried 1)默认基于类型查找容器的的Bean进行注入(注入的Bean的实现类是唯一的). 2)当实现类的Bean大于一个的时候,需结合@Qualifier,根据Bean的名称来指定需 ...
- php判断手机是安卓系统还是ios系统
最近项目,要判断用户的手机是安卓的还是ios的,搜了一下相关的资料,最终获得的结果.事实证明,是有效的!主要是要用到HTTP_USER_AGENT,它表示的意思是用来检查浏览页面的访问者在用什么操作系 ...
- 让vue-cli脚手架搭建的项目可以处理vue文件中postcss语法
图中&属于postcss的语法,这样书写样式可以清楚的看出选择器之前的层级关系,非常好用. 在利用vue-cli脚手架搭建的项目中如果不配置是不支持这种写法的,这样写不会报错,但是样式不生效. ...
- ES5-ES6-ES7_iterator和for of
iterator(遍历器)的概念 JavaScript原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6又添加了Map和Set.这样就有了四种数据集合,用户还可以组合 ...
- Django-rest-framework 接口实现 版本控制 versioning
版本控制 rest_framework 提供了 5 种版本控制 以及对应的 写法 url的 更改都可以 在 from rest_framework import versioning 中查看 Acce ...
- SQL 简介
SQL 是用于访问和处理数据库的标准的计算机语言. 什么是 SQL? SQL 指结构化查询语言 SQL 使我们有能力访问数据库 SQL 是一种 ANSI 的标准计算机语言 编者注:ANSI,美国国家标 ...