共计 4558 个字符,预计需要花费 12 分钟才能阅读完成。
在 react 中,通常都是应用单页面利用 (SPA),即整个页面只有一个 html,而后通过不同的 url 地址进行组件的匹配和切换。
咱们看到的 url 地址可能会有两种模式,一种是 localhost:3000/home,一种是 localhost:3000/#/home,两种地址的区别在于有无 #,有#的是依据 hash 来进行匹配,即 url 中的锚点,实质上是通过 location.hash 来扭转 href,hash 后的内容是不会发送给服务器的,没有# 是通过 html5 的 history 来进行跳转,两者跳转后都不会进行刷新。
以下路由是美团和网易云音乐的,能够看到他们的路由地址的别离是基于 hash 和 history
具体来说,通过 #来辨别路由的实现原理是监听 hash 的变动,这里监听的形式是应用 hashchange 事件,当 url 地址产生了变动时,通过匹配以后 url 的地址来进行展现,代码如下所示
<body>
<div>
<a href="#/home"> 首页 </a>
<a href="#/about"> 对于 </a>
</div>
<div id="content"></div>
<script>
window.addEventListener('hashchange',()=>{const content = document.getElementById('content')
switch(location.hash){
case '#/home':
content.innerHTML = '首页'
break
case '#/about':
content.innerHTML = '对于'
break
default:
content.innerHTML = ''
}
})
</script>
</body>
通过 html5 的 history 来实现跳转无刷新,就须要阻止 a 标签的默认行为,再通过 history 的 pushState 这一办法实现 url 地址的替换,同样是监听 url 地址的变动,这里应用 popState 办法,当 url 地址产生了变动之后,展现对应的内容。
<body>
<div>
<div>
<a href="/home"> 首页 </a>
<a href="/about"> 对于 </a>
</div>
<div id="content">
</div>
</body>
<script>
const content = document.getElementById('content')
const aEles = document.getElementsByTagName('a')
for (let el of aEles) {el.addEventListener('click', (event) => {event.preventDefault()
const href = el.getAttribute("href")
history.pushState({}, '', href)
urlchange()})
}
window.addEventListener('popstate', () => {urlchange()
})
function urlchange() {switch (location.pathname) {
case '/home':
content.innerHTML = '首页'
break
case '/about':
content.innerHTML = '对于'
break
default:
content.innerHTML = ''
}
}
</script>
以上的锚点和 history 别离对应了 react-router 中的 HashRouter 和 BrowserRouter,在 react-router 中,想要应用路由组件必须在最外层包裹一层 HashRouter 或者 BrowserRouter,来抉择想要应用的路由类型,而后能力应用 react-router 提供的其它组件,react-route 中用于 web 端的库为 react-router-dom,以下所有的组件都是从 react-router-dom 中导出。
上面来说说 react-router 的罕用组件
1、<Link> 和 <NavLink>,这两个标签都是由 a 标签的封装,通过 to 属性指定跳转的链接地址,<NavLink> 比 <Link> 多的是能够指定选中的款式和类名,格局如
<Link to="/about"> 对于 </Link>
通过 <Link> 标签,点击之后就能够跳转到指定的地址,此时即便外层包裹的是 HashRouter,也不须要本人加上 #,react-router 会帮咱们在 url 上加上#
2、当应用 <Link> 定义了跳转的 url 地址后,此时须要指定匹配跳转该 url 地址时须要显示的内容,此时应用的是 <Route>,通过 path 属性指定 url 地址,component 属性指定渲染的组件,格局如
<Route path="/about" component={About} />
加上了之后,/about 这个地址显示的就是 About 这个组件里的内容。<Route> 进行的是含糊匹配,一个 url 门路可能能够匹配多个 Route,如果须要严格匹配的话,能够减少一个属性 exact,适宜没有二级路由的时候开启。
3、在有很多的 Route 的状况下,即便在第一个 Route 匹配到适合的之后,依然会持续向下匹配,直到最初一个,所以在所有的 <Route> 外包裹一个 <Switch> 标签能够让它进行惟一的匹配,匹配到适合的之后就不持续匹配了。
4、当所有的 <Route> 都无奈匹配到 url 上的地址时,能够定义 <Redirect> 组件间接重定向到一个页面,通过 to 来指定路由地址,这个组件要搁置到 <Route> 的最初面,因为它和 <Link> 不同,<Link> 是点击了之后才会跳转对应的地址,而 <Redirect> 会间接执行并跳转
<Redirect to="/about"/>
5、通过路由来匹配的组件称为路由组件,<Route path=”/about” component={About} />,这里的 About 就是路由组件,和其它的组件是不一样的,路由组件的 props 里有一些数据,其中包含三大属性,history、location 和 match,history 能够自定义页面的跳转,location 用来获取 url 地址相干的信息,match 能够用作动静路由的匹配。
但个别的组件是没有这些 props 属性的,如果个别的组件也须要这样一些属性的话,能够通过一个高阶组件 withRouter。如
class myCom extends PureComponent { }
export default withRouter(myCom)
再来说说路由传参
有时候,咱们心愿在链接上带一个 id 值或者两个页面之间跳转的时候传递一些参数,这时候有三种路由传参形式
1、params 传参
<NavLink to="/detail/1"> 商品详情 </NavLink>
<Route path="/detail/:id" component={Detail}/>
// 如果是自行定义跳转的地址能够通过 this.props.history.push("/detail/1")
此时的 id 就是动静的,能够在 id 这个地位传递任意的数值或者字符串,而后通过 props 里的 match 对象中的 params 获取动静匹配的内容
2、search 传参
<NavLink to="/detail?id=1"> 商品详情 </NavLink>
<Route path="/detail" component={Detail}/>
// 如果是自行定义跳转的地址能够通过 this.props.history.push("/detail?id=1")
此时相当于在 url 上增加一个问号进行拼接,须要把地址拼成一种键值对的模式,通过 props 里的 location 对象中的 search 属性获取从问号开始的匹配内容,这一种路由的匹配形式须要自行解析字符串
3、state 传参
<NavLink to={pathname: "/detail", state: { id: 1} }> 商品详情 </NavLink>
<Route path="/detail" component={Detail}/>
// 如果是自行定义跳转的地址能够通过 this.props.history.push("/detail", { id: 1})
这样的传递参数形式通过 props 里的 location 对象中的 state 属性来获取传递的值,这种形式的能够间接以对象的模式传递,并且可传递的数据更多,这些参数不会显示在 url 上
用一个小的组合案例展现以上内容
import React, {PureComponent} from 'react'
import {NavLink, Route, Switch, withRouter, Redirect} from "react-router-dom"
import Home from "./pages/Home"
import About from "./pages/About"
import Detail from "./pages/Detail"
import Product from './pages/Product'
class App extends PureComponent {jumpToProduct(){this.props.history.push('/product')
}
render() {
return (
<div>
<NavLink exact to="/"> 首页 </NavLink>
<NavLink to="/about"> 对于 </NavLink>
<NavLink to="/detail/1"> 详情 </NavLink>
<button onClick={e=>this.jumpToProduct()}> 商品 </button>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/detail/:id" component={Detail} />
<Route path="/product" component={Product}/>
<Redirect to="/"/>
</Switch>
</div>
);
}
}
export default withRouter(App)