前端路由原理和React Router

前端路由原理

前端三大框架 Angular、React、Vue ,它们的路由解决方案 angular/router、react-router、vue-router 都是基于前端路由原理进行封装实现的,因此将前端路由原理进行了解和掌握是很有必要的。

路由的概念起源于服务端,在以前前后端不分离的时候,由后端来控制路由。但由于后端路由还存在着自己的不足,前端路由才有了发展空间。路由的映射函数通常是进行一些 DOM 的显示和隐藏操作,当访问不同的路径的时候,会显示不同的页面组件。前端路由主要有两种实现方案:Hash和History。

Hash模式

hash模式就是基于 location.hash 来实现的,原理很简单,location.hash 的值就是 URL 中 # 后面的内容。比如https://www.xxxxxx.com#search的 location.hash 的值为 '#search'

hash 有下面几个特性:

  • URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 部分不会被发送。
  • hash 值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash 的切换。
  • 我们可以使用 hashchange 事件来监听 hash 的变化。

那么如何来触发hash改变(hashchange 事件)呢?

  1. 通过 a 标签,并设置 href 属性,当用户点击这个标签后,URL 就会发生改变,也就会触发 hashchange 事件了:
search
  1. 直接使用 JavaScript来对 loaction.hash 进行赋值,从而改变 URL,触发 hashchange 事件:
location.hash="#search"

History模式

由于hash模式下使用时都需要加上 #,并不是很美观。到了 HTML5,又提供了 History API 来实现 URL 的变化。其中做最主要的 API 有以下两个:history.pushState()history.repalceState()。这两个 API可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录。代码格式如下:

window.history.pushState(null, null, path);
window.history.replaceState(null, null, path);

history 有下面几个特性:

  • pushState 和 repalceState 的标题(title):一般浏览器会忽略,最好传入 null ;
  • 我们可以使用 popstate 事件来监听 url 的变化;
  • history.pushState() 或 history.replaceState() 不会触发 popstate 事件,这时我们需要手动触发页面渲染;

在对前端路由原理有了基本的掌握后,就可以尝试去阅读 vue-router 和 react-router 的源码实现。下面来看下react-router。


React Router

React Router 中的组件主要分为三类:

  • 路由组件,例如 BrowserRouter和 HashRouter
  • 路由匹配器,例如 Route 和 Switch
  • 导航,例如 Link,NavLink 和 Redirect

下面逐一展开介绍:

路由组件

路由组件分为两种:BrowserRouter(对应前端路由history 模式) 和 HashRouter(对应前端路由hash 模式),用法一样,只是 url 展示不同,其中 hash 模式带有 # 号符。

BrowserRouter:http://localhost:3000/page2
HashRouter:http://localhost:3000/#/page2

// BrowserRouter:

    
    

// HashRouter:

    
    

路由匹配器

  1. Route
    Route 是用于声明路由映射到应用程序的组件层。
    路由Route的参数介绍如下:
  • path
    string 类型,用来指定路由跳转路径,根据 path 和 url 匹配到对应的页面;如下所示
// 路由配置

// 页面访问 http://localhost:3000/page1
  • exact
    boolean 类型,用来精确匹配路由,如果为 true 则精确匹配,否则为正常匹配;如下所示
// exact = true 时

// 浏览器通过 http://localhost:3000/page1/one 访问不到 page1 页面

// exact = fasle 时

// 浏览器通过 http://localhost:3000/page1/one 可以访问到 page1 页面
  • sensitive
    boolean 类型,用来设置是否区分路由大小写,如果为 true 则区分大小写,否则不区分;如下所示
// sensitive = true 时

// 浏览器通过 http://localhost:3000/PAGE1 访问不到 page1 页面

// sensitive = fasle 时

// 浏览器通过 http://localhost:3000/PAGE1 可以访问到 page1 页面
  • strict
    boolean 类型,对路径末尾斜杠的匹配。如果为 true,path 为 '/page1/' 将不能匹配 '/page1' 但可以匹配 '/page1/one'。;如下所示
// strict = true 时

// 浏览器通过以下 url 访问不到 page1 页面
http://localhost:3000/page1
// 浏览器通过以下 url 可以访问到 page1 页面
http://localhost:3000/page1/one

// strict = fasle 时

