React之路由

路由

安装

  • 先用create-react-app生成一个react项目
  • 进入该项目
  • 安装包 react-router-dom
  • 分为2种模式:hash(哈希),browser(浏览器的那种,H5提供的API)

认识路由

路由模块,需要一个路由容器,标识着使用了那种模式的路由

import {容器} from "react-router-dom";
// 容器中存放的就是路由:HashRouter 或者 BrowserRouter
// 这2种都可以,并且都是组件,为了我们方便来回使用方便一般都会给他们起个别名
//⚠️ 起完别名之后,则BrowserRouter这个名字就作废了。
import {BrowserRouter as Router} from "react-router-dom";

在以下案例中均已,Router 为BrowserRouter。
Route

  • 路由一条一条的路由,也是个组件
import {BrowserRouter as Router,Route} from "react-router-dom";

渲染路由

⚠️ Router 路由盒子,只能有一个根节点

  • 先渲染Router
  • 在渲染Route

修改前

import React,{Component} from "react";
import ReactDOM from "react-dom";
import {BrowserRouter as Router,Route} from "react-router-dom";

render(
//直接Router 放在第一个根节点会报错,Error:  Router只能有一个child
 //所以要加一个 <>或者 div 包起来
,wondow.root)

修改后

import React,{Component} from "react";
import ReactDOM from "react-dom";
import {BrowserRouter as Router,Route} from "react-router-dom";
render(
	<>
	
	
	
,wondow.root)

每条路由都和对应的组件创建链接
默认路由都是从上到下匹配,如果匹配成功就会渲染对应的组件

  • path 路由地址
  • component 对应的组件
  • exact 是否严格匹配,默认为不严格匹配(false)。
import React,{Component} from "react";
import ReactDOM from "react-dom";
import {BrowserRouter as Router,Route} from "react-router-dom";
import Home from "./pages/Home";
import Proile from "./pages/Proile";
render(
	<>
	
	
	
,wondow.root)

路由匹配的问题

  • 路由只匹配开头
  • path="/home" 和 path="/home/user" 都有/home 会渲染2个组件
  • home 一般用/路径表示,在浏览器中输入/会显示home,输入/user或者其他的/… 也会显示home页面
    此时,只需要加入是否严格匹配 exact
render(
	<>
	
	
	
,wondow.root)

解决404问题,在后端处理,找不到的话就返回首页,首页会根据当前的路径再次渲染路由。

  • Vue 中有专门说根据那种方式来处理的
    https://router.vuejs.org/guide/essentials/history-mode.html#example-server-configurations
  • react-router-dom的解决方案
    • 添加一个Redirect 组件用来解决找不到对应的页面的时候
      • to 找不到页面是定位到哪里页面的地址
    • 存在的问题当我们给home的地址设置为 / 时,这是Redirect的地址也是**/** ,页面会报错:Error 你尝试去定位一个相同的路由
    	import {BrowserRouter as Router,Route,Redirect} from "react-router-dom";
    	render(
    		<>
    		
    		
    		
    		
    	,wondow.root)
    
    • 解决方案
      希望路由匹配到一个只会,就不要向下匹配了。
    • 添加一个Switch 组件
    • Switch 里面只能包路由Route
    • 把包裹Route的元素换成Switch就好
    	import {BrowserRouter as Router,Route,Redirect} from "react-router-dom";
    	render(
    		
    		
    		
    		
    		
    	,wondow.root)
    

路由导航

  • Link
  • NavLink
  • 这2个都是组件,用法也都一样,唯一的就是:NavLink 可以给选中的按钮配置高亮样式
  • 默认都是a标签
  • 不支持指定标签名
    • to 路径
    • 使用NavLink 要注意当碰到首页是一个**/的时候加exact属性严格匹配,不然会出现多个元素都有高亮**类名
	import {BrowserRouter as Router,Route,Redirect,NavLink} from "react-router-dom";
	render(
			<>
				
//
,wondow.root)

优化

每个页面都各自处理自己的内容

布局:nav.js 存放导航,index.js 存放路由并渲染到页面,home.js /user.js 是导航对应的内容
需求:每个页面都各自存放自己的东西,采用另一个app.js 页面把路由和导航组合到一起,这样就不需要把导航文件引入到index.js路由页面了

  • 把导航nav.js 页面在app.js 页面引入
  • 把app.js 页面在 index.js 路由页面引入
  • 用引入的App组件包裹 ‘Switch’ 组件
  • 在app.js 里面使用 {this.props.children} 把路由在这里展示

index.js

import App from "./app";
render(
			
				
				
				
				
				
			
		,wondow.root)

App.js

import React,{Component} from "react";
import ReactDOM from "react-dom";
import Nav from "./nav";

export default class App extends Component{
constructor(){
	super();
}
	render(
		return(
			
{this.props.children}
) ) }

添加二级导航

  • 在user.js为例

改写user.js

import React, { Component } from 'react';
import SliderBar from '../components/SliderBar';
import {Route,Switch} from 'react-router-dom';
import Add from './Add'
import UserDetail from './UserDetail'
import List from './List'
export default class User extends Component {
  state = {
  // 二级路由地址和标题
    sliderBarData: [
      { path: '/user/add', content: '用户添加' },
      { path: '/user/list', content: '用户list' },
    ]
  }
  render() {
    return (
{/* 二级菜单 默认展示添加路由 */} //当点击user路径的时候默认展示Add组件的内容。这样不会当我们点击user路径的时候页面空着 ///user/detail/:uid 路径参数
) } }

SliderBar.js 二级导航

	import React, { Component } from 'react';
	import { Link} from  'react-router-dom'
	export default class SliderBar extends Component {
	  constructor() {
	    super();
	  }
	  render() {
	    return ()
	  }
	}

