React 前端路由解析

250 阅读4分钟

系统的整理下肯定会有新的收获

前端路由是什么

路由这个概念最早出现在后端,后端用户请求URL导航到html页面。而前端路由不用服务器解析,可以通过hash函数和history API来实现,本质上是匹配前端组件的一种规则,也就是说整个过程只有一个页面,也就是我们说的单页面应用。

优点

  • 不涉及页面跳转,过程服务器没有参与,减少服务器压力
  • 前端组件之间切换,跳转流畅,用户体验好
  • 页面效果酷炫(可以加页面转场动画)
  • 组件化开发

缺点

  • 首屏加载(FCP)慢(整个项目都在一起能不慢嘛)
  • 不利于seo(spa是通过js加载来创建dom元素,爬虫只是抓去静态资源,不会执行js文件)
  • 页面的复杂度会提高(修改单个页面都要发布整个项目)

对应的解决方案 SPA(单页应用)首屏加载速度慢怎么解决?零成本实现SPA(单页网页应用)seo优化

哈希路由和历史路由

哈希路由(hash)模式

这里的hash就是url尾巴后的#号和后面的字符,本身可以作为页面的定位,使用对应id显示在可是区域内,hash变化不会的导致浏览器发请求,hash 会触发 hashchange事件,前进后退也能进行控制。

widow.addEventListener('hashChange', function() {})

history 模式

history在hash之后出现,相比hash穿参只能基于url,如果需要复杂数据,会有体积的限制,history不仅可以在url放参数,还可以放在一个特定的对象中。

如果不想要很丑的 hash,我们可以用路由的 history 模式

History API

以掘金为例 image.png

React-router基础使用

React Router 使用教程

create-react-app

react-router 简介 react包含三个库,react-router,react-router-dom,react-router提供最基础的路由功能,在使用时不直接使用react-router,根据运行的环境使用安装react-router-dom(浏览器中使用),react-router-native(rn中使用)。他们都依赖react-router,会自动安装,创建web应用。

image.png

BrowserRouter和HashRouter对比

  • HashRouter最简单,不需要服务器段渲染,靠浏览器的#来区分path,BrowserRouter需要服务器对不同的url返回HTML,需要服务器配置
  • BrowserRouter使用HTML5的history API(pushState,replaceState,popState事件),让页面UI同步给URL
  • Hash history不需要服务器配置就可以运行,最简单,如果你介意的项目有个丑的url,那么你应该使用browserHistory

常见的BrowserRouter配置:

  • Nginx配置
// 这样所有的路径都会指向index.html 浏览器上的path,会自动被React-router处理,进行无刷新跳转。
server {
    server_name react.thinktxt.com;
    listen 80;
    root /Users/txBoy/WEB-Project/React-Demo/dist;
    index index.html;
    location / {
        try_files $uri /index.html;
      }
}
  • webpack-dev-server运行方式
// 直接在运行时加入参数“–history-api-fallback”
"scripts": {
    "build": "webpack",
    "dev": "webpack-dev-server  --inline --devtool eval --progress --colors --hot --content-base ./build --history-api-fallback"
},

route渲染的三种方式

Route 渲染的优先级:children > component > render,三者都能接受route props,包括match,locaction 和 history,当不匹配时,children的match为null。

三种互斥只能选择一种

component

憨憨,给我啥我渲染啥

render

render的类型是function,Route会渲染这个function的返回值。因此它的作用就是附加一些额外的逻辑

<Route path="/home" render={() => {
   console.log('额外的逻辑');
   return (<div>Home</div>);
 }/>

children

这是最特殊的渲染方式。一、它同render类似,是一个function。不同的地方在于它会被传入一个match参数来告诉你这个Route的path和location匹配上没有。二、第二个特殊的地方在于,即使path没有匹配上,我们也可以将它渲染出来。秘诀就在于前面一点提到的match参数。我们可以根据这个参数来决定在匹配的时候渲染什么,不匹配的时候又渲染什么。

// 在匹配时,容器的calss是light,<Home />会被渲染
// 在不匹配时,容器的calss是dark,<About />会被渲染
<Route path='/home' children={({ match }) => (
    <div className={match ? 'light' : 'dark'}>
    {match ? <Home/>:<About>}
    </div>
 )}/>

Route核心渲染代码

<RouterContext.Provider value={props}>
  {props.match
    ? children
      ? typeof children === "function"
        ? __DEV__
          ? evalChildrenDev(children, props, this.props.path)
          : children(props)
        : children // 这里能看到优先级是children>component>render
      : component
      ? React.createElement(component, props)
      : render
      ? render(props)
      : null
    : typeof children === "function"
    ? __DEV__
      ? evalChildrenDev(children, props, this.props.path)
      : children(props)
    : null}
</RouterContext.Provider>

注意

如果使用component渲染时,Route指定的组件会使用React.createElement创建一个新的[React element],这意味着如果使用内联函数时,每次render都会创建一个新的组件。导致不再更新现有的组件,而是会重新挂载一个新的组件,这样会造成性能问题,所以如果使用内联函数,请使用render或者children。

404页面

作为路由匹配不上的展示页面

// switch是按照顺序来匹配
<Switch>
  <Route path="/" component={homePage}></Route>
  <Route path="/user" component={userPage}></Route>
  <Route component={_404Page}></Route>
</Switch>

动态路由

使用::id的形式 获取:props.match.params 里面的参数

嵌套路由

Route组件嵌套在其他页面,比如列表跳转详情页面

React router API

image.png

参考链接

React Router 使用教程 - 阮一峰的网络日志

react-router 源码

最新 React Router 全面整理