📄目录结构
- 服务端路由
- 客户端路由
- MPA和SPA
- React Router是什么
- Router组件
- BrowserRouter
- HashRouter
- Route
- Switch
- 路由跳转
- 路由Hook
- 集成React Router
🔖Demo
| 标题 | Demo |
|---|---|
Router常用组件BrowserRouter和HashRouter | Demo |
| 集成React Router | Demo |
像函数组件和类组件那样,路由也有两个分类。一个是服务端路由,另一个是客户端路由。
服务端路由
请求发送到服务端,服务端返回对应的页面内容,以字节跳动的官网为例:www.bytedance.com
它的请求返回结果是一个完整的HTML文档,这其实就是一个服务端路由的例子。我们在浏览器发送一个请求,然后服务器接收到这个请求并返回对应的页面的内容。
客户端路由
请求不发送到服务器,由客户端代码更新页面内容,以掘金的官网为例:juejin.cn/
我们看到最开始进入页面,依然发送的是服务端的请求,然后返回完整的页面。
但是我们点击“资讯”,它并没有新的请求发送到服务端
但是我们点击字节官网的“我们的产品”时,就会发送新的请求/products到服务器端。
在浏览器里我们观察发现
如果这个小图标变成X再变成圆,表示这个请求是从服务端处理过,然后返回一个完整的页面过程。
如果是客户端路由,这个图标它并不会去显示刷新。
由此我们引入MPA(多页面应用)和SPA(单页面应用)的概念。
MPA和SPA
左图是服务端路由的场景,MPA-多页面应用(Multi Page Application)。我们请求一个应用地址,比如说应用的根路径/,服务端会返回这个HTML页面。然后当我们去跳转另外一个路径,比如说home路径/home,也会经过服务端处理,服务端又会返回另外一个页面。
这就是传统的多页面应用方式,每一个页面都会对应一个HTML,每个路由发生变化的时候都会经过服务端的处理,然后服务端返回对应的HTML页面。
客户端路由的话,我们首次去请求应用的根路径/,服务端也是会处理然后返回一个HTML,在之后我们跳转到其它路径的时候,并不会发送到服务端,而是它自己就处理掉。那整个过程中,只会有一个HTML,那这就是所说的SPA-单页面应用(Single Page Application)。
之后你还可能遇到SSR(Server-Side Rendering)和CSR(Client-Side Rendering),需要注意区分。
React Router是什么
在我们日常开发中,SPA是最常用的一种前端应用的组织方式。
React Router其实就提供了SPA下,我们客户端路由应该怎么去管理的几个方案。
Router组件
常用的如下:
BrowserRouter:根据URL中的path做路由跳转HashRouter:根据URL中的hash部分做路由跳转
BrowserRouter
当我们点击导航栏的时候,path这部分会发生变化。
完整代码请查看Demo
HashRouter
如果是HashRouter呢,这时候我们点击的时候,变化的是URL#号后面的部分,#后面的部分其实就是URL的hash部分
这时候路由是根据Hash部分的变化,然后去渲染相关页面的内容的。
这其实就是BrowserRouter和HashRouter的区别。
在日常使用当中,我们一般会去使用BrowserRouter,而HashRouter存在更多是为了兼容一些老版本的浏览器,因为BrowserRouter它内部的实现应用了History.push这些API。老版本的浏览器可能不支持这些。所以React提供了一个HashRouter作为一个降级方案。
Route
当url和Route中定义的path匹配时,渲染对应的组件,重要props:path、exact。
如果在home页不加exact的话,那么不管点击哪个,url是对应的标签,内容都是home的内容,也就是都会被home匹配。
要避免这个问题我们就用exact。添加exact属性后,只有严格相等,里面的组件才会被渲染。
Switch
就是上面示例中和Route组合使用的Switch。
当找到Switch组件内的第一个路由规则匹配的Route组件后,立即停止后续的查找。
路由跳转
声明式的组件方式:Link
命令式的API调用方式:history.push
它的底层会渲染出a标签。
路由Hooks
我们再来看一下如何通过命令式的API来做路由跳转。
useHistory:获取history对象useParams:获取路由中的参数
首先,在Switch里定义一个新的Route
<Route path="/user/:username">
<User />
</Route>
接着,在Home组件中使用useHistory
function Home() {
const history = useHistory();
return (
<div>
<h2>Home</h2>
<button onClick={() => {
history.push("/user/jason");
}}>
Go User{" "}
</button>
</div>
);
}
我们通过history.push("/user/jason")这样一个路径,这个jason是会作为username这个参数的值传入到Switch里的Route path="/user/:username"里的User这个组件内。
在User组件中使用useParams
function User() {
const params = useParams();
const username = params.username;
return <div>Welcome: {username}</div>;
}
在User这个组件内,我们通过useParams这个API获取到路由路径当中的参数,再通过params.username获取到传入的这个参数的值,然后将参数值在这里渲染出来。
这个其实就是useParms这个API的作用。
集成React Router
在实战案例中,集成ReactRouter
在这篇文章中,我们主要关注实战里的目录结构
目录里的features表明的是功能模块,这个功能模块可以是一个页面级的(一个路由页面代表一个模块),也可以是一个复杂的前端页面,页面当中的某一个模块。比如说,一个通知模块。总之,一个features里放的是不同页面拆分出来的小features。
每个小features下它会去包含开发这个features所需要的所有的业务逻辑或代码。如果各个features之间有一些通用的代码需要复用的话我们该怎么办呢,会把它往上去提,提到和features这个文件夹同级的文件中。
比如说各个组件当中都会去用到一个Navbar顶部的导航栏这样的组件。我们把Navbar这个组件提到最外层的components文件夹下。
它是一个在各个features当中去进行复用的一个组件,如果是一个features内部所专属的代码,那我们只需要把它放到features所代表的这个文件夹下就可以,那这其实是我们开发者应用的一种常用的目录结构、组织方式。