react 详解之路由

image.png

React中的路由

概述

路由是什么

路由是一种实现单页面开发的解决方案(SPA),使用路由能够在不刷新页面的情况实现页面的跳转,更加贴近原生应用

怎么用

路由有两种:声明式导航和编程式导航

解决什么问题

通过依赖包能够解决在不刷新页面的情况下实现页面的跳转

下包

yarn add react-router-dom

yarn add react-router-dom@5.2.1

注意:不同版本的 router 使用方式不同
复制代码

声明式导航

基本使用

声明式导航使用的是组件的形式实现路由跳转

import {BrowserRouter as Router,Route,Link}

class Login extends React.Component {
    render () {
        <div>我是 Login 组件</div>
    }
}

class App extends React.Component {
    render () {
        return (
        	<BrowserRouter>
            	<Link to="/login">点我去登录</Link>
                <Route path="/login" component={Login}></Route>
            </BrowserRouter>
        )
    }
}
复制代码

注意:Route 和 Link 组件必须放在 BrowserRouter 或者 HashRouter 中

匹配规则

模糊匹配

在 react-router-dom 中匹配默认采用的是模糊匹配,也就是说当路由地址开头部分相同的路由组件都会被一同渲染出来

例如:

import {BrowserRouter as Router,Route,Link}

class Demo1 extends React.Component {
    render () {
        return <div>我是Demo1</div>
    }
}
class Demo2 extends React.Component {
    render () {
        return <div>我是Demo2</div>
    }
}

class App extends React.Component {
    render () {
        return (
        	<BrowserRouter>
            	<Link to="/demo/1">点我去Demo1</Link>
            	<Link to="/demo/2">点我去Demo2</Link>
                <Route path="/demo" component={Demo1}></Route>
                <Route path="/demo" component={Demo2}></Route>
            </BrowserRouter>
        )
    }
}
复制代码

只要 Route 中的 path 与 location 中的 pathname 开头部分相同,例如 login 与 / ,或者 /demo/first 与 /demo ,那么就会模糊匹配成功,渲染出所有路由

精准匹配

解决模糊匹配的解决方案,只有 path 与 pathname 完全一致才会匹配成功,渲染组件

import {BrowserRouter as Router,Route,Link}

class Demo1 extends React.Component {
    render () {
        return <div>我是Demo1</div>
    }
}

class App extends React.Component {
    render () {
        return (
        	<BrowserRouter>
            	<Link to="/demo/1">点我去Demo1</Link>
                <Route exact path="/demo" component={Demo1}></Route>
            </BrowserRouter>
        )
    }
}
复制代码

上述情况就会匹配失败,原因是因为添加 exact 属性,也就是 path 必须与页面的 pathname 必须完全一致才会匹配成功

404

404 解决方案是利用了 Route 不添加 path 就会无条件的特性

import {BrowserRouter as Router,Route,Link}

class notFount extends React.Component {
    render () {
        return (<div>我是 404 组件</div>)
    }
}

class App extends React.Component {
    render () {
        return (
        	<BrowserRouter>
                <Route component={notFount}></Route>
            </BrowserRouter>
        )
    }
}
复制代码

路由组件只要不添加 path 属性就会无条件渲染组件

常用组件

在 react-router-dom 中,有许多功能性组件,例如

高亮 NavLink

<NavLink to="/login" activeClassName="router-link-active">登录</NavLink>
复制代码

BrowserRouter

包裹路由组件,一个页面只能有一个 BrowserRouter,使用的是 history 模式的路由

import {BrowserRouter,Route,Link} from 'react-route-dom'

<BrowserRouter>
	<Link to="/login">登录</Link>
    <Route path="/login" component={Login}></Route>
</BrowserRouter>
复制代码

HashRouter

同上,使用的是 hash 模式的路由

import {HashRouter,Route,Link} from 'react-route-dom'

<HashRouter>
	<Link to="/login">登录</Link>
    <Route path="/login" component={Login}></Route>
</HashRouter>
复制代码

Route

用于路由组件占位,匹配成功后的路由组件将会替换 Route 组件

import {HashRouter,Route,Link} from 'react-route-dom'

<HashRouter>
	<Link to="/login">登录</Link>
    <Route path="/login" exact component={Login}></Route>
</HashRouter>
复制代码

可以使用 exact 属性实现精准匹配

Link

声明式导航的跳转组件,通过该组件可以实现页面的跳转

import {HashRouter,Route,Link} from 'react-route-dom'

