赞
踩
不同于vue,通过在路由里设置meta元字符实现路由拦截。在使用 Vue ,框架提供了路由守卫功能,用来在进入某个路有前进行一些校验工作,如果校验失败,就跳转到 404 或者登陆页面,比如 Vue 中的 beforeEnter
函数:
- ...
- router.beforeEach(async(to, from, next) => {
- const toPath = to.path;
- const fromPath = from.path;
- })
- ...
react实现路由拦截的基本思路还是利用Route 的render函数。通过判断拦截条件来实现不同的组件的跳转,从而实现拦截。在之前的版本中,React Router 也提供了类似的 onEnter
钩子,但在 React Router 4.0 版本中,取消了这个方法。React Router 4.0 以后采用了声明式的组件,路由即组件,要实现路由守卫功能,就得我们自己去写了。
如果不使用路由守卫,Router 组件是这样子的:
- import * as React from 'react';
- import { HashRouter,Switch,Route,Redirect } from 'react-router-dom';
-
- import Index from "./page/index";
- import Home from "./page/home";
- import ErrorPage from "./page/error";
- import Login from "./page/login";
-
- export const Router = () => (
- <HashRouter>
- <Switch>
- <Route path="/" exact component={Index}/>
- <Route path="/login" exact component={Login}/>
- <Route path="/home" exact component={Home}/>
- <Route path="/404" exact component={ErrorPage}/>
- <Redirect to="/404" />
- </Switch>
- </HashRouter>
- );
上面的 Router 组件,包含了三个页面:
以及四个路由:
其中,根路由和 /home
路由,都定向到了主页路由。
以上是一个基本的路由定义,可以在登陆/主页和 404 页面之间来回跳转,但也有一些问题:
/login
路由跳转到登录页现在,我们想完成这样的功能:
要完成这个功能,有两种方案:
props
上的 history
对象来进行跳转首先在跟目录src下创建一个routerMap.js文件,代码如下:
- import Index from "./page/index";
- import Home from "./page/home";
- import ErrorPage from "./page/error";
- import Login from "./page/login";
- export default [
- { path: "/", name: "App", component: Index, auth: true },
- { path: "/home", name: "Home", component: Home, auth: true },
- { path: "/login", name: "Login", component: Login },
- { path: "/404", name: "404", component: ErrorPage },
- ];
将 auth
设置为 true
,表示该路由需要权限校验。
然后,定义 Router
组件,在App.js中,该组件是经过高阶组件包装后的结果:
- //App.js
- import React, { Component } from "react";
- import { BrowserRouter as Router, Switch } from "react-router-dom";
- import FrontendAuth from "./FrontendAuth";
- import routerMap from "./routerMap";
- class App extends Component {
- // eslint-disable-next-line no-useless-constructor
- constructor(props) {
- super(props);
- }
- render() {
- return (
- <Router>
- <div>
- <Switch>
- <FrontendAuth routerConfig={routerMap} />
- </Switch>
- </div>
- </Router>
- );
- }
- }
-
- export default App;
所有的路由跳转,都交给 FrontendAuth
高阶组件代理完成。下面是 FrontendAuth
组件的实现:
- import React, { Component } from "react";
- import { Route, Redirect } from "react-router-dom";
- class FrontendAuth extends Component {
- // eslint-disable-next-line no-useless-constructor
- constructor(props) {
- super(props);
- }
- render() {
- const { routerConfig, location } = this.props;
- const { pathname } = location;
- const isLogin = sessionStorage.getItem("username");
- console.log(pathname, isLogin);
- console.log(location);
- // 如果该路由不用进行权限校验,登录状态下登陆页除外
- // 因为登陆后,无法跳转到登陆页
- // 这部分代码,是为了在非登陆状态下,访问不需要权限校验的路由
- const targetRouterConfig = routerConfig.find(
- (item) => item.path === pathname
- );
- console.log(targetRouterConfig);
- if (targetRouterConfig && !targetRouterConfig.auth && !isLogin) {
- const { component } = targetRouterConfig;
- return <Route exact path={pathname} component={component} />;
- }
- if (isLogin) {
- // 如果是登陆状态,想要跳转到登陆,重定向到主页
- if (pathname === "/login") {
- return <Redirect to="/" />;
- } else {
- // 如果路由合法,就跳转到相应的路由
- if (targetRouterConfig) {
- return (
- <Route path={pathname} component={targetRouterConfig.component} />
- );
- } else {
- // 如果路由不合法,重定向到 404 页面
- return <Redirect to="/404" />;
- }
- }
- } else {
- // 非登陆状态下,当路由合法时且需要权限校验时,跳转到登陆页面,要求登陆
- if (targetRouterConfig && targetRouterConfig.auth) {
- return <Redirect to="/login" />;
- } else {
- // 非登陆状态下,路由不合法时,重定向至 404
- return <Redirect to="/404" />;
- }
- }
- }
- }
- export default FrontendAuth;
创建登录的界面src/page/login.js,代码如下:
- import React, { Component } from "react";
- import { Redirect } from "react-router-dom"; //重定向使用
- class Login extends Component {
- constructor(props) {
- super(props);
- this.state = {
- username: "",
- password: "",
- rediectToReferrer: false, // 是否重定向之前的界面
- };
- this.handleChange = this.handleChange.bind(this);
- this.handleSumit = this.handleSumit.bind(this);
- }
- // 处理用户名、密码的变化
- handleChange(e) {
- if (e.target.name === "username") {
- this.setState({
- username: e.target.value,
- });
- } else if (e.target.name === "password") {
- this.setState({
- password: e.target.value,
- });
- }
- }
- // 提交登录表单
- async handleSumit(e) {
- e.preventDefault();
- const username = this.state.username;
- const password = this.state.password;
- if (username.length === 0 || password.length === 0) {
- alert("用户名或密码不能为空!");
- return;
- }
- // 保存信息到sessionStorage
- sessionStorage.setItem("username", username);
- // 登录成功后,设置redirectToReferrer为true;
- this.setState({
- rediectToReferrer: true,
- });
- let RedirectUrl = this.props.location.state
- ? this.props.location.state.from.pathname
- : "/";
- // 登陆成功之后的跳转
- this.props.history.push(RedirectUrl);
- }
- render() {
- return (
- <form className="login" onSubmit={this.handleSumit}>
- <div>
- <label htmlFor="">
- 用户名:
- <input
- type="text"
- name="username"
- value={this.state.username}
- onChange={this.handleChange}
- />
- </label>
- </div>
- <div>
- <label htmlFor="">
- 密码:
- <input
- type="password"
- name="password"
- value={this.state.password}
- onChange={this.handleChange}
- />
- </label>
- </div>
- <input type="submit" value="登录" />
- </form>
- );
- }
- }
- export default Login;
全部代码目录如下:
页面上的路由跳转,都由 FrontendAuth
高阶组件代理了,在 Switch
组件内部,不再是 Route
组件,而只有一个 FrontendAuth
组件。FrontendAuth
组件接收一个名为 config
的 Props
,这是一份路由表。同时,由于 FrontendAuth
组件放在了 Switch
组件内部,React Router 还自动为 FrontendAuth
注入了 location
属性,当地址栏的路由发生变化时,就会触发 location
属性对象上的 pathname
属性发生变化,从而触发 FrontendAuth
的更新(调用 render
函数)。FrontendAuth
的 render
函数中,根据 pathname
查找到路由表中的相关配置,如果该配置中指定了无需校验,就直接返回相应的 Route
组件。
如果查找到的配置需要进行校验,再根据是否登陆进行处理,具体可以查看代码中的注释。
总结一下,实现路由守卫需要考虑到以下的问题:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。