在React中使用React Router来构建单页应用
一、为什么需要路由
在我们使用React来构建单页App的时候,最大的区别是,导航一个页面应用程序并不涉及到一个全新的页面。而是你的整个应用是在同一个页面进行操作的。
当你加载网页内容的时候,将会变得有一点困难,困难的部分不是加载内容本身,这相对来说比较容易,而是确保单页应用的行为与用户习惯性的操作行为保存一致,更显著的是,当用户导航使用你的App时候,有以下几点问题:
- 在地址栏显示的URL总能够反映出视图展示的东西
- 能够成功地使用浏览器的返回和前进按钮
- 能够直接的使用相关联的URL导航到某个特别的视图
对于多页应用,这三点是不用去考虑的,也没有额外的你不得不去为多页应用考虑的。而对于单页应用,因为你不能导航到一个完成新的页面,你不得不真正的去处理这三个你的用户指出的问题。你需要确保导航进入你的App的URL是完全正确的。你需要确保浏览器的历史记录每个导航都是同步的来准许用户使用后退或前进按钮。如果用户标记(收藏)了某个特殊的视图或复制粘贴了一URL以便之后可以访问,你需要确保的是你的单页应用能够将用户引导正确的位置。
为了处理以上的问题,你需要有一门通常叫做路由的技术。
二、开始
二.一 构建App
create-react-app react_spa cd react_spa npm i react-router-dom --save
二、二 整理项目结构
将原来项目中public与src文件夹下的所有文件(夹)删除,然后在public文件夹下创建一个index.html将服务于我们App的开始入口。
<!DOCTYPE html> <html lang="en"> <head> <Meta charset="utf-8"> <Meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"> <Meta name="theme-color" content="#000000"> <title>React Router Example</title> </head> <body> <div id="root"></div> </body> </html>
之后在src文件夹下创建一个index.js文件作为我们的入口文件,并添加下面内容:
import React from "react"; import ReactDOM from "react-dom"; import Main from "./Main"; ReactDOM.render( <Main/>,document.getElementById("root") );
接下来在src文件夹下面创建一个Main.js文件作为路由的配置文件:
import React,{ Component } from "react"; class Main extends Component { render() { return ( <div> <h1>Simple SPA</h1> <ul className="header"> <li><a href="/">Home</a></li> <li><a href="/stuff">Stuff</a></li> <li><a href="/contact">Contact</a></li> </ul> <div className="content"> </div> </div> ); } } export default Main;
Home.js
import React,{ Component } from "react"; class Home extends React.Component { render() { return ( <div> <h2>HELLO</h2> <p>Cras facilisis urna ornare ex volutpat,et convallis erat elementum. Ut aliquam,ipsum vitae gravida suscipit,metus dui bibendum est,eget rhoncus nibh metus nec massa. Maecenas hendrerit laoreet augue nec molestie. Cum sociis natoque penatibus et magnis dis parturient montes,nascetur ridiculus mus.</p> <p>Duis a turpis sed lacus dapibus elementum sed eu lectus.</p> </div> ); } } export default Home
Stuff.js
import React,{ Component } from "react"; class Stuff extends React.Component { render() { return ( <div> <h2>STUFF</h2> <p>Mauris sem velit,vehicula eget sodales vitae,rhoncus eget sapien:</p> <ol> <li>Nulla pulvinar diam</li> <li>Facilisis bibendum</li> <li>Vestibulum vulputate</li> <li>Eget erat</li> <li>Id porttitor</li> </ol> </div> ); } } export default Stuff
Contact.js
import React,{ Component } from "react"; class Contact extends React.Component { render() { return ( <div> <h2>GOT QUESTIONS?</h2> <p>The easiest thing to do is post on our <a href="https://github.com/huangche007">GitHub</a>. </p> </div> ); } } export default Contact
三、使用路由
在Main组件中已经有了这个App的基本框架,也有了Home、Stuff、Contact三个内容组件了,现在需要做的就是将它们连接起来构建成我们的App,这时候就需要使用到了React 路由了,回到Main.js,确保导入的语句如下:
import React,{ Component } from "react"; import { Route,NavLink,HashRouter } from "react-router-dom"; import Home from "./Home"; import Stuff from "./Stuff"; import Contact from "./Contact";
React路由工作方式被定义成一种称为路由区域,在路由区域里,有两件事可做:
导航的连接:在Main.js组件的render方法中增加<HashRouter>的代码:
class Main extends Component { render() { return ( <HashRouter> <div> <h1>Simple SPA</h1> <ul className="header"> <li><a href="/">Home</a></li> <li><a href="/stuff">Stuff</a></li> <li><a href="/contact">Contact</a></li> </ul> <div className="content"> </div> </div> </HashRouter> ); } }
HashRouter组件为导航和由路由组成的浏览器历史记录操作提供了基础,接下来将定义导航的连接,使用特殊的NavLink组件替换掉a标签,并为这样的组件添加to属性.
class Main extends Component { render() { return ( <HashRouter> <div> <h1>Simple SPA</h1> <ul className="header"> <li><NavLink to="/">Home</NavLink></li> <li><NavLink to="/stuff">Stuff</NavLink></li> <li><NavLink to="/contact">Contact</NavLink></li> </ul> <div className="content"> </div> </div> </HashRouter> ); } }
注意每个连接,将让路由得知导航到的URL,URL的值(通过to属性定义)作为一个标识来确保正确的内容得到加载,我们匹配内容的URL的方式在Route(路由)组件中得到使用
class Main extends Component { render() { return ( <HashRouter> <div> <h1>Simple SPA</h1> <ul className="header"> <li><NavLink to="/">Home</NavLink></li> <li><NavLink to="/stuff">Stuff</NavLink></li> <li><NavLink to="/contact">Contact</NavLink></li> </ul> <div className="content"> <Route path="/" component={Home}/> <Route path="/stuff" component={Stuff}/> <Route path="/contact" component={Contact}/> </div> </div> </HashRouter> ); } }
正如你所见到的,Route组件包含一个path属性,指定的path值决定了路由何时被激活。当路由激活的时候,通过组件prop指定的组件将被渲染。比如:当点击Stuff链接的时候,path值为/stuff的路由变成了激活状态,也就意味着Stuff组件的内容得到了渲染。
接下来可以npm start 来看看效果了。
四、修复路由
当运行起来之后,会发现,无论我们切换的Stuff,Contact,Home组件的内容一直存在,这意味着无论导航到哪个路由,Home组件总是能够匹配上,这是不对的。可以在Home路由组件中,增加exact属性来解决。
<div className="content"> <Route exact path="/" component={Home}/> <Route path="/stuff" component={Stuff}/> <Route path="/contact" component={Contact}/> </div>
exact属性能够确保只有路由路径正确的匹配,那么这个路由组件才能将加载