GX博客

分享个人 Full-Stack JavaScript 项目开发经验

使用React Router作为React应用程序的路由转发解决方案

在单页应用中,由于内容载入和 UI 修改都是通过 JavaScript 完成的,所以它可以避免页面重载,提高用户的使用体验。但如果没有路由转发解决方案,浏览器的历史纪录、书签、前进和后退等功能就无法正常工作,无法找回特定端点的请求内容。对于 React 应用程序, React Router 作为路由转发解决方案被广泛使用。下面介绍React Router v4.3.1的应用例子。


React Router 提供五种高级路由。基于 DOM 创建的路由有:浏览器端使用的 HashRouter 和 BrowserRouter;服务器端渲染可以使用 StaticRouter。

安装 react-router-dom:

yarn add react-router-dom

HashRouter(哈希路由)

哈希路由会在每个路由路径前添加井号(#)。一般来说,地址栏上的 # 用于定义锚点链接,不会向服务器发送请求。哈希路由可以理解为仅在浏览器端作用的路由。React Router 会根据 # 后的路由路径渲染指定的 React 组件。

由于哈希路由会占用了 URL 哈希字符串的一部分,若果在 #/clear 路由下,想使用浏览器锚点跳转到 #jump 的位置,则 id 就需要写成 #/clear#jump。

本博客的后台管理系统使用的就是哈希路由。


BrowserRouter(浏览器路由)

React Router 提供了 RESTful 风格的浏览器路由支持,同时允许使用查询字符串。它可配合服务器端渲染,实现同构性。使用 React Router 提供的链接组件,可以实现跟哈希路由一样的用户体验。当浏览器重新载入该路径并向服务器端发送一个 GET 请求时(如用户点击收藏的书签),服务器端可以获取相应的 URL 参数,并基于请求地址渲染路由组件 HTML,最后返回给客户端。由于组件预先在服务器端渲染,所以可以支持 SEO。


StaticRouter(静态路由)

静态路由不会去改变 location,它用于服务器端渲染,根据请求地址或重写过的请求地址渲染路由组件。

本博客的前台使用的就是浏览器路由/静态路由的同构性实现。


HashRouter/BrowserRouter 组件的使用

但在代码编写上,这两种高级路由组件的用法基本相同。

首先把 HashRouter/BrowserRouter 作为应用程序的根组件渲染,若果项目使用了 react-redux 则需再使用 Provider 组件包裹。

import React from "react";
import {render} from "react-dom";
import {HashRouter} from "react-router-dom";
import {Provider} from "react-redux";
import App from "./App";
import configureStore from "./store";

const store = configureStore();

render(
    <Provider store={store}>
        <HashRouter>
            <App/>
        </HashRouter>
    </Provider>
    , document.getElementById('react-container')
);

Route 组件

可以在 APP 组件的一个用于渲染路由组件的 div 内使用 Route 组件,并定义每个路径匹配规则和匹配时要渲染的组件。其中的 Switch 组件使其只会显示首个匹配的路由,如果没有任何路径匹配,则显示最后一个没有定义 path 的404组件。

import React from "react";
import {Route, Switch} from "react-router-dom";

const App = () => (
    <div id="app">
        <OtherComponent/>
        <div className="router-container">
            <Switch>
                <Route exact path="/" component={WebsiteMessageContainer}/>
                <Route exact path="/clear" component={ClearContainer}/>
                <Route exact path="/write/:article_id?" component={WriteContainer}/>
                <Route exact path="/404" component={Whoop404}/>
                <Route component={Whoop404}/>
            </Switch>
        </div>
    </div>
);

export default App;

Route 组件的属性说明如下:

  • exact

    该属性表示只有精准匹配时才会显示对应组件。如果不添加属性,则 /clear/123 这样的路径也会匹配 path="/clear",并显示 ClearContainer 组件。

  • path

    设置匹配的路由路径。要了解更多的路由路径匹配语法,请点击这里

  • component

    该属性用于传入路由匹配后要渲染的 React 组件,一般为容器组件。


Router 属性

<Route> 组件会将路由参数属性传递给它渲染的组件。

可以注意到上面的路径 /write/:article_id?,它表示该路径有一个可选的参数段 article_id。它可以通过 props.match.params.article_id 在对应的容器组件连接的 UI 组件中获取。React Router 还会传递 props.history 和 props.location 等属性,帮助组件获取更多路由相关信息。下面列出一些常用的路由属性:

  • match.params

    【Object】用于获取路由参数段的参数值。

  • match.isExact

    【Boolean】表示路由路径是否完全匹配。如果 Route 组件使用了 exact 属性,则它始终为 true。

  • match.path

    【String】获取 Route 组件定义的 path 的值。

  • match.url

    【String】获取 path 值所匹配的 url 结果。

  • location.hash

    【String】路由路径的哈希字符串部分。

  • location.pathname

    【String】获取实际的 url。

  • location.search

    【String】获取路径的查询字符串部分。

  • location.state

    【Object】获取保存在当前 location 中的 state。

  • history

    【Object】封装的 history 对象。你可以使用 history.push、history.replace 等方法修改 history 记录,还可以使用 history.listen 监听记录变化。在 history.action 中还给出了路由改变的操作类型,PUSH 为点击路由链接或调用 push 方法,POP 为浏览器的前进后退,REPLACE 为调用 replace 方法。下面是一个 history.push 的使用例子:

    this.props.history.push({
        pathname: '/search.html',
        search: `keyword=${encodeURIComponent(keyword)}&page=${page}`,
        state: {keyword, page}
    });

对于同一组件,当路由参数发生变化时,可以在 componentDidUpdate 生命周期内检查前后 props 的路由属性来更新组件到对应状态。


Link/NavLink 链接组件

我们不需要在地址栏手动输入地址才能实现页面导航,React Router 提供了 Link 和 NavLink 组件,让用户可以通过点击链接直接访问任意内部页面,而无需重载整个网页。

下面是 Link 组件的使用例子:

import {Link} from 'react-router-dom';

export const Example = () =>
    <nav>
        <Link to={{
            pathname: '/clear',
            search: '?query=1&name=2',
            hash: '#the-hash',
            state: { page: 1 }
        }}
        >导航链接</Link>
    </nav>

其中 to 属性接收的对象字段与 props.location 中的对应,也可以简单地传入一个路径字符串:

<Link to="/clear">导航链接</Link>

NavLink 组件与 Link 组件用法基本相同,一般用于需要根据路由路径控制 a 链接样式的情况:

<NavLink
    exact
    to="/clear"
    activeClassName={ ...}
    activeStyle={...}
    isActive={ (match, location) => {} }
>导航链接</NavLink>

其中 exact 属性用于设置当路径精确匹配时,才激活样式。isActive 回调函数用于具体判断激活样式的条件,下面是博客中的一个使用例子:

<NavLink className="blog-nav-item"
     activeClassName="active"
     isActive={(match, location) => {
         return /^\/list_hot_[\d+].html$/.test(location.pathname);
     }}
     to="/list_hot_1.html"
     title="最热文章"
>最热文章</NavLink>

服务器端渲染

为了实现同构性,我们需要对 App 组件作如下修改:

// ...
const App = ({store, url, context}) => (
    (typeof document === 'undefined') ?
        <Provider store={store}>
            <StaticRouter location={url} context={context}>
                <Root/>
            </StaticRouter>
        </Provider>
        :
        <Provider store={store}>
            <BrowserRouter>
                <Root/>
            </BrowserRouter>
        </Provider>
);

export {App};

koa2 的服务器端控制器响应示例如下:

const {renderToString} = require("react-dom/server");
const {buildHtmlPage} = require("../../../views/front/bulidHhtmlPage");
// ......
const context = {};
const bodyHtml = renderToString(App({store, url: ctx.url, context}));
// ......
if (context.url) {
  ctx.redirect(context.url);
} else {
  ctx.body = buildHtmlPage({
    bodyHtml,
    state: JSON.stringify(store.getState()).replace(/</g, '\\u003c')
  });
}

一般传入客户端请求地址给 location 属性,作为路由组件的渲染地址。当请求地址被如反向代理服务器重写过的,应使用重写前的请求路径。context 对象用于接收匹配的 <Redirect> 组件的重定向路由地址。

以上就是 React Router 在 React 应用程序中的基本应用。


要了解浏览器原生 history API,请点击这里

要了解 React Route 的详细说明文档,请点击这里这里

版权声明:

本文为博主原创文章,若需转载,须注明出处,添加原文链接。

https://leeguangxing.cn/blog_post_23.html