从 React Router 谈谈路由

React Router 是专为 React 设计的路由解决方案,在使用 React 来开发 SPA (单页应用)项目时,都会需要路由功能,而 React Router 应该是目前使用率最高的。

React Router 并不是 Facebook 的 React 官方团队开发的,但是据说有官方人员参与开发。React Router 的设计思想来源于 Ember 的路由,如果原来有用过 Ember 的路由,那么应该对 React Router 不会陌生。

什么是路由?

对于没有开发过后端,也没有开发过 SPA 的前端来说,「路由」这个名词可能会让人比较困惑,这里的路由并不是指「硬件路由」,也不是网络七层协议中的「网络层路由」,但是其思想原理是一样的。我尽量简单通俗的介绍一下。

假如我们有一台提供 Web 服务的服务器的网络地址是:10.0.0.1,而该 Web 服务又提供了三个可供用户访问的页面,其页面 URI 分别是:

http://10.0.0.1/

http://10.0.0.1/about

http://10.0.0.1/concat

那么其路径就分别是 /,/about,/concat。

当用户使用 http://10.0.0.1/about 来访问该页面时,Web 服务会接收到这个请求,然后会解析 URI 中的路径 /about,在 Web 服务的程序中,该路径对应着相应的处理逻辑,程序会把请求交给路径所对应的处理逻辑,这样就完成了一次「路由分发」,这个分发就是通过「路由」来完成的。

前端路由

前端的路由和后端的路由在实现技术上不一样,但是原理都是一样的。在 HTML5 的 history API 出现之前,前端的路由都是通过 hash 来实现的,hash 能兼容低版本的浏览器。如果我们把上面例子中提到的 3 个页面用 hash 来实现的话,它的 URI 规则中需要带上 #。

http://10.0.0.1/

http://10.0.0.1/#/about

http://10.0.0.1/#/concat

Web 服务并不会解析 hash,也就是说 # 后的内容 Web 服务都会自动忽略,但是 JavaScript 是可以通过 window.location.hash 读取到的,读取到路径加以解析之后就可以响应不同路径的逻辑处理。

history 是 HTML5 才有的新 API,可以用来操作浏览器的 session history (会话历史)。基于 history 来实现的路由可以和最初的例子中提到的路径规则一样。

http://10.0.0.1/

http://10.0.0.1/about

http://10.0.0.1/concat

用户可能都察觉不到该访问地址是 Web 服务实现的路由还是前端实现的路由。

从性能和用户体验的层面来比较的话,后端路由每次访问一个新页面的时候都要向服务器发送请求,然后服务器再响应请求,这个过程肯定会有延迟。而前端路由在访问一个新页面的时候仅仅是变换了一下路径而已,没有了网络延迟,对于用户体验来说会有相当大的提升。

说了这么多的「路由基础」,该回头来说说 React Router 了。

配置路由

使用 React Router 来配置上面例子中的三个页面,每个页面分别对应着一个 React Component。

/about 页面的入口文件 about.js

import Reactfrom'react';

classAboutextendsReact.Component{

render(){

return(

ThisisAboutpage.

);

}

};

exportdefaultAbout;

/concat 页面的入口文件 concat.js

import Reactfrom'react';

constConcat=()=>{

return(

ThisisConcatpage.

);

};

exportdefaultConcat;

/ 首页对应的是 app.js,它也是整个 React Component 的入口文件。

import Reactfrom'react';

import{Link}from'react-router';

classAppextendsReact.Component{

render(){

return(

React Router Demo

bystylechen.com

Home

About

Concat

{this.props.children}

)

}

};

exportdefaultApp;

app.js 中的 Link 组件是 React Router 提供的组件,用于链接到相应页面。如果直接使用 a 标签的话相当于页面跳转了,而使用 Link 只是应用内的路由跳转,页面跳转意味着先重新加载整个页面,然后才是应用内部的路由跳转。

index.js 中包含了 route 的配置,同时在该文件中对 React Component 进行 render。

import Reactfrom'react';

import ReactDOMfrom'react-dom';

import createBrowserHistoryfrom'history/lib/createBrowserHistory';

import Appfrom'./component/app';

import Aboutfrom'./component/about';

import Concatfrom'./component/concat';

import{Router,Route}from'react-router';

consthistory=createBrowserHistory();

constrouter=(

);

ReactDOM.render(

router,

document.getElementById('root')

);

Route 组件就是用于配置路由,path 属性用于配置路径,component 就是对应的 React Component。

Route 组件支持嵌套,嵌套时子组件的路径可以继承父组件的路径,上面的 about 嵌套后就成了 /about,当然也可以直接以根路径为开头。

此时再看看 app.js 中的 this.props.children,About 和 Concat 两个页面组件其实就是以这种形式插入到父组件中,只是插入的时候用 React Router 提供的组件再包装了一下使它们可以支持路由。

Router组件还有一个重要的属性,那就是history,这可以配置使用history来实现路由,如果没有配置这个属性则默认使用hash。

路由参数

假如我们有很多 list 页面,这些页面除了动态内容不同,其他的页面部分都相同,这个时候需要怎么配置路由和组件呢?

这种场景就需要用到路由的参数功能,增加一条包含参数的路由配置。

import Listfrom'./component/list';

注意 path 属性中的 :id 就是该路由的参数(param)。再来看看 List 页面的组件。

/list 对应了 list.js

import Reactfrom'react';

classListextendsReact.Component{

render(){

return(

ThisisListpage.

Thelistpageidis

{this.props.params.id}

);

}

};

exportdefaultList;

在 List 组件中,可以直接通过 this.props.params.id 来访问实际的参数值(这里的id key 就和定义路径的 :id 相对应),React Router 将路由的数据都通过 props 传递给了页面组件,这样就可以非常方便的访问路由相关的数据了。

组件的生命周期

每个 React Component 都有生命周期,按照常规的策略,当调用父组件的 render 的时候,会将所有的页面子组件也进行 render,这种逻辑显然不合理了。那么当一个 React 的应用有了路由功能后,它的生命周期会如何处理呢?

当切换路由的路径的时候才去 render 对应的页面组件,给 About 绑定两个组件装载和卸载的事件,就可以测试出来了。

import Reactfrom'react';

classAboutextendsReact.Component{

componentDidMount(){

console.log('mount');

}

componentWillUnmount(){

console.log('un mount');

}

render(){

return(

ThisisAboutpage.

);

}

};

exportdefaultAbout;

在切换到 /about 页面的时候,会打印出 mount,而当离开页面的时候会打印出 un mount。路由的这种进入时装载组件离开时卸载组件的策略就可以做到合理利用「资源」,不会一下把所有的组件都装载进来使内存占用飙升,也不会离开时没有卸载而时内存泄漏。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom