相关理解
SPA:单页面应用
整个应用只有一个完整的页面
点击页面链接不会刷新页面,只会页面局部更新
数据通过ajax请求获取,在前端异步展现
路由【一个路由就是一个映射关系(key:value)key:路径 value:function或者component】
后端路由【value是function,处理客户端提交的请求】
注册路由:router.get(path,function(req,res))
工作流程:当node接收到请求,根据请求路径找到匹配的路由,调用路由中的函数处理请求,返回请求数据
前端路由【浏览器路由,values是component,用于展示页面内容】
注册路由 <Route path="/test" component={Test}>
工作过程:当浏览器的path变成/test,前端路由组件就会变成Test组件
react-router
react 的一个插件库
专门实现SPA应用
基于react 项目基本都会用到此库
react-router相关API
内置组件
<BrowserRouter>
<HashRouter>
<Route>
<Redirect> ——————react-router-dom@6取消了重定向方法
<link>
<NavLink>
<Switch> ——————<Switch>重命名为<Routes>
其他
history对象
match对象
withRouter对象
注意事项
Route 组件必须包裹 Routes 组件
Route 组件属性由原来的 component 改为了 element
a标签由 Link组件代替 to跳转路径
<Link className="list-group-item" to="/about">About</Link>
渲染的时候 App组件 包裹 BrowserRouter 或者 HashRouter
ReactDOM.render(<BrowserRouter><App/></BrowserRouter> ,document.getElementById('root'));
Navlink组件 改样式【v6版 支持小写 activeclassname=’active’】【v5 activeClassName】
<NavLink className={({ isActive }) => "list-group-item" + (isActive ? " active1" : "")} to="/about">About</NavLink>
标签体内容是一个特殊的标签属性【this.props.children获取】 【对NavLink 封装到 MyNavLink组件】
<MyNavLink name='About' to="/about" a={1} a={100} a='12'>About</MyNavLink>
1 2 3 4 5 // v6 <Routes> <Route path="/about" element={<About></About>}></Route> <Route path="/home" element={<Home></Home>}></Route> </Routes>
路由组件和一般组件
存放位置:一般组件【components】路由组件【pages】
写法:一般组件<demo/>
路由组件 <Route path="/home" element={<Home></Home>}></Route>
接收到的props不同【v5版】: 一般组件【写什么接收什么】 路由组件【v5:接收三个固定属性:history、localtion、match】
Switch组件
通常情况下,path和component是一一对应的关系。
Switch可以提高路由匹配效率(单一匹配)。1 2 3 4 5 6 // v5 <Switch> <Route path="/home" component={Home}></Route> <Route path="/about" component={About}></Route> <Route path="/about" component={Test}></Route> </Switch>
v5 : 精准匹配 与 模糊匹配 【 开启精准匹配: exact={true}】
<Route exact={true} path="/about" element={<About></About>}></Route>
v5 : Redirect 重定向 v6 :Navigate useNavigate
//v5 <Redirect to="/about"></Redirect>
//v6 <Route path="*" element={<Navigate to="/about"/>} />
嵌套路由【v6】 1 2 3 4 5 6 7 8 9 10 11 12 // App.jsx Routes> <Route path="/about" element={<About></About>}></Route> <Route path="/home" element={<Home></Home>}> <Route path="/home/news" element={<News></News>}></Route> <Route path="/home/messages" element={<Messages></Messages>}></Route> <Route path="/home" element={<Navigate to="/home/news" />} /> </Route> <Route path="*" element={<Navigate to="/about" />} /> </Routes> // Home.jsx <Outlet></Outlet>
路由传值的三种方式(v5 和 v6) 1.params参数【传参带/,路由带冒号,useParams】
v5:
1 2 3 4 5 6 7 8 9 //路由链接(携带参数): <Link to='/demo/test/tom/18'}>详情</Link> //或 <Link to={{ pathname:'/demo/test/tom/18' }}>详情</Link> //注册路由(声明接收): <Route path="/demo/test/:name/:age" component={Test}/> //接收参数: this.props.match.params
v6: useParams 只能在函数组件中使用<Link to={
/home/messages/detail/${id}/${title}}>Child1</Link>
<Route path="/home/messages/detail:id/:title" component={Test}/>
1 2 3 4 5 6 7 8 9 10 11 //路由链接(携带参数): <Link to={{ pathname:`/b/child1/${id}/${title}` }}>Child1</Link> //或 <Link to={`/b/child1/${id}/${title}`}>Child1</Link> //注册路由(声明接收): <Route path="/b/child1/:id/:title" component={Test}/> //接收参数: import { useParams } from "react-router-dom"; const params = useParams(); //params参数 => {id: "01", title: "消息1"}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import React, { Component } from 'react' import { useParams } from 'react-router-dom' export default function Detail() { const {id,title} = useParams(); return ( <div> <ul> <li>ID: {id}</li> <li>TITLE:{title}</li> <li>CONTENT:???</li> </ul> </div> ) }
2.search参数【传参带问号,路由不变,useSearchParams】
v5
1 2 3 4 5 6 7 8 9 10 //路由链接(携带参数): <Link to='/demo/test?name=tom&age=18'}>详情</Link> //注册路由(无需声明,正常注册即可): <Route path="/demo/test" component={Test}/> //接收参数: this.props.location.search //备注:获取到的search是urlencoded编码字符串(例如: ?id=10&name=zhangsan),需要借助query-string解析参数成对象
v6: useSearchParams 只能在函数组件中使用<Link to={
/home/messages/detail?id=${item.id}&title=${item.title}}>{item.title}</Link>
<Route path="/home/messages/detail" element={<Detail></Detail>}></Route>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 //路由链接(携带参数): <Link className="nav" to={`/b/child2?age=20&name=zhangsan`}>Child2</Link> //注册路由(无需声明,正常注册即可): <Route path="/b/child2" component={Test}/> //接收参数方法1: import { useLocation } from "react-router-dom"; import qs from "query-string"; const { search } = useLocation(); //search参数 => {age: "20", name: "zhangsan"} //接收参数方法2: import { useSearchParams } from "react-router-dom"; const [searchParams, setSearchParams] = useSearchParams(); // console.log( searchParams.get("id")); // 12 //备注:获取到的search是urlencoded编码字符串(例如: ?age=20&name=zhangsan),需要借助query-string解析参数成对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import React, { Component } from 'react' // 方式一 import { useSearchParams } from "react-router-dom"; // 方式二 // import { useLocation } from "react-router-dom"; // import qs from "query-string"; export default function Detail() { // 1.接收参数 const [searchParams, setSearchParams] = useSearchParams(); // console.log(searchParams.get("id")); // console.log(searchParams.get("title")); // 2.接收参数 // const { search } = useLocation(); // let { id, title } = qs.parse(search) return ( <div> <ul> <li>ID: {searchParams.get("id")}</li> <li>TITLE: {searchParams.get("title")}</li> {/* <li>ID: {id}</li> <li>TITLE: {title}</li> */} <li>CONTENT:???</li> </ul> </div> ) }
3.state参数【传参带state属性,路由不变,useLocation,[浏览器路径:/home/messages/detail,没有带参数]】
v5
1 2 3 4 5 6 7 8 9 10 //路由链接(携带参数): <Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link> //注册路由(无需声明,正常注册即可): <Route path="/demo/test" component={Test}/> //接收参数: this.props.location.state //备注:刷新也可以保留住参数
v6<Link to={
/home/messages/detail} state={{ id: item.id, title: item.title }} >{item.title}</Link>
<Route path="/home/messages/detail" element={<Detail></Detail>}></Route>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 //通过Link的state属性传递参数 <Link className="nav" to={`/b/child2`} state={{ id: 999, name: "i love merlin" }} > Child2 </Link> //注册路由(无需声明,正常注册即可): <Route path="/b/child2" component={Test}/> //接收参数: import { useLocation } from "react-router-dom"; const { state } = useLocation(); //state参数 => {id: 999, name: "我是梅琳"} //备注:刷新也可以保留住参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import React, { Component } from 'react' import { useLocation } from "react-router-dom"; {/* <Route path="/home/messages/detail" element={<Detail></Detail>}></Route> */} {/* <Link to={`/home/messages/detail`} state={{ id: item.id, title: item.title }} >{item.title}</Link> */} export default function Detail(){ const { state } = useLocation(); console.log(useLocation()); return ( <div> <ul> <li>ID: {state.id}</li> <li>TITLE: {state.title}</li> <li>CONTENT:???</li> </ul> </div> ) }
class组件接收参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 import React, { Component, useImperativeHandle } from 'react' import { useParams } from "react-router-dom"; // APP.jsx 路由配置 {/* <Route path="/home/messages/detail/:id/:title" element={<Detail></Detail>}></Route> */} // Messages.jsx 列表页面 {/* <Link to={`/home/messages/detail/${item.id}/${item.title}`}>{item.title}</Link> */} function A(props) { // 获取动态路由的值 const params = useParams(); useImperativeHandle(props.onRef, () => { return { params, } }); return ( <span></span> ) } export default class Detail extends Component { constructor(props) { super(props); this.state = { // 创建ref funRef: React.createRef(), data:{} } } paramsData = ()=> { let {funRef} = this.state; this.setState({ data:funRef.current.params }) console.log(funRef.current.params); } render() { let {funRef,data} = this.state; return ( <div> <ul> <li>ID: {data.id}</li> <li>TITLE: {data.title}</li> <li>CONTENT:???</li> </ul> <button onClick={this.paramsData}>获取params数据</button> <A onRef={funRef} style={{ display: 'none' }}></A> </div> ) } }
push 和 replace 模式 react 默认是push模式
replace跳转不会形成history,不可返回到上一层this.props.history.replace(‘router地址’)
push跳转会形成history,可返回到上一层this.props.history.push(‘router地址’)
编程式跳转 函数式组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 // v5 import { useHistory } from 'react-router-dom'; function MyButton() { let history = useHistory(); function handleClick() { history.push('/home'); }; return <button onClick={handleClick}>Submit</button>; }; // v6 import { useNavigate } from 'react-router-dom'; function MyButton() { let navigate = useNavigate(); function handleClick() { navigate('/home'); }; return <button onClick={handleClick}>Submit</button>; }; // v5 history.push('/home'); history.replace('/home'); // v6 navigate('/home'); navigate('/home', {replace: true});
1 2 3 4 5 6 7 8 9 10 11 12 13 1. import { useNavigate } from 'react-router-dom' 2. let navigate = useNavigate(); function showReplace(id, title) { // params参数 // navigate(`/home/messages/detail/${id}/${title}`,{replace: true}); // search参数 navigate(`/home/messages/detail/?id=${id}&title=${title}`,{replace: true}) // navigate state参数 【似乎没有这种方式】 // navigate(`/home/messages/detail`, { 'id':id, 'title':title }) }; 3. <button onClick={() => showReplace(item.id, item.title)}>replace查看</button>
类组件 v5
1 2 this.props.history.replace(‘router地址’) this.props.history.push(‘router地址’)
withRouter 【v5有,v6应该是删除了,不能使用】 withRouter:加工一般组件,让一般组件具备路由组件的API,返回值是一个新组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import React, { Component } from 'react' import { withRouter } from 'react-router-dom' class Header extends Component { back = () => { // this.props.history.goBack() } // forward = () => { // this.props.history.goForward() // } // go = () => { // this.props.history.go(-2) // } render() { return ( <div> <h2>React Router Demo111</h2> <button onClick={this.back}>回退</button> <button onClick={this.forward}>前进</button> <button onClick={this.go}>go</button> </div> ) } } export default withRouter(Header)
BrowserRouter 和 HashRouter 的区别 1.底层原理不一样: BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。 HashRouter使用的是URL的哈希值。 2.path表现形式不一样 BrowserRouter的路径中没有#,例如:localhost:3000/demo/test HashRouter的路径包含#,例如:localhost:3000/#/demo/test 3.刷新后对路由state参数的影响【v6没有影响】 (1).BrowserRouter没有任何影响,因为state保存在history对象中。 (2).HashRouter刷新后会导致路由state参数的丢失!!! 4.备注:HashRouter可以用于解决一些路径错误相关的问题。