whinc.github.io icon indicating copy to clipboard operation
whinc.github.io copied to clipboard

react-router 指引

Open whinc opened this issue 5 years ago • 0 comments

简单介绍 react-router 的总体组成,大概需要阅读 5 分钟。

react-router 是基于 react 封装的前端路由库,通过它可以实现 SPA 中的路由切换(不刷新页面的情况下进行页面切换)。react-router 是一组声明式导航组件的集合,意味着你可以像普通 UI 组件一样,在 JSX 中使用路由组件,这非常符合 react 的风格。

安装

react-router 项目包含四个 npm 包,按需选择安装。

  • react-router:是实现路由的核心包,其余的几个包都依赖此包。服务端渲染时,只需要引入此包即可。
  • react-router-dom:react-router 的 react 绑定,适用于浏览器环境。
  • react-router-native:react-router 的 react-native 绑定,适用于基于 react-native 的原生 App 环境。
  • react-router-config:静态路由配置,从 react-router v4 开始没有了集中路由配置,改为动态路由,react-router-config 在动态路由的基础上提供了静态路由配置。

如果是在浏览器上的使用,只需要安装 react-router-dom 包即可。

npm install react-router-dom

一个简单的例子

下面是一个简单路由切换示例,有两个链接,点击链接切换到相应的内容。

function BasicExample() {
  return (
    <BrowserRouter>
      <div>
        <Link to="/home">Home</Link><br/>
        <Link to="/about">About</Link><br/>

        <Route path="/home" component={Home} />
        <Route path="/about" component={About} />
      </div>
    </BrowserRouter>
  );
}

这个示例麻雀虽小,但五脏俱全,包含了编写 react-router 应用所必备的三个组成部分:

  1. 使用<BrowserRouter>组件包裹根元素,监听路由变化
  2. 使用<Route>组件定义路由,在路由匹配时渲染组件
  3. 使用<Link>组件作为跳转链接,点击时切换路由

无论多复杂的路由应用,归根结底都是这么个套路。

react-router 核心组件

react-router 包含三类组件:

  • 路由器组件:如<Router><HashRouter><BrowserRouter>,作用是监听路由变化。
  • 路由组件:如<Route><Switch><Redirect>,作用是匹配路由时渲染组件。
  • 导航组件:如<Link><NavLink>,作用是渲染出链接,并且点击链接时自动切换路由。

以及一些辅助 API 和组件:<Prompt>withRouter

react-router 核心组件

react-router 的使用

参考官网文档和示例

本来是想针对每个组件的用法写写demo的,但是写了几个之后感觉看我写的示例还不如看官网的,我写的 demo 不一定会定期更新,但是官方的文档会保持更新,并且文档详细示例也丰富。

常见问题

(1)<Route> 组件 component/render/children 的区别

下面是<Route>组件的源码,从中可以观察到他们的区别:

class Route extends React.Component {
  render() {
    return (
      // 省略部分代码...
      <RouterContext.Provider value={props}>
        {children && !isEmptyChildren(children)
          ? children
          : props.match
            ? component
              ? React.createElement(component, props)
              : render
                ? render(props)
                : null
            : null}
      </RouterContext.Provider>
    )
  }
}

翻译成伪代码如下:

if (存在 children) {
  渲染 children ,并且忽略 component 和 render 属性
} else {
  if (匹配当前路由) {
    if (component 不为空) {
      返回 React.createElement(component, props) 创建的组件实例
    } else if (render 不为空) {
      返回 render(props) 创建的组件
  } else {
    返回`null`
  }
}

(2)使用<BrowserRouter>时服务端的配置

<BrowserRouter><HashRouter>都可以实现前端路由的功能,前者路由变化部分是 URL 的 pathname 段,后者路由变化部分是 hash 段。

前者:http://127.0.0.1:3000/article/num1 后者:http://127.0.0.1:3000/#/article/num1

这样的区别带来的直接问题就是当处于二级或多级路由状态时,刷新页面,<BrowserRouter>会将当前路由发送到服务器,而<HashRouter>不会。

解决办法:使用<BrowserRouter>时,服务端配合做点修改,当服务端收到请求的 url 不是功能性的,而是前端路由时,返回入口 html 文件内容。这样修改后,虽然页面刷新会发送到服务器,但是服务器返回的页面内容相同,达到的效果和使用<HashRouter>相同。

以 node.js 为例,大致代码如下:

app.use(function(req, res, next) {
  // 如果是功能性 URL,则交给按正常流程处理

  // 如果是前端路由,返回单页应用的入口页内容
  fs.readFile(__dirname + '/public/dist/index.html', function(err, data){
      if(err){
          console.log(err);
          res.send('后台错误');
      } else {
          res.writeHead(200, {
              'Content-type': 'text/html',
              'Connection':'keep-alive'
          });
          res.end(data);
      }
  })
});

参考

whinc avatar Apr 02 '19 02:04 whinc