<HashRouter>
    <Link to="/login">登录</Link>
<Route path="/login" exact component={Login}></Route>
</HashRouter>
复制代码

Redirect

重定向组件,通过该组件可以实现路由地址的重定向

import {HashRouter,Route,Link,Redirect} from 'react-route-dom'

<HashRouter>
	<Link to="/login">登录</Link>
	<Redirect from="/" to="/dashbord"></Redirect>
    <Route path="/login" exact component={Login}></Route>
</HashRouter>
复制代码

跳转后失效

Switch

Switch 是为了解决模糊匹配的问题,被该组件包裹的路由匹配规则只会渲染第一个成功匹配的组件

import {HashRouter,Route,Link,Redirect,Switch} from 'react-route-dom'

<HashRouter>
	<Link to="/login">登录</Link>
	<Redirect from="/" to="/dashbord"></Redirect>
	<Switch>
    	<Route path="/login" exact component={Login}></Route>
	</Switch>    
</HashRouter>
复制代码

6.0+解决方案

6.0+ 的 react-router-dom 使用方式发生了些许变化

import {BrowserRoter as Router,Routes,Route,Link}

<Router>
	<Link to="/login">登录</Link>
    <Routes>
    	<Route path="/login" element={<Login></Login>}></Route>
    </Routes>
</Router>
复制代码

编程式导航

跳转

编程式导航是利用了 props.history.push 方法实现跳转

import {BrowserRouter as Router,Route,Link}

class Demo1 extends React.Component {
    render () {
        return <button onClick={()=>{this.props.history.push('/login')}}>去登录</button>
    }
}

class App extends React.Component {
    render () {
        return (
        	<BrowserRouter>
            	<Link to="/demo/1">点我去Demo1</Link>
                <Route exact path="/demo" component={Demo1}></Route>
            </BrowserRouter>
        )
    }
}
复制代码

回退

回退也是利用了 props 中的方法

import {BrowserRouter as Router,Route,Link}

class Demo1 extends React.Component {
    render () {
        return <button onClick={()=>{this.props.history.go(-1)}}>回退到上一页面</button>
    }
}

class App extends React.Component {
    render () {
        return (
        	<BrowserRouter>
            	<Link to="/demo/1">点我去Demo1</Link>
                <Route exact path="/demo" component={Demo1}></Route>
            </BrowserRouter>
        )
    }
}
复制代码

动态路由

动态路由实际就是路由参数传参的形式,匹配规则为路由参数的动态属性的值

import {BrowserRouter as Router,Route,Link}

<Router>
	<Link to="/dashbord/123123">去首页</Link>
    <Route path="/dashbord/:userId"></Route>
</Router>
复制代码

路由传参

声明式导航

查询参数传参

传递参数
import {BrowserRouter as Router,Route,Link}

class Demo1 extends React.Component {
    render () {
        return <button onClick={()=>{this.props.history.go(-1)}}>回退到上一页面</button>
    }
}

class App extends React.Component {
    render () {
        return (
        	<BrowserRouter>
            	<Link to="/demo1?id=213">点我去Demo1</Link>
                <Route exact path="/demo" component={Demo1}></Route>
            </BrowserRouter>
        )
    }
}
复制代码
接收参数
this.props.location.search

参数是字符串,需要手动转换成对象格式的数据

this.props.location.search
    .slice(1)
    .split("&")
    .map((item) => {
    const target = item.split("=");
    return { [target[0]]: target[1] };
})
复制代码

路由参数传参

传递参数
import {BrowserRouter as Router,Route,Link}

class Demo1 extends React.Component {
    render () {
        return <button onClick={()=>{this.props.history.go(-1)}}>回退到上一页面</button>
    }
}

class App extends React.Component {
    render () {
        return (
        	<BrowserRouter>
            	<Link to={{pathname:'/demo/123',a:{aa:123123}}}>点我去Demo1</Link>
                <Route exact path="/demo/:id" component={Demo1}></Route>
            </BrowserRouter>
        )
    }
}
复制代码
接收参数
this.props.match.params.id
复制代码

注意:对象中的所有的键值对都会被带到 this.props.location 中

编程式导航

查询参数传参

传递参数
import {BrowserRouter as Router,Route,Link}

class Demo1 extends React.Component {
    render () {
        return <button onClick={()=>{this.props.history.push('/login?id=123')}}>去登录</button>
    }
}

