react-router的使用及实现原理

328 阅读6分钟

路由

如果要在单页应用中完成组件的切换,需要实现下面两个功能:

  1. 根据不同的页面地址,展示不同的组件(核心);
  2. 完成无刷新的地址切换;

我们把实现了以上两个功能的插件,称之为路由

React Router

  1. react-router:路由核心库,包含诸多和路由功能相关的核心代码;
  2. react-router-dom:利用路由核心库,结合实际的页面,实现跟页面路由密切相关的功能;

两种模式

Hash Router 哈希路由

根据url地址中的哈希值来确定显示的组件

原因:hash的变化,不会导致页面刷新 这种模式的兼容性最好

Borswer History Router 浏览器历史记录路由

HTML5出现后,新增了History Api,从此以后,浏览器拥有了改变路径而不刷新页面的方式 History表示浏览器的历史记录,它使用栈的方式存储, history表示当前路由的信息。

  1. history.length:获取栈中数据量
  2. history.pushState:向当前历史记录栈中加入一条新的记录
    1. 参数1:附加的数据,自定义的数据,可以是任何类型
    2. 参数2:页面标题,目前大部分浏览器不支持
    3. 参数3:新的地址
  3. history.replaceState:将当前指针指向的历史记录,替换为某个记录
    1. 参数1:附加的数据,自定义的数据,可以是任何类型
    2. 参数2:页面标题,目前大部分浏览器不支持
    3. 参数3:新的地址

路由组件

Router组件

它本身不做任何展示,仅提供路由模式配置,另外,该组件会产生一个上下文,上下文中会提供一些实用的对象和方法,供其他相关组件使用

  1. HashRouter:该组件,使用hash模式匹配
  2. BrowserRouter:该组件,使用BrowserHistory模式匹配

通常情况下,Router组件只有一个,将该组件包裹整个页面

Route组件

根据不同的地址,展示不同的组件。 Route组件可以写到任意的地方,只要保证它是Router组件的后代元素

重要属性:

  1. path:匹配的路径
    1. 默认情况下,不区分大小写,可以设置sensitive属性为true,来区分大小写
    2. 默认情况下,只匹配初始目录,如果要精确匹配,配置exact属性为true
    3. 如果不写path,则会匹配任意路径
  2. component:匹配成功后要显示的组件
  3. children:
    1. 传递React元素,只有匹配到该,一定会显示children,并且会忽略component属性
    2. 传递一个函数,该函数有多个参数,这些参数来自于上下文,该函数返回react元素,则一定会显示返回的元素,并且忽略component属性
  4. exact:是否精确匹配

Switch组件

写到Switch组件中的Route组件,当匹配到第一个Route后,会立即停止匹配。

由于Switch组件会循环所有子元素,然后让每个子元素去完成匹配,若匹配到,则渲染对应的组件,然后停止循环。因此,不能在Switch的子元素中使用除Route外的其他组件。

widthRouter

Link

生成一个无刷新跳转的a元素。

  • to
    • 字符串:跳转的目标地址
    • 对象:
      • pathname:url路径
      • search
      • hash
      • state:附加的状态信息
  • replace:bool,表示是否是替换当前地址,默认是false
  • innerRef:可以将内部的a元素的ref附着在传递的对象或函数参数上
    • 函数
    • ref对象

NavLink

是一种特殊的Link,Link组件具备的功能,它都有。 它具备的额外功能是:根据当前地址和链接地址,来决定该链接的样式

  • activeClassName: 匹配时使用的类名
  • activeStyle: 匹配时使用的内联样式
  • exact: 是否精确匹配
  • sensitive:匹配时是否区分大小写
  • strict:是否严格匹配最后一个斜杠

Redirect

重定向组件,当加载到该组件时,会自动跳转(无刷新)到另外一个地址。

  • to:跳转的地址
    • 字符串
    • 对象
  • push: 默认为false,表示跳转使用替换的方式,设置为true后,则使用push的方式跳转
  • from:当匹配到from地址规则时才进行跳转
  • exact: 是否精确匹配from
  • sensitive:from匹配时是否区分大小写
  • strict:from是否严格匹配最后一个斜杠

路由信息

Router组件会创建一个上下文,并且,向上下文中注入一些信息

该上下文对开发者是隐藏的,Route组件若匹配到了地址,则会将这些上下文中的信息作为属性传入对应的组件

history

它并不是window.history对象,我们利用该对象无刷新跳转地址

为什么没有直接使用history对象

  1. React-Router中有两种模式:Hash、History,如果直接使用window.history,只能支持一种模式
  2. 当使用windows.history.pushState方法时,没有办法收到任何通知,将导致React无法知晓地址发生了变化,结果导致无法重新渲染组件
  • push:将某个新的地址入栈(历史记录栈)
    • 参数1:新的地址
    • 参数2:可选,附带的状态数据
  • replace:将某个新的地址替换掉当前栈中的地址
  • go: 与window.history一致
  • forward: 与window.history一致
  • back: 与window.history一致

location

与history.location完全一致,是同一个对象,但是,与window.location不同

location对象中记录了当前地址的相关信息

我们通常使用第三方库query-string,用于解析地址栏中的数据

match

该对象中保存了,路由匹配的相关信息

  • isExact:事实上,当前的路径和路由配置的路径是否是精确匹配的
  • params:获取路径规则中对应的数据

实际上,在书写Route组件的path属性时,可以书写一个string pattern(字符串正则)

react-router使用了第三方库:Path-to-RegExp,该库的作用是,将一个字符串正则转换成一个真正的正则表达式。

向某个页面传递数据的方式:

  1. 使用state:在push页面时,加入state
  2. 利用search:把数据填写到地址栏中的?后
  3. 利用hash:把数据填写到hash后
  4. params:把数据填写到路径中

非路由组件获取路由信息

某些组件,并没有直接放到Route中,而是嵌套在其他普通组件中,因此,它的props中没有路由信息,如果这些组件需要获取到路由信息,可以使用下面两种方式:

  1. 将路由信息从父组件一层一层传递到子组件
  2. 使用react-router提供的高阶组件withRouter,包装要使用的组件,该高阶组件会返回一个新组件,新组件将向提供的组件注入路由信息。