路由的概念,起初来源于服务端,就是当浏览器访问一个网站的不一样页面时,服务端可以正确的返回页面的内容。当访问首页时,它能返回首页的内容,访问关于咱们页面时,返回关于咱们的内容。能够看到路由就是一种控制和匹配,从而保证页面内容和页面的地址一一对应的关系。可是每次页面地址发生变化,服务端都会返回一个新的页面,从而致使整个页面从新加载,用户体验很差。因此就兴起了单页应用,全部的内容都在一个页面上进行展现,页面中的变化也是局部变化,不用再刷新整个页面。那局部变化是怎么实现的呢?也就是说,当点击Home时,怎么才能正确的渲染出Home的内容,而不是其它的内容, 当点击about时,怎么才能渲染出About 内容。css
其实这里和服务端路由是一个道理,在服务端路由时,点击home页面时,咱们是向服务端发送一个请求,告诉服务端,咱们须要home页面。在单页应用中,咱们虽然不用向服务器发送请求,但咱们仍是要标识一下咱们的请求,咱们想要home的内容。这也很好理解,若是咱们都不知道须要什么内容,那还怎么展现内容。 怎么标识咱们的请求呢? 这里仍是和服务器路由同样,浏览器的地址栏,就是改变window.location 的值。但这里要注意的是,地址栏的变化,不该该致使向服务器发送请求。这里有两种方法能够实现,一种是hash, location 对象有一个hash属性,hash值变化了,location 也就发生变化了,但hash的变化不会向服务器发生请求,这符合咱们和要求。如在浏览器地址栏中显示#home, 则表示咱们想进入home 的内容。 另外一种是h5 的history API, history 对象一个pushState 方法,它能够改变location 对象的值,而不会向服务器发送请求。history.pushState({url: ‘home’}), 浏览器地址发生变化,但没有像服务器发送请求。html
如今咱们标识了请求,那就要处理请求,就像服务器返回页面同样,咱们也要正确渲染相对应的内容,因此就要作控制和匹配,保证标识的请求和渲染的内容一致。因为这些匹配和控制是在客户端完成的,因此叫作客户端路由。react
首先要作的就是匹配, 当用户标识请求时,返回对应的内容。因此一个路由要知足两个基本的条件。一个匹配用户的标识,一个是匹配成功的内容。对于react-router来讲,它的路由有点不一样,它不是配置型的,而是使用组件,对于路由所要知足的两个基本条件,它就是给组件定义两个属性。它提供的路由组件是Route, 对于匹配它是path属性,对于内容,则是component 属性,定义home 的路由,就能够这么写。正则表达式
<Route path=’/home’, component= {home} /> // 固然,咱们这里要写好Home 组件。
定义成功后,用户若是访问home, 则展现home 组件的内容。其实, 咱们只是写好了一条路由,当用户访问About呢?那确定渲染About的内容,那就要再写一条匹配路由npm
<Route path=’/about’, component= {About} />
若是页面中还要有其它内容访问,那么咱们就要依次写好上面的路由。咱们这样一条条的路由是定义好了,还须要进行控制和管理。当有户进行访问的时候,它要去查找匹配的路由,React-Router 提供了一个hashRouter, 和browserRouter组件,咱们只要把这一条条的路由放到它下面的,它就会自动管理了。hashRouter 是根据hash 值的变化进行管理,browserRouter 则是根据h5 的历史管理api 进行管理。编程
理论知识讲的差很少了,咱们实战体验一下react-router 吧。 直接使用create-react-app 建立项目,cnpm install react-router --save 就能够了。 项目就是一个简单的企业网站,它有5个部分:首页,关于咱们,企业事件,联系咱们,公司产品。 每个部分占据一个页面区域,看起来是一个多页应用,其实它是一个单页应用。 对应这几个部分, 咱们分别写几个组件: home, about, events, contact, products. 组件内部随便返回点内容,表示不一样的页面。新建一个pages.js文件,内容以下:api
import React from 'react' // 首页内容 export const Home = () => ( <section className="home"> <h1>企业网站</h1> <p>首页内容</p> </section> ) // 企业事件内容 export const Events = () => ( <section className="events"> <h1>企业大事件</h1> </section> ) // 公司产品 export const Products = () => ( <section className="products"> <h1>公司产品:手机、电脑</h1> </section> ) // 联系咱们 export const Contact = () => ( <section className="contact"> <h1>联系咱们</h1> <p>公司电话:0755 - 12345678</p> </section> ) // 关于咱们 export const About = () => ( <section className="about"> <h1>公司理念</h1> <p>公司以人为本</p> </section> )
组件写完了,咱们就要定义路由,好让用户访问时返回正确的内容。路由的定义用到了Route 组件,它有两个属性:path和compoent. path, 路径的意思,表示用户怎么访问,component 就是很简单了,表示渲染的组件。由于有5个部分,因此要定义5条路由浏览器
<Route path='/' component={Home}/> <Route path='/about' component={About}/> <Route path='/contact' component={Contact}/> <Route path='/products' component={Products}/> <Route path='/events' component={Events}/>
路由是须要管理的,因此咱们要把上面定义好的路由放到Router下面,react-router 提供了hashRouter 和brwoserRouter 组件,咱们在这里使用hashRouter。不过要注意的是,不管是hashRouter 仍是brwoserRouter,它都只接受一个元素,因此咱们还要使用一个div把上面的5个路由包括起来放到hashRouter下面。服务器
<Router> <div> <Route path='/' component={Home}/> <Route path='/about' component={About}/> <Route path='/contact' component={Contact}/> <Route path='/products' component={Products}/> <Route path='/events' component={Events}/> </div> </Router>
咱们把整个路由配置放到什么地方呢?放到根组件下,在这里,就是放到app 组件下面, 这样,Router 就能够管理 咱们整个应用的路由了。整 个app.js 文件以下:react-router
import React from 'react' // 引入hashRouter Route 组件 import { HashRouter, Route } from 'react-router-dom' // 引入5个组件 import { About, Contact, Home, Products, Events } from './pages'; function App() { return ( <HashRouter> <div> <Route path='/' component={Home}/> <Route path='/about' component={About}/> <Route path='/contact' component={Contact}/> <Route path='/products' component={Products}/> <Route path='/events' component={Events}/> </div> </HashRouter> ) } export default App
如今配置完成了, npm start 启动服务看一看效果, 看到首页内容了。
那咱们怎么访问其它页面的内容呢?在介绍理论知识内容的时候说了,那就要标识请求,标识请求的地方在于浏览器的地址栏中。这时咱们在地址栏的后面输入about, 显示About组件的内容,输入events, 显示event 组件的内容。但这时你会发现一个问题,不管在哪一个路径下,它都用home组件的内容,这是为何呢?原来是path匹配的问题。
地址栏中的地址和path属性值的匹配是使用的正则表达式进行匹配,因此/about 和 / 都会匹配到 /, 只要匹配成功,对应的组件就会渲染出来,因此总会有home 组件进行展现, 这确定不是咱们想要的,必需要一一对应。这时要用到一个属性exact, 只要把它加到<Route path=’/’ component = ‘home’> 上,就表示严格匹配,这时/about 就不会匹配到/, 只有/ 才能成功匹配/, 因此当/about页面时,就不会显示home 页面。home 组件路由改为以下就能够了。
<Route path='/' exact component={Home}/>
这里还会遇到一个问题, 若是用户随便输入一个路径,咱们没有设置对应的路由进行匹配,怎么办?如输入hello, 页面一片空白, 用户体验确定很差。就和服务器会返回一个404页面同样,咱们确定要写一个组件,告诉用户没有匹配成功。那就在pages.js 下面再写一个组件,
export const NotFound404 = () =>(
<div className="whoops-404">
<h1>没有页面能够匹配</h1>
</div>
)
如今就要把这个组件用路由进行管理,再写一条路由, 但path 属性要怎么写呢?由于用户任意输入,咱们是不可能提早知道的,那就直接不写路径了, 不写路径,就表示这个组件能够匹配任意路径,这又会遇到另一个问题,咱们只想在没有匹配的时候,显示该组件, React-Router 提供了switch 组件来解次这个问题。咱们把每一条路由用switch 组件包起来。switch 组件的做用,就像js 中switch 条件判断功能同样,它从上到下依次匹配,若是成功就会渲染组件,若是不成功,接着继续向下找,直到找到一个匹配的为止,因此没有匹配成功的路径都会到 NotFound404 组件,每一条有path属性的Route至关于一个case, 没有path的Route 则至关提供了一个default。
<HashRouter> <div> <Switch> <Route path='/' exact component={Home}/> <Route path='/about' component={About}/> <Route path='/contact' component={Contact}/> <Route path='/products' component={Products}/> <Route path='/events' component={Events}/> <Route component={NotFound404}/> </Switch> </div> </HashRouter>
如今路由功能正常了,可是是经过改变地址栏来进行导航的,对于验证功能是没有什么问题的, 用户使用,问题就大了,没有人愿意手动输入地址来进行页面导航。有没有更好的办法来解决这个问题,React-Router 提供了link, navLink 组件, 它们功能是同样的,都是用于导航,只是使用的场景不同。navLink 组件是Link 组件的一种特殊化、若是是使用文字,按钮进行导航的话,就须要高亮显示当前咱们在哪一个位置以进行区分,navLink 提供了一个activeClass 或activeStyle 属性,能够直接设置高亮样式。但 若是点击图片进行导航,那就不须要对图片进行高亮,那就用link.
如今咱们在home 组件中增长四个link用于导航,它有一个to属性,表示到什么地方去,最基本的用法就是提供一个字符串路径,这个路径和Route中的path属性一一对应就能够了。如<Link to=’/about’>关于咱们</Link>,就表示要到/about 下面。
import React from 'react' // 引入link 组件 import { Link } from "react-router-dom"; import './pages.css'; // 首页内容 export const Home = () => ( <section className="home"> <h1>企业网站</h1> <nav> {/* 添加了四个导航组件Link */} <Link to='/about'>关于咱们</Link> <Link to='/events'>企业事件</Link> <Link to='/products'>公司产品</Link> <Link to='/contact'>联系咱们</Link> </nav> </section> )
这时,咱们点击不一样的link 就能够进行页面导航,这时你会发现点击link, 它仍是改变地址栏,和咱们手动改变地址栏是如出一辙的。 其实link的本质是a 标签,to属性变成了它的href属性, 打开控制台,查看element 元素就能够看到。
这时,咱们想要给link 添加样式,实际上就是给a 标签添加样式。nav a 就能够了。pages.css 内容以下:
html, body, #root { height: 100%; } h1 { font-size: 3em; color: slategray; } /* home 组件 */ .home { height: 100%; display: flex; flex-direction: column; align-items: center; } .home > nav { display: flex; justify-content: space-around; padding: 1em; width: calc(100% - 2em); border-top: dashed 0.5em ghostwhite; border-bottom: dashed 0.5em ghostwhite; background-color: slategray; } .home > nav a { font-size: 2em; color: ghostwhite; flex-basis: 200px; } /* 其它组件 */ section.events, section.products, section.contact { flex-grow: 1; margin: 1em; display: flex; justify-content: center; align-items: center; } /* 404页面 */ .whoops-404 { position: fixed; top: 0; left: 0; z-index: 99; display: flex; width: 100%; height: 100%; margin: 0; justify-content: center; align-items: center; background-color: darkred; color: ghostwhite; font-size: 1.5em; }
这就是react-router 最基本的用法, Link 用于导航或跳转,Route 用于定义一条一条的路由,Router 则管理路由,进行匹配。咱们每次都是定义一条一条的路由,其实就是告诉Router, 当浏览器的地址栏中的地址发生变化时,哪一个组件会被渲染出来。每一条路由都有两个path, component 两个属性,当浏览器地址栏中的地址,可以匹配path时,相应的component 就会显示出来。当地址是/ 时,router 会去渲染相应的home 组件。当地址是/about 时,router 会去渲染相应的Products组件。
其实在Router渲染相应组件的时候,它会向组件props属性中添加3个属性history, location, match. history和window.history对象同样,也能够用于导航,它有push, go等方法,这能够用于编程式导航. location则是浏览器地址栏对象,能够获取到当前路径什么的, match,则用表示匹配,能够获取到匹配的参数等。咱们在404NotFound组件中把这三个属性打印出来。
export const NotFound404 = (props) =>(
console.log(props.location),
console.log(props.match),
console.log(props.history),
<div className="whoops-404">
<h1>没有页面能够匹配</h1>
</div>
)