目录
react-router介绍
Declarative routing for React
react-router
仓库的简介是: 为React
声明路由
Components are the heart of React's powerful, declarative programming model. React Router is a collection of navigational components that compose declaratively with your application. Whether you want to have bookmarkable URLs for your web app or a composable way to navigate in React Native, React Router works wherever React is rendering--so take your pick!
官方文档中对react-router
的介绍是: 组件是React
强大的声明式编程模型的核心. React Router
是一组以声明方式与你的应用程序组合起来的导航组件集合. 不管你是否想要为你的web
应用添加可书签的url
, 或是在React Native
中添加一个可组合的导航方式, React Router
都能在React
渲染的地方工作, 所以你可以选择!
简而言之, react-router
为React
提供了路由能力, 不管是web
应用或是React Native
应用, 都可以使用react-router
进行路由管理;
这里只对web
应用的路由原理进行分析
react-router路由跳转原理
常见路由模式
SPA
(单页面应用)的路由模式一般分为两种:
hash
模式history
模式
源码分析react-router路由跳转
在引入了react-router
的React
应用中, 我们通常使用react-router-dom
提供的Link
组件进行路由跳转; 在Link
组件中, 路由跳转相关代码如下:
const method = replace ? history.replace : history.push;
method(location);
replace
表示是否替换当前路由, location
表示跳转的路由
可以看出, react-router
实现路由跳转主要使用了history.replace
以及history.push
, 往上层探究后发现, 这里的history
是react-router
开发者实现的一个库, 对window.history
进行封装, 利用window.history.pushState
和window.history.replaceState
两个api
, 实现url
跳转而无须重新加载页面;
模拟react-router路由跳转
react-router
中路由跳转之类的路由操作便是通过history
库完成的, 下面使用create-react-app
(react
^16.13.1)写了一个小栗子?, 简单实现了一下history
路由跳转的原理:
History.ts
interface Listener {
(url: string): void
};
interface History {
listeners: Array<Listener>,
push: {
(url: string, state?: {[propsName: string]: any} | null): void
},
listen: {
(fn: Listener): {(): void}
}
};
const createHistory = (): History => {
const globalHistory = window.history;
const _history: History = {
listeners: [],
listen(fn) {
this.listeners.push(fn);
return () => {
let i: number = -1;
this.listeners.find((listener, index) => {
if (listener === fn) {
i = index;
}
return listener === fn;
});
if (i !== -1) {
this.listeners.splice(i, 1);
}
};
},
push(url, state) {
globalHistory.pushState(state, '', url);
this.listeners.forEach(listener => {
listener(url);
});
}
};
return _history;
};
export default createHistory;
上面是一个简单实现的history
库, 只实现了push
的功能(未实现replace
功能), 主要分为三个部分:
listeners
: 数组类型, 当history.push
调用时, 依次执行listeners
中的函数;listen
: 函数类型, 接受一个函数listener
作参数, 并将listener
加到listeners
中, 等待history.push
执行; 返回一个函数unlisten
, 执行时将当前的listener
从listeners
中移除;push
: 函数类型, 接收一个url
作为参数, 执行globalHistory.pushState
(此处的globalHistory
为window.history
), 并依次执行listeners
中所有函数;
从上面代码可以看出, history
主要运用了订阅-发布设计模式的思想;
App.ts
import React, {useEffect, useState} from 'react';
import createHistory from './history';
const history = createHistory();
const Page1: React.FC = props => {
return <div>Page1</div>;
};
const Page2: React.FC = props => {
return <div>Page2</div>;
};
const App: React.FC = props => {
const [location, setLocation] = useState<string>(window.location.pathname);
const pushHistory = (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>, url: string): void => {
event.preventDefault();
history.push(url);
};
const renderComponent = (): ReactElement => {
switch (location) {
case '/page1': {
return <Page1></Page1>;
}
case '/page2': {
return <Page2></Page2>;
}
default: {
return <Page1></Page1>;
}
}
};
useEffect(() => {
// 页面首次渲染完成后执行
history.listen((url) => {
setLocation(url);
});
}, []);
return (
<div>
<div className="nav">
<a href="/page1" onClick={(event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => pushHistory(event, '/page1')}>page1</a>
<a href="/page2" onClick={(event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => pushHistory(event, '/page2')}>page2</a>
</div>
<div>{renderComponent()}</div>
</div>
);
};
export default App;
上面的代码生成的页面结构分为:
- 导航部分: 对超链接的默认事件进行阻止, 避免刷新页面, 并绑定新的点击事件, 触发
history.push
进行路由跳转; - 路由组件渲染部分: 通过
location
变量渲染对应的路由组件;
代码逻辑结构如下:
- 创建一个
history
示例; - 执行
renderComponent
函数, 渲染出当前路由对应组件; App
首次渲染完成时使用history.listen
注册一个监听事件, 事件调用时使用setLocation
将location
设置为url
参数; 并将history.listen
返回的函数赋值给变量unlisten
;- 点击超链接, 执行
history.push
跳转路由, 执行history.listen
中的回调函数, 执行setLocation
修改location
变量的值, 导致组件重新渲染,renderComponent
函数重新执行, 路由组件成功渲染; - 退出页面时, 执行
unlisten
函数, 销毁当前监听事件;
总结
这篇文章主要是对react-router
中路由跳转原理的分析, 并自行实现了一个简单的history
库, 当然history
库的逻辑更为复杂, 这里并不深究; 如果喜欢请点个赞吧, 下一篇文章将会接着对react-router
的组件进行源码分析, 冲!
如果发现文章有错误可以在评论区里留言哦, 欢迎指正!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。