如何优雅的使用react router v6, 并实现全局守卫
1.项目初始化
首先用一句话简单总结下 react 是什么?
React 是一个用于构建用户界面的 JavaScript 库
对于项目初始化这话,react 也有自己的脚手架Create React App,react 官网也是建议我们初学者使用 CRA 去初始化一个 react 项目,奈何这个这个脚手架太鸡肋,好多东西都需要开发者手动去配置,另外是基于 webpack 的,所以项目大的时候配置起来是及其麻烦,那怎么办呢?
还好有尤大的 vite,vite 也支持快速生成 react 项目,并内置一些基础配置,使用起来是真香
套用几句vite官网的介绍:
-
一个开发服务器,它基于 原生 ES 模块 提供了 丰富的内建功能,如速度快到惊人的 模块热更新(HMR)。
-
一套构建指令,它使用 Rollup 打包你的代码,并且它是预配置的,可输出用于生产环境的高度优化过的静态资源。
那接下来我们使用 vite 创建一个 react + ts 的项目,我们使用pnpm创建项目,还不会使用 pnpm 的伙伴可以看下如何使用 pnpm 搭建一个 monorepo 工程
pnpm create vite
我们取名为 react-ts,然后选择 react 技术栈,以及 TypeScript 语言
ok,项目初始化完毕后,我们运行如下,标识已经初始化成功!
2.react-router v6 简单介绍
react router 发布了三个不同的包:
- react-router:路由核心库,提供许多组件、钩子;
- react-router-dom: 包括了 react-router 所有内容,同时添加了用于 DOM 的组件,如
<BrowserRouter>
; - react-router-native: 包括了 react-router 所有内容,同时添加了用于 ReactNative 的 API,如
<NativeRouter>
。
与 react-router 5.X 区别:
- 内置组件的变化:移除
<Switch/>
,新增<Routes/>
…… - 语法变化:
component={About}
变成element={<About/>}
…… - 新增 hook:
useParams
、useNavigate
、useMatch
…… - 官方明确表示推荐使用函数式组件
3.使用 react route v6 创建路由表
首先我们先安装 react router v6 以及依赖
pnpm install react-router router-router-dom
安装完路由后,我们也想使用 vue 那样,单独创建一个路由表,用来管理路由。
#依次执行下面代码
cd src
mkdir routes
touch index.tsx
我们首先创建两个页面,一个 home 页面,一个 about 页面,然后再 routes/index.tsx 中引入,那是否可以像 vue 那样实现路由懒加载呢,答案是当然可以,react 也为我们光大开发者提供了一个懒加载的 api,就是 React.lazy(),通过这个方法,我们就可以实现路由懒加载
const Home = React.lazy(() => import("../pages/Home"));
const About = React.lazy(() => import("../pages/About"));
interface Route {
path: string;
name: string;
element: ReactNode;
}
export const routes: Route[] = [
{
path: "/home",
name: "home",
element: <Home />,
},
{
path: "/about",
name: "about",
element: <About />,
},
];
ok 路由表建立后我们如何在页面中优雅的展示呢?
这里不得不提useRoutes这个 hooks, 他能够将我们创建的路由表一一映射成为路由对象
我们也想 vue 开发中一样,在 App.tsx 中引入刚才我们创建好的路由表,
function App() {
const elements = useRoutes(generateRouter(routes));
return <div>{elements}</div>;
}
这样一个简单的路由表就创建好了,但是开发中,我们肯定不会是这么简单,比如,切换路由时我们可以增加 loading 转场等,react 也为我们提供了React.Suspense组件,通过 callback props 传入一个 loading,来实现转场效果,我们用该组件将上面的 elements 封装下
<React.Suspense fallback={<Loading />}>{elements}</React.Suspense>
封装完后,在切换路由时,会发现有一个 loading 转场的一个效果。
4.增加全局守卫
如果是一个大型项目,那简单的路由表肯定是不能满足我们的需求的,比如常见的权限校验,前置逻辑等等就无法处理。那 react-router 没有像 vue 一样的类似全局守卫的函数吗?
那还真没有,不得不说 react 是真的优秀,什么都需要自己手动去搞定,搞就搞,谁怕谁,哈哈哈哈
无非我们就是实现一个高阶组件包裹下,然后再高阶组件里面我们去做下路由拦截处理和前置逻辑处理
我们首先对刚才的路由表进行扩展,我们先增加一个权限校验和子组件吧
interface Route {
path: string,
name: string,
element: ReactNode,
children?: Route[],
auth?: boolean
}
export const routes: Route[] = [
{
path: '/home',
name: 'home',
element: <Home />
auth: true
},
{
path: '/about',
name: 'about',
element: <About />
},
]
紧接着需要对上面的路由表实现一个类似中间件的映射,首先实现一个RouterBeforeEach
高阶组件,参数为外部传入的 routes,在该组件内部,将上面我们写的 routes 和传入的做一个对比和判断,如果有 auth 并且为 true,则需要做权限校验,如果没有,则不需要。
export const RouterBeforeEach = ({ children }: any) => {
const location = useLocation();
const navigator = useNavigate();
useEffect(() => {
let router = getCurrentRouterMap(routes, location.pathname);
if (!isLogin && router.auth) {
navigator("/login");
}
}, [location.pathname]);
return children;
};
另外上面的两个 hooks:
-
useLocation: 这个钩子返回当前路由对象。如果您想在当前路由更改时执行一些副作用,我们就可以使用这个 hooks。
-
useNavigate: useNavigate 钩子返回一个函数,这个 hooks 能够让我可以编程式的导航。
上面的 getCurrentRouterMap 就是在路由表中获取到当前的路由对应的路由
const getCurrentRouterMap = (routers: Router[], path: string): Route => {
for (let router of routers) {
if (router.path == path) return router;
if (router.child) {
const childRouter = getCurrentRouterMap(router.child, path);
if (childRouter) return childRouter;
}
}
return routes[routes.length - 1];
};
然后,我们在去 App.tsx 中用 RouterBeforeEach 包裹下 elements
<React.Suspense fallback={<Loading/>}>
<RouterBeforeEach>
{elements}
</RouterBeforeEach>
</React.Suspense>
</div>
ok,到这里用 react-router 6 实现一个路由表,并实现一个全局守卫已经完成。
5.总结
总的来说,react 对初学者确实比 vue 要要求高些,很多东西需要自己手动去封装和处理,不像 vue,在框架层面已经帮我们做了太多的处理,对开发者还是非常友好的;但是不能说 react 就不友好,只能说各有各的好处,相信坚持下去他会给我们能力上带来很大的提升。