// 浏览器通过以下 url 可以访问到 page1 页面
http://localhost:3000/page1
http://localhost:3000/page1/one
  • component
    设置路由对应渲染的组件,如下所示Page1为要渲染的组件

  • render
    通过render函数返回路由对应渲染的组件或者dom,好处是不仅可以通过 render 方法传递 props 属性,并且可以传递自定义属性。如下所示
 {
    return 
}}>

class Page1 extends React.Component {
  componentDidMount() {
    console.log(this.props) 
    // this.props:
    // history: {length: 9, action: "POP", location: {…}, createHref: ƒ, push: ƒ, …}
    // location: {pathname: "/home", search: "", hash: "", state: undefined, key: "ad7bco"}
    // match: {path: "/home", url: "/home", isExact: true, params: {…}}
    // name: "name1"
  }
}
  1. Switch
    如果路由 Route 外部包裹 Switch 时,路由匹配到对应的组件后,就不会继续渲染其他组件了。但是如果外部不包裹 Switch 时,所有路由组件会先渲染一遍,然后选择所有匹配的路由进行显示。
// 当没有使用 Switch 时

  
  
  

// 当面访问 http://localhost:3000/page1 时,浏览器会同时显示 page1 和 page2 页面的内容 

// 当使用 Switch 时

  
    
    
    
  

// 当面访问 http://localhost:3000/page1 时,浏览器只会显示 page1 页面的内容 

导航

  1. Link
    用来指定路由跳转,Link的参数介绍如下:
  • to
    可以通过字符串执行路由跳转,也可以通过对象指定路由跳转,如下所示
// 通过字符串

  跳转到 page2
  

// 通过对象

  跳转到 page2

通过对象指定路由跳转时,各字段的含义如下:
pathname: 表示跳转的页面路由 path
search: 表示查询参数的字符串形式,即等同于 location 中的 search
hash: 放入网址的 hash,即等同于 location 中的 hash
state: 可以通过这个属性,向新的页面隐式传参,在上面的例子中page2 中可以通过 this.props.location.state 可以拿到 age: 11;

  • replace
    如果设置 replace 为 true 时,表示用新地址替换掉上一次访问的地址;
  1. NavLink
    这是 Link 的特殊版,顾名思义这就是为页面导航准备的,因为导航需要有 “激活状态”。除Link的固定参数外,还有如下可选参数:
  • activeClassName: string,导航选中激活时候应用的样式名
  • activeStyle: object,如果不想使用样式名就直接写 style,
  • exact: bool,若为 true,只有当访问地址严格匹配时激活样式才会应用
  • strict: bool,若为 true,只有当访问地址后缀斜杠严格匹配(有或无)时激活样式才会应用
  • isActive: func,决定导航是否激活,或者在导航激活时候做点别的事情。不管怎样,它不能决定对应页面是否可以渲染。
  1. Redirect
    Redirect用于路由的重定向,它会执行跳转到对应的to路径中,其中参数to的规则同Link一样。

withRouter

withRouter 可以将一个非路由组件包裹为路由组件,使这个非路由组件也能访问到当前路由的 match, location, history对象。

class Component1 extends React.Component {
  handleClick () {
    this.props.history.push('/page2');
  }
  render () {
    return 
this.handleClick()}>this is component1
; } } export default withRouter(Component1);

参数传递

传递参数有三种方式:

  1. 动态路由的方式
    假如/detail的路径对应 一个组件Detail,若将路径中的Route匹配时写成/detail/:id,那么/detail/123,/detail/xyz都可以匹配到该Route并进行显示
// 路由配置

// 路由跳转
this.props.history.push('/detail/1000');
// 参数获取
this.props.match.params;  // {id: "1000"}
  1. search传递参数
    根据参数to的不同书写格式,search传参可以分为两种形式:
详情2
详情2

// 参数获取
this.props.location.state;  // {name:'boge', age:20}
  1. 隐式传参
    所传递的参数不可见
//数据定义
const data = {id:3,name:sam,age:36};
const path = {
    pathname: '/user',
    query: data,
}
this.props.history.push(path);

// 参数获取
this.props.location.query;   // {id:3,name:sam,age:36};

嵌套路由

嵌套路由就是在子页面中再设置一层新的路由导航规则。重点在于不能在父级加 exact(精准匹配),因为先要匹配父级然后才能匹配子集。

// appRouter.js

    
    
    


// video.js

视频教程


为什么我能够看得更远,那是因为我站在巨人的肩上,以上内容来源:
深度剖析:前端路由原理
一文搞定 React 路由

你可能感兴趣的:(前端路由原理和React Router)