React路由匹配的问题

  • 最近在写 React 项目时,被 React 路由匹配的问题折磨的够呛,这里来分享一下其中的一些坑,以及我的路由配置组件
    在这里插入图片描述

前提

安装路由

npm i react-router-dom -S
  • 用 react-router-dom 不用 react-router 的原因:
    • react-router-dom 多出<Link> <BrowserRouter>等组件。
    • 更适合,更好用,更简单(没有这些组件就需要你自己去封装)。

配置 history 模式

  • 如果使用 hash 模式可以忽略此步骤。
  • 页面使用:
import { BrowserRouter } from "react-router-dom";
// 路由组件
export default function RouterView() {
	return (<BrowserRouter></BrowserRouter>);
}
{
	output: {
		// history会返回真实的路径
		// 当你使用按需加载的时候,默认为相对路径则会出现404
		publicPath: "/" 
	}
	devServer:{
		historyApiFallback: true // 解决路由 history 404 问题
	}
}
  • 上线服务器 Nginx 部署 history 模式 404,配置详情
server {
  location / {
    try_files $uri /index.html
  }
}

React 路由匹配规则

  • 这里提一下 Vue 路由匹配规则:
    • 调用了匹配器的 match 方法,通过 path-to-regexp , 来创建一个正则表达式 , 然后 , 通过这个正则来检查是否匹配。
    • 如果已经匹配则不会往下再去匹配。
  • React 匹配规则:
    • 从上到下执行,会继续匹配所以满足匹配的项。

/路由地址会匹配所有

  • 如果我现在的路由是/admin,则会显示下面两个组件。
    • 因为/admin会匹配//admin的前面有/
<Route path="/" component={} />
<Route path="/admin" component={} />
  • 解决:让他精确匹配,exact属性表示要完全相等。
    • /admin/是不全等的,所以不会同时出现两个。
<Route path="/" exact component={} />
<Route path="/admin" component={} />

路由匹配多个

  • 路由从上往下匹配,先匹配了/admin,他还会继续匹配/
  • 上面可以添加exact精确匹配可以达到目的。
<Route path="/admin" component={} />
<Route path="/" component={} />
  • 解决:
    • 可以使用上面的exact属性精确匹配。
    • 这里用另外一种方法。<Switch>组件匹配完一个之后不再继续往下匹配。
<Switch>
	<Route path="/admin" component={} />
	<Route path="/" component={} />
</Switch>

完整路由组件

  • 注意:因为我的第一个子路由和父路由不一样,所以不需要用到exact精确匹配。
  • 如果你的第一个子路由和父路由一样,则需要将第一个子路由上加上exact
  • /admin路由组件放前面的原因是因为如果他匹配了/则不会往下匹配,因为<Switch>组件,所以/路由要放在Routes数组的最后一个对象即可。
import React from "react";
import { BrowserRouter, Switch, Route, Redirect } from "react-router-dom";
import { Require } from "@/utils/global/function";

// 路由配置
const Routes = [
  {
    path: "/admin", // 账号管理模块
    children: [
      {
        path: "/admin/login" // 登录页面
      },
      {
        path: "/admin/forget" // 忘记密码页面
      }
    ]
  },
  {
    path: "/", // 首页模块
    children: [
      {
        path: "/home" // 首页-单页
      },
      {
        path: "/public", // 公共模块
        children: [
          {
            path: "/public/noticeList" // 公告列表
          },
          {
            path: "/public/helpCenter" // 帮助中心
          },
          {
            path: "/public/versionUpdate" // 版本更新
          }
        ]
      },
      {
        path: "/function", // 功能模块
        children: [
          {
            path: "/function/imgList" // 图片列表
          }
        ]
      },
      {
        path: "/imgText", // 图文模块
        children: [
          {
            path: "/imgText/carouselList" // 轮播图列表
          },
          {
            path: "/imgText/specialList" // 特效列表
          },
          {
            path: "/imgText/articleList" // 文章列表
          }
        ]
      },
      {
        path: "/user", // 用户中心模块
        children: [
          {
            path: "/user/userInfo" // 用户信息
          },
          {
            path: "/user/userAdmin" // 用户管理
          },
          {
            path: "/user/editPwd" // 修改密码
          },
          {
            path: "/user/feedbackList" // 用户反馈
          }
        ]
      }
    ]
  }
];

// 给路由添加组件
(function addRouter(arr) {
  arr.forEach(elem => {
    const path = elem.path;
    let url = "";
    // 首页-单页自定路径
    switch (path) {
      case "/":
        url = "/index";
        break;
      case "/home":
        url = "/index/home";
        break;
      default:
        url = path;
        break;
    }
    elem.component = Require(() => import(`~/${url.slice(1)}`)); // 懒加载组件
    // 判断是否有子路由
    if (elem.children) {
      addRouter(elem.children); // 子集添加组件
    }
  });
})(Routes);

// 路由组件
export default function RouterView() {
  return (
    <BrowserRouter>
      {(function routerComponent(arr) {
        return (
          <Switch>
            {/* 路由声明组件 */}
            {arr.map(({ path, children, component: Comp }, i) => (
              <Route path={path} key={i} children={props => <Comp {...props}>{children && routerComponent(children)}</Comp>}></Route>
            ))}
            <Redirect path="*" to={arr[0].path}></Redirect>
          </Switch>
        );
      })(Routes)}
    </BrowserRouter>
  );
}

  • 懒加载方法Require
import React, { Component } from "react";
/**
 * 组件懒加载
 * @param {Any} importComponent  - 需要导入的组件
 * @returns {Any} - 导出组件
 */

export const Require = importComponent => {
  return class extends Component {
    state = {
      component: null
    };
    componentDidMount() {
      importComponent() //传进来的函数返回我们想要的组件
        .then(cmp => {
          //这里的cmp估计就是组件的返回值,类似promise的resolve写法
          this.setState({ component: cmp.default }); //把组件存起来
        });
    }
    render() {
      const C = this.state.component; // 渲染的时候把组件 B 拿出来
      return C ? <C {...this.props} /> : null; // 返回的其实就是组件 B,并且把传给A的属性也转移到B上
    }
  };
};
  • ~/表示自定义aliaspath.resolve(__dirname, "src/views")
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页