手挽手带你学React入门三档,带你学会使用Reacr-router4.x,开始创建属于你的React项目
什么是React-router
React Router 是一个基于 React 之上的强大路由库,它可以让你向应用中快速地添加视图和数据流,同时保持页面与 URL 间的同步。通俗一点就是,它帮助我们的程序在不同的url展示不同的内容。
为什么要用React-router
我们开发的时候,不可能所有的东西都展示在一张页面上,在业务场景的要求下,我们要根据不同的URL或者不同的哈希来展示不同的组件,这个我们可以称它为路由。在我们不使用React-router的时候,我们如何去做路由呢?
我在这里给大家举个例子,不使用React-router,来实现一个简单路由。
// App.js
import React,{Component} from 'react'
export default class App extends Component {
constructor(){
super()
// 我们在App.js内部来渲染不同的组件 我们这里采用哈希路由的方式,鉴于React的渲染机制,我们需要把值绑定进入state内部。
this.state={
route:window.location.hash.substr(1)
}
}
componentDidMount() {
// 这里我们通过监听的方式来监听哈希的变化,并且来更新state促进视图更新
window.addEventListener('hashchange', () => {
console.log(window.location.hash.substr(1))
this.setState({
route: window.location.hash.substr(1)
})
})
}
render() {
//在这里我们定义一个RouterView 所有的变化后的组件都会丢到这个RouterView中
let RouterView = App
switch (this.state.route) {
case '/children1':
RouterView = Children
break;
case '/children2':
RouterView = ChildrenTwo
break;
default:
RouterView = Home
break;
}
return (
<div>
<h1>App</h1>
<ul>
<li><a href="#/children1">children1</a></li>
{/* 点击更改哈希值 这里触发我们的监听 然后修改state来触发组件的重新传染 */}
<li><a href="#/children2">children2</a></li>
</ul>
<RouterView/>
</div>
)
}
}
// 为了展示效果定义子组件一
class Children extends Component{
constructor(){
super()
this.state={
}
}
render(){
return(
<div>
<h1>我是子组件1</h1>
</div>
)
}
}
// 为了展示效果定义子组件二
class ChildrenTwo extends Component{
constructor(){
super()
this.state={
}
}
render(){
return(
<div>
<h1>我是子组件2</h1>
</div>
)
}
}
// 为了展示效果定义Home组件
class Home extends Component{
constructor(){
super()
}
render(){
return(
<h1>我是Home</h1>
)
}
}
这样我们通过监听哈希变化的方式实现了我们的第一个简单路由,是不是对于路由的原理有了那么一丢丢的认识呢?接下来我们想象一个场景,这么一个路由出现了/children1/user/about/1,看到这里是不是觉得,用switch case已经蒙B了?我们接下来就要使用到React-router了,这个东西可不需要我们自己去写一大串的switch case了,并且性能啊,整洁度啊等等等方面都比我们自己写的好多了!
React-router 初体验
首先我们在React项目文件目录下执行
npm install react-router-dom --save
npm install react-router --save
要想体验一个新的东西,当然是要拿自己开刀 我们改进上面我们写过的组件
// App.js
import React,{Component} from 'react'
// 首先我们需要导入一些组件...
import { HashRouter as Router, Route, Link } from "react-router-dom";
// 这里有BrowserRouter 和 HashRouter 两种模式 我比较推荐HashRouter来学习 BrowserRouter需要后端配合 单独前端设置 刷新会出现404
// 然后我们把App组件整理干净,大概成这个样子
export default class App extends Component {
constructor(){
super()
this.state={
}
}
render() {
return (
<Router>
<div>
<h1>App</h1>
<ul>
<li> <Link to="/">Home</Link></li>
<li><Link to="/children1/">children1</Link></li>
<li><Link to="/children2/">children2</Link></li>
</ul>
<Route exact path="/" component={Home} />
<Route path="/children1" component={Children} />
<Route path="/children2" component={ChildrenTwo} />
</div>
</Router>
)
}
}
// 为了展示效果定义子组件一
class Children extends Component{
constructor(){
super()
this.state={
}
}
render(){
return(
<div>
<h1>我是子组件1</h1>
</div>
)
}
}
// 为了展示效果定义子组件二
class ChildrenTwo extends Component{
constructor(){
super()
this.state={
}
}
render(){
return(
<div>
<h1>我是子组件2</h1>
</div>
)
}
}
// 为了展示效果定义Home组件
class Home extends Component{
constructor(){
super()
}
render(){
return(
<h1>我是Home</h1>
)
}
}
这里我们就完成了React-router4.X的初步体验,怎么样~我给大家的第一次的感觉还可以吧~
基本组件
Routers
在React-router4.0中,Routers有两个表现方式 <BrowserRouter>
和 <HashRouter>
这两个标签都将会给你创建一个专门的history对象,BrowserRouter的表现模式需要搭配一个可以响应请求的服务器。HashRouter是静态文件服务器,我们本地开发的时候,建议使用HashRouter。这里需要注意一下,BrowserRouter和 HashRouter标签下面,只允许存在一个标签 具体写法如下
<BrowserRouter>
<div>
{/*其余内容写在这里面*/}
</div>
</BrowserRouter>
<HashRouter>
<div>
{/*其余内容写在这里面*/}
</div>
</HashRouter>
Route
注意,最外层的是Router 这里是 Route 这是我们的路由匹配组件,它有两个 Route和Switch
Route 组件拥有多个属性,这在我们后期的路由传参中将会用到。这里我们需要简单了解几个属性 path component exact strict
path 是我们匹配的地址,当地址匹配的时候 我们才会去渲染 component内的组件内容
path 后面如果书写模式带了 /: 这样的内容,那么这里将会成为占位符 我们可以利用占位符来取到对应路径的参数 使用 this.props.match.params+占位符的名字 即可拿到
exact 属性来规定我们是否严格匹配
strict 则是严格匹配模式 我们规定的路径使用/one/来书写 如果没有完全匹配 /one/ 则不会展示
<Route exact path="/one" component={App} />
// 这里我们加了exact 那么 路径完全等于/one的时候才会渲染App /one/ one /one/two 后面这三种情况均不会渲染App
// 相反 如果我们没有加 exact 的时候 /one /one/two App都会被渲染
<Route strict path="/one/" component={App} />
// 我们加了 strict 以后 /one 将不会渲染App 而如果没有 strict 这种情况下 /one 是可以渲染App的
同时React-router给我们提供了一个Switch标签 在这个标签内 我们只会渲染一个Route 并且使第一个符合条件的Route 这是什么意思呢?我们来看代码
<Route path="/about" component={About} />
<Route path="/:User" component={User} />
<Route path="/" component={Home} />
<Route component={NoMatch} />
在这样的路由下 我们匹配/about 会发现 所有的路由都可以匹配到并且渲染了出来,这肯定不是我们想要的结果 于是我们给它嵌套一个 Switch
<Switch>
<Route path="/about" component={About} />
<Route path="/:User" component={User} />
<Route path="/" component={Home} />
<Route component={NoMatch} />
</Switch>
这时候我们就只会匹配到第一个/about,仅仅渲染About,大家根据实际需求去决定是否嵌套Switch使用
Link和NavLink
Link 主要api是to,to可以接受string或者一个object,来控制url。我们代码里看看书写方法
<Link to='/one/' />
// 这就是路由到one 搭配Router使用 当然我们可以使用一个对象
<Link to={{
pathname:'/one',
search:'?name=qm',
hash:'#two',
state:{
myName:'qimiao'
}
}} />
// 这样我们点击link不仅可以到one 还可以传递一些参数
NavLink 它可以为当前选中的路由设置类名、样式以及回调函数等。使用如下
<NavLink exact activeClassName='navLink' to='/one/' />
// 这就是路由到one 搭配Router使用 当然我们可以使用一个对象
<NavLink exact activeClassName='navLink' to={{
pathname:'/one',
search:'?name=qm',
hash:'#two',
state:{
myName:'qimiao'
}
}} />
// 这里的exact 同样是严格比配模式 同Route
// 这样我们可以为当前选中的路由设置样式了
子路由的书写
在react-router4之前的版本,子路由通过路由嵌套就可以实现了,但是这一个方法到了React-router4中就行不通了
先来一个之前的写法,当然也是错误示例
export default class App extends Component {
constructor(){
super()
this.state={
}
}
render() {
return (
<Router>
<div>
<h1>App</h1>
<ul>
<li> <Link to="/home">Home</Link></li>
<li><Link to="/children1/">children1</Link></li>
<li><Link to="/children2/">children2</Link></li>
</ul>
<Route path="/home" component={Home} >
<Route path="children1" component={Children} />
<Route path="children2" component={ChildrenTwo} />
{/*在4.0以后的版本这样都会报错 那么我们应该怎么写呢*/}
</Route>
</div>
</Router>
)
}
}
在react-router4.x版本中 我们子路由必须要在子组件内部了
export default class App extends Component {
constructor(){
super()
this.state={
}
}
render() {
return (
<Router>
<div>
<h1>App</h1>
<ul>
<li> <Link to="/home">Home</Link></li>
{/* 同样 这两个link让他们转移到Home中 我们的home组件就变成了下面的样子 */}
</ul>
<Route path="/home" component={Home} >
{/*<Route path="children1" component={Children} />*/}
{/*<Route path="children2" component={ChildrenTwo} />*/}
{/*先把这里注释掉 然后我们来到Home组件内*/}
</Route>
</div>
</Router>
)
}
}
// home内部用{this.props.match.url+子路由路径}来获取当前的路径并且加上我们要路由到的位置来进行路由匹配和路径跳转匹配 这样书写 Children和ChildrenTwo就是home的子路由了
class Home extends Component{
constructor(){
super()
}
render(){
return(
<div>
<h1>我是Home</h1>
<li><Link to={`${this.props.match.url}/children1`}>children1</Link></li>
<li><Link to={`${this.props.match.url}/children2`}>children2</Link></li>
<Route path={`${this.props.match.url}/children1`} component={Children} />
<Route path={`${this.props.match.url}/children2`} component={ChildrenTwo} />
</div>
)
}
}
路由跳转
声明式跳转上面已经说过了 Link和NavLink 两个标签就可以满足了 我们主要说一下js跳转
在4.0刚刚发布的时候 this.props.history.push(‘路径’)这个方法已经行不通了,不过值得庆幸的是,我现在所使用的4.3.1版本又可以使用这个方法来进行js路由跳转了。
我们用home组件来举个例子
class Home extends Component{
constructor(){
super()
}
toPath=(home)=>{
console.log(123)
this.props.history.push({ //我们把要传参的东西都放在push对象内了
pathname:home, //我们要到达哪个路由
search:"?a=1", //明文传参的模式
query:{'type':'type'}, //query对象传参
state:{ //state对象传参
b:456
}
})
// this.props.history.push('/123')
}
render(){
return(
<div>
<h1 onClick={()=>{this.toPath(`${this.props.match.url}/children1/123`)}}>我是Home</h1>
<li><Link to={`${this.props.match.url}/children1`}>children1</Link></li>
<li><Link to={`${this.props.match.url}/children2`}>children2</Link></li>
<Route path={`${this.props.match.url}/children1/:id`} component={Children} />
<Route path={`${this.props.match.url}/children2`} component={ChildrenTwo} />
</div>
)
}
}
/*相应的 我们在Children 组件里面有对应的方法来获取参数。*/
class Children extends Component{
constructor(){
super()
this.state={
}
}
render(){
console.log(this.props.match.params.id) //这种是通过路径上面的:id传过来的参数
console.log(this.props.location.query) //这是通过push的参数对象中的query传过来的 和vue的query有区别 它不在地址栏 刷新丢失
console.log(this.props.location.state) //这是通过push的参数对象中的state传过来的 它不在地址栏 刷新丢失
console.log(this.props.location.search) //暴露在地址栏,需要自行处理获取数据
return(
<div>
<h1>我是子组件1 </h1>
</div>
)
}
}
总结
这一期我们主要还是讲了react-router4.x的基础使用和传参方法,react-router4.x坑比较多,不过使用熟练了会让你感觉很爽,大家不要吝啬自己的时间多多学习,博客开通了注册投稿功能,如果大家有好的学习文章,经典总结,可以投稿,可以扫码加我微信申请专栏~感谢大家的阅读和观看。