十四、react路由
一、路由的使用
1 npm i -S react-router-dom
1、相关组件
- Route组件:指定路由展示组件相关信息(组件渲染)
- path属性:路由规则,这里需要跟Link组件里面to属性的值一致
- component属性:展示的组件
2、封装NavLink组件
1、NavLink标签是一个高亮显示的导航标签
2、NavLink标签体内容是一个特殊的标签属性,通过this.props.children属性可以获取标签体内容(即Home或About)
NavLink封装
1 import React,{Component} from 'react' 2 import {NavLink} from 'react-router-dom' 3 4 export default class MyNavLink extends Component{ 5 render(){ 6 return( 7 <NavLink activeClassName="link-name" className="link-item" {...this.props}/> 8 ) 9 } 10 }
使用
1 <MyNavLink to="/home">Home</MyNavLink> 2 <MyNavLink to="/about">About</MyNavLink>
3、Switch组件
1 <Switch> 2 {/*Switch只渲染第一个匹配的组件*/} 3 <Route path="/home" component={Home}></Route> 4 <Route path="/home" component={Test}></Route> 5 <Route path="/about" component={About}></Route> 6 </Switch>
二、路由导航方式
1、声明式导航
1 import React from "react"; 2 import ReactDOM from "react-dom"; 3 4 // 设置路由模式 5 import {HashRouter as Router} from 'react-router-dom' 6 7 // 定义 provider 8 import { Provider } from "react-redux"; 9 import store from "./Store/index"; 10 11 import App from "./App"; 12 13 ReactDOM.render( 14 <Provider store={store}> 15 // 使用Router包裹根组件 16 <Router> 17 <App></App> 18 </Router> 19 </Provider>, 20 document.getElementById("root") 21 );
1 import React, { Component } from "react"; 2 import { HashRouter as Router, Route, Link } from "react-router-dom"; 3 4 import Cmp10 from "./Components/Cmp10"; 5 import Cmp11 from "./Components/Cmp11"; 6 7 class App extends Component { 8 render() { 9 return ( 10 <Router> 11 <div> 12 <h1>导航区域</h1> 13 <div> 14 <ul> 15 <li> 16 <Link to="/home">首页</Link> 17 </li> 18 <li> 19 <Link to="/news">新闻</Link> 20 </li> 21 </ul> 22 </div> 23 </div> 24 <Route path="/home" component={Cmp10}></Route> 25 <Route path="/news" component={Cmp11}></Route> 26 </Router> 27 ); 28 } 29 } 30 export default App;
需要注意:
1 <MyNavLink replace to="/home">Home</MyNavLink>
1 this.props.history.push({ 2 pathname: "/home", 3 search: "from=404", // 表示传递查询字符串 4 state: { // 隐式传参,地址栏不体现 5 username: "admin", 6 }, 7 }); 8 9 this.props.history.go(-1)
不要在根组件中使用编程式导航。
三、路由匹配
1、模糊匹配与严格匹配
模糊匹配(默认)
1 {/*MyNavLink中的/about/a/b匹配到Route中的/about"*/} 2 <MyNavLink to="/home">Home</MyNavLink> 3 <MyNavLink to="/about/a/b">About</MyNavLink> 4 5 <Switch> 6 <Route path="/home" component={Home}></Route> 7 <Route path="/home" component={Test}></Route> 8 <Route path="/about" component={About}></Route> 9 </Switch>
严格匹配:exact属性
1 <MyNavLink to="/home">Home</MyNavLink> 2 <MyNavLink to="/about/a/b">About</MyNavLink> 3 4 <Switch> 5 <Route exact path="/home" component={Home}></Route> 6 <Route exact path="/home" component={Test}></Route> 7 <Route exact path="/about" component={About}></Route> 8 </Switch>
2、重定向路由
React的重定向路由有以下两种写法:
方式一:推荐
1 import { Redirect } from "react-router-dom" 2 3 <Redirect from="/from" to="/to"></Redirect> 4 <Route path="/to" component={xxxx}></Route>
方式二 :不推荐
1 import { Route, Redirect } from "react-router-dom" 2 3 <Route path="/from"> 4 <Cmp></Cmp> 5 <Redirect to="/to" /> 6 </Route>
3、404路由
项目中少不了404页面的配置,在React里面配置404页面需要注意:
1 import NotFound from "./Components/404"; 2 3 <Route> 4 <NotFound></NotFound> 5 </Route> 6 // 或 7 <Route component={NotFound}></Route>
- 并不会因为当前是404路由/重定向路由而改变状态码,因为当前写的是前端的内容,状态码是后端提供的,只有等后期上线以后才能有状态码。
1 <div> 2 <Link to="/home">家</Link>   3 <Link to="/news">新闻</Link>  4 <Link to="/about">关于</Link>  5 <Redirect from="/" to="/home"></Redirect> 6 <Switch> 7 <Route path="/home" component={Cmp11}></Route> 8 <Route path="/news" component={Cmp12}></Route> 9 <Route path="/about" component={Cmp13}></Route> 10 <Route component={NotFound}></Route> 11 </Switch> 12 </div>
四、嵌套路由
在有一些功能中,往往请求地址的前缀是相同的,不同的只是后面一部份,此时就可以使用多级路由(路由嵌套)来实现此路由的定义实现。
例如,路由规则如下
1 admin/user 2 admin/goods
1 <Route path="/admin" component={Admin}></Route>
- 创建模块路由组件负责指定各个路由的去向
1 render() { 2 // 获取前缀,供后续地址做路由拼接 3 let prefix = this.props.match.path; 4 return ( 5 <div> 6 <h1>欢迎使用后台管理程序</h1> 7 <Route path={`${prefix}/user`} component={User}></Route> 8 <Route path={`${prefix}/goods`} component={Goods}></Route> 9 </div> 10 ); 11 }
五、路由懒加载
优化性能,让路由按需加载
1 import React, {Component, lazy, Suspense} from 'react' 2 import {NavLink, Route} from 'react-router-dom' 3 4 // Loading用于懒加载组件未加载出来的展示 5 import Loading from './Loading' 6 7 // import Home from './Home' 8 // import News from './News' 9 10 const Home = lazy(() => {import('./Home')}) 11 const News = lazy(() => {import('./News')}) 12 13 export default class Demo extends Component{ 14 <div> 15 <h1>导航区域</h1> 16 <div> 17 <ul> 18 <li> 19 <Link to="/home">首页</Link> 20 </li> 21 <li> 22 <Link to="/news">新闻</Link> 23 </li> 24 </ul> 25 </div> 26 <Suspense fallback={<Loading/>}> 27 <Route path="/home" component={Home}></Route> 28 <Route path="/news" component={News}></Route> 29 </Suspense> 30 </div> 31 }
六、路由参数
路由参数:在Route定义渲染组件时给定动态绑定的参数。
React路由传参方式有三种:
1、动态路由参数(param)
1 <Link to={`/film/detail/${detailObj.id}`}></Link>
以/film/detail/:id
1 <Router path="/film/detail/:id" component={Detail}></Router>
3、
- 不适合写在声明式导航中,写在编程式导航中更加合适
1 <Link to={{pathname:`/film/detail`,state:{id:detailObj.id,title:detailObj.title}}}></Link>
- 埋点数据
1 constructor(props){ 2 super(props) 3 this.state = { 4 // 接收动态路由参数 5 news_id: this.props.match.params.id, 6 // 接收查询字符串并处理 7 query: querystring.parse(this.props.location.search.slice(1)), 8 // 接收state 9 state: this.props.location.state 10 }; 11 }
七、路由三种渲染方式
1 <Route path="/home" component={Home} />
1 <Route path="/home" component={() => <Home />} />
2、render属性(函数)
1 <Route path="/home" render={props => <Home />} />
3、
1 <Route path="/about" children={props => { 2 if(props.match){ 3 return <div>children渲染</div> 4 } 5 }} />
1 <Route path="/about" children={<About />} />
注意
- component可以使用组件类渲染或内联方式渲染,render只能使用函数,children使用函数或直接使用组件
- 当children的值是一个函数时,无论当前地址和path路径匹不匹配,都将会执行children对应的函数,当children的值为一个组件时,当前地址和path不匹配时,路由组件不渲染
- children函数方式渲染,会在形参中接受到一个对象,对象中match属性如果当前地址匹配成功返回对象,否则null
- children函数方式渲染,在自定义导航组件的上面会非常好用
八、withRouter高阶组件
作用:把不是通过路由切换过来的组件中,将react-router 的 history、location、match 三个对象传入props对象上
- 默认情况下,必须是经过路由匹配渲染的组件才存在this.props才拥有路由参数,才能使用编程式导航的写法,才能执行this.props.history.push('/uri')跳转到对应路由的页面
- 然而不是所有组件都直接与路由相连的,当这些组件需要路由参数时,使用withRouter就可以给此组件传入路由参数,此时就可以使用this.props
1 // 引入withRouter 2 import { withRouter} from 'react-router-dom' 3 4 // 执行一下withRouter 5 export default withRouter(Cmp)
该高阶组件是路由包自带的东西,因此只需要引入+使用就可以了,不需要自己定义。
九、自定义导航组件
1 import React, { Component, Fragment } from "react"; 2 import { Route } from "react-router-dom" 3 // 引入自定义导航组件 4 import MyLink from "./Components/MyLink" 5 import Cmp1 from "./Components/Cmp1"; 6 import Cmp2 from "./Components/Cmp2"; 7 8 class App extends Component { 9 render() { 10 return ( 11 <Fragment> 12 <MyLink tag="h1" to="/cmp1"> 13 去1 14 </MyLink> 15 <MyLink tag="h1" to="/cmp2"> 16 去2 17 </MyLink> 18 <Route path="/cmp1" component={Cmp1}></Route> 19 <Route path="/cmp2" component={Cmp2}></Route> 20 </Fragment> 21 ); 22 } 23 } 24 export default App;
自定义导航组件参考
1 import React, { Component, Fragment } from "react"; 2 import { withRouter, Route } from "react-router-dom"; 3 4 class MyLink extends Component { 5 // 点击跳转动作 6 goUrl = () => { 7 this.props.history.push(this.props.to); 8 }; 9 render() { 10 // 获取参数 11 var Tag = this.props.tag ? this.props.tag : "a"; 12 return ( 13 <Fragment> 14 <Route 15 path={this.props.to} 16 children={({ match }) => { 17 if (match) { 18 // 匹配 19 return ( 20 <Tag 21 onClick={this.goUrl} 22 style={{ color: "red" }} 23 > 24 {this.props.children} 25 </Tag> 26 ); 27 } else { 28 // 不匹配 29 return ( 30 <Tag onClick={this.goUrl}> 31 {this.props.children} 32 </Tag> 33 ); 34 } 35 }}> 36 </Route> 37 </Fragment> 38 ); 39 } 40 } 41 42 export default withRouter(MyLink);
十、样式引入问题
css样式引入:public文件下自定义样式引入
- 导航方式:BrowserRouter改成HashRouter