class App extends React.Component {
    render () {
        return (
        	<BrowserRouter>
            	<Link to={{pathname:'login',params:{id:123}}}>点我去Demo1</Link>
                <Route exact path="/demo" component={Demo1}></Route>
            </BrowserRouter>
        )
    }
}
复制代码
接收参数
this.props.location.search

参数是字符串,需要手动转换成对象格式的数据

this.props.location.search
    .slice(1)
    .split("&")
    .map((item) => {
    const target = item.split("=");
    return { [target[0]]: target[1] };
})
复制代码

路由参数

传递参数
import {BrowserRouter as Router,Route,Link}

class Demo1 extends React.Component {
    render () {
        return <button onClick={()=>{this.props.history.push({path:'/login/' + '123'})}}>去登录</button>
    }
}

class App extends React.Component {
    render () {
        return (
        	<BrowserRouter>
            	<Link to={{pathname:'/demo/123',params:{id:123}}}>点我去Demo1</Link>
                <Route exact path="/demo/:id" component={Demo1}></Route>
            </BrowserRouter>
        )
    }
}
复制代码
接收参数
this.props.match.params.id
复制代码

传参总结

查询参数传参

查询参数的传参可以直接通过 中的 query 对象中添加属性传递参数

可以直接使用 this.props.location.参数名.属性

路由参数传参

路由参数需要提前定义,并且参数不会带到 this.props.location 中,而是会存储在 this.match.params.参数名.属性

seatch 传参

search 传参是以 ?号开头,& 分割,= 号相连的字符串,拼接到地址的末尾,需要手动将其转换成对象

state 传参

state 传参同 query 传参

跨域

package节点配置

由于 create-react-app 中未生成 webpack.config.js ,所以无法添加 proxyServer 插件,开启 proxy 服务

可以在 package.json 中配置代理节点

"proxy":"服务器地址"
复制代码

setupProxy配置

区别与在 package.json 中配置,可以使用第三方包实现跨域 http-proxy-middleware

const proxy  = require('http-proxy-middleware')

module.exports (app) => {
    app.use(
        proxy('/api',{
            target:'服务器地址',
            changeOrigin:true // 允许跨域
        }) 
    )
}
复制代码

路由原理

hash模式

import React from "react";
import "./App.css";

class Demo1 extends React.Component {
  render() {
    console.log(this.props);
    return (
      <div>
        <p>我是Demo1</p>
      </div>
    );
  }
}
class Demo2 extends React.Component {
  render() {
    console.log(this);
    return <div>我是Demo2</div>;
  }
}

class App extends React.Component {
  state = {
    componentName: "",
  };
  componentDidMount() {
    window.addEventListener("hashchange", (event) => {
      this.setState((newState) => {
        return {
          componentName: window.location.hash.slice(1),
        };
      });
    });
  }
  render() {
    return (
      <div>
        <button onClick={() => (window.location.hash = "/demo1")}>
          点我去Demo1
        </button>
        <button onClick={() => (window.location.hash = "/demo2")}>
          点我去Demo2
        </button>
        {(this.state.componentName === "/demo1" ||
          window.location.hash.slice(1) === "/demo1") && <Demo1></Demo1>}
        {(this.state.componentName === "/demo2" ||
          window.location.hash.slice(1) === "/demo2") && <Demo2></Demo2>}
      </div>
    );
  }
}
export default App;

复制代码

history 模式

import React from "react";
import "./App.css";

class Demo1 extends React.Component {
    render() {
        return (
            <div>
                <p>我是Demo1</p>
            </div>
        );
    }
}
class Demo2 extends React.Component {
    render() {
        return <div>我是Demo2</div>;
    }
}

class App extends React.Component {
    state = {
        componentName: "",
    };
componentDidMount() {
    window.addEventListener("popstate", (event) => {
        console.log(event);
    });
}
render() {
    const render = () => {
        // 切换 history 地址让他更新一下
        this.forceUpdate();
    };
    return (
        <div>
            <button
                onClick={() => {
                    window.history.pushState({}, "", "/demo1");
                    render();
                }}
                >
                点我去Demo1
            </button>
            <button
                onClick={() => {
                    window.history.pushState({}, "", "/demo2");
                    render();
                }}
                >
                点我去Demo2
            </button>
            {window.location.pathname === "/demo1" && <Demo1></Demo1>}
            {window.location.pathname === "/demo2" && <Demo2></Demo2>}
        </div>
    );
}
}
export default App;

复制代码

猜你喜欢

转载自juejin.im/post/7076254888006533127