Add.js

  • 使用了 Router 容器,路由容器上会挂在着一些属性,Provider(供应商的意思,在最外层组件提供一些属性,所有子孙都可以应用),histoty…
  • Route 组件中 可以获取到 父级提供的属性,Route来消费,并把这些属性传递给了渲染组件_
  • 在Add.js 中console.log(this.props);可以看到有3个属性分别是: history,location,match
    加粗为常用方法
    • history 类似浏览器的history,可以理解为主要是实现跳转
      history下的方法
      • go 去哪
      • goBack 后退
      • goForward 往前走
      • push 跳转页面
    • location 当前关于路径的一些信息。
      • hash
      • key
      • _pathname _
      • state 跳转页面时带的数据放在这里
    • match 匹配,当前路径是否和我的匹配
      • path
      • url
      • params:{} 当前路径的参数
      • isExact:false/true 是否严格匹配
	import React, { Component } from 'react';
	import ReactDOM from 'react-dom';
	export default class Add extends Component {
	  input = React.createRef();
	  constructor() {
	    super();
	  }
	  handleSubmit = (e) => {
	    e.preventDefault(); // 阻止默认行文
	    let username = this.input.current.value;
	    let lists = JSON.parse(localStorage.getItem('lists'))|| [];
	    lists.push({username,id: Math.random()});
	    localStorage.setItem('lists', JSON.stringify(lists));
	    // 使用路由容器后 路由容器上挂载着一些属性 Provider history..
	    // Route组件中可以获取到 父级提供的属性,Route来消费 并且把这些属性传递给了渲染的组件
	    this.props.history.push('/user/list'); //跳转到 list页面
	  }
	  render() {
	    return (
) } }

List.js

	import React, { Component } from 'react';
	import ReactDOM from 'react-dom';
	import {Link} from "react-router-dom";
	export default class List extends Component {
	  state = {
	    users: JSON.parse(localStorage.getItem('lists')) || [] //取app.js 添加的数据
	  }
	  render() {
	    return (
	          {this.state.users.map((user,index)=>{
	            return 
	              //如果不传state数据可以直接这样:
	             //  
	          })}
	      
id name
{user.id}{user.id} {user.username}
) } }

UserDetail.js 二级内容 当点击list的时候进入,展示id和内容

	import React,{Component} from 'react';
	import ReactDOM from 'react-dom';
	export default class UserDetail extends Component{
	    constructor(){
	        super();
	   }
	   render(){
	      return (
UserDetail //浏览器的地址 真实的 去到id {/* /user/detail/1 /user/detail/:id => id:1 */} //拿到当前路径的参数 {this.props.match.params.uid} {/* 如果没拿到状态 就在获取一遍 通过id*/} {this.props.location.state} //拿到传入的数据
) } }

Route 嵌套组件

过程

  • 最开始渲染Route组件
  • Route 又帮我们渲染 传入的组件

方法1
修改App.js

import React,{Component} from "react";
import ReactDOM from "react-dom";
import {Route} from "react-router-dom";
import Nav from "./nav";
export default class App extends Component{
constructor(){
	super();
}
	render(
		return(
			
// 改为 //这样可以使 Nav下面的所有导航都可以有history //所有用Route 渲染的组件 都有history {this.props.children}
) ) }

方法2
把App.js 复原

import React,{Component} from "react";
import ReactDOM from "react-dom";
import Nav from "./nav";
export default class App extends Component{
constructor(){
	super();
}
	render(
		return(
			
{this.props.children}
) ) }

Nav.js修改

import React, { Component } from 'react';
import { NavLink, Route, withRouter } from 'react-router-dom';
import MenuLink from './MenuLink';
//希望给当前组件带上一个Route,模拟的
// let withRouter = (Component) => {
//   return ()=>{
//     return 
//   }
// }
// 高阶组件 就是组件返回组件 ,可以把公共的功能放到父亲来做
// 封装公共方法的组件
class Nav extends Component {
  constructor() {
    super();
  }
  handleClick = () => {
    this.props.history.push('/')
  }
  render() {
    return (
      
    )
  }
}
// 如果某个组件 不是通过route来渲染的还想用里面的props 可以使用withRouter
export default withRouter(Nav); // 可以改写成 @withRouter的形式

权限校验

需求:判断用户是否登入,没有登入则跳转到登入页面,
login.js

	import React,{Component} from 'react';
	import ReactDOM from 'react-dom';
	export default class Login extends Component{
	   constructor(){
	        super();
	   }
	   render(){
	      return (
) } }

给index.js 添加login

	import Login from './Login';
	

给nav.js 添加login导航

登录
  • 这里拿user来做案例
  • 在进入user之前,在外面在包一层做校验
  • 叫做:高阶组件中判断用户是否校验,如果没做校验就跳转
    添加一个protected.js
	import React,{Component} from 'react';
	import { Route,Redirect} from 'react-router-dom';
	// 函数组件 参数是属性
	// 把component 拿出来 重新命名Component 组件名必须大写
	// props就是其他属性
	
	// route中可以放置 component
	// render render可以放一个函数 他会渲染这个函数的返回值
	let Protected = ({component:Component,...props})=>{
	  return { // p=> history,match,lication
	    return localStorage.getItem('login') ?  :     
	  }}/>
	}
	export default Protected

修改index.js

	import Protected from './Protected'
	render(
	  
	    {/* switch 会判断path */}
	    
	      
	      
	      {/* 高阶组件中判断用户是否登录过,如果没登陆跳转 Protected */}
	      
	      
	      
	    
	  
	, window.root)

你可能感兴趣的:(react,react-router)