- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章ReactRouter的实现方法由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
ReactRouter是React的核心组件,主要是作为React的路由管理器,保持UI与URL同步,其拥有简单的API与强大的功能例如代码缓冲加载、动态路由匹配、以及建立正确的位置过渡处理等.
React Router是建立在history对象之上的,简而言之一个history对象知道如何去监听浏览器地址栏的变化,并解析这个URL转化为location对象,然后router使用它匹配到路由,最后正确地渲染对应的组件,常用的history有三种形式: Browser History、Hash History、Memory History.
Browser History是使用React Router的应用推荐的history,其使用浏览器中的History对象的pushState、replaceState等API以及popstate事件等来处理URL,其能够创建一个像https://www.example.com/path这样真实的URL,同样在页面跳转时无须重新加载页面,当然也不会对于服务端进行请求,当然对于history模式仍然是需要后端的配置支持,用以支持非首页的请求以及刷新时后端返回的资源,由于应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问URL时就会返回404,所以需要在服务端增加一个覆盖所有情况的候选资源,如果URL匹配不到任何静态资源时,则应该返回同一个index.html应用依赖页面,例如在Nginx下的配置.
1
2
3
|
location / {
try_files $uri $uri/ /index.html;
}
|
Hash符号即#原本的目的是用来指示URL中指示网页中的位置,例如https://www.example.com/index.html#print即代表example的index.html的print位置,浏览器读取这个URL后,会自动将print位置滚动至可视区域,通常使用<a>标签的name属性或者<div>标签的id属性指定锚点。 通过window.location.hash属性能够读取锚点位置,可以为Hash的改变添加hashchange监听事件,每一次改变Hash,都会在浏览器的访问历史中增加一个记录,此外Hash虽然出现在URL中,但不会被包括在HTTP请求中,即#及之后的字符不会被发送到服务端进行资源或数据的请求,其是用来指导浏览器动作的,对服务器端没有效果,因此改变Hash不会重新加载页面。 ReactRouter的作用就是通过改变URL,在不重新请求页面的情况下,更新页面视图,从而动态加载与销毁组件,简单的说就是,虽然地址栏的地址改变了,但是并不是一个全新的页面,而是之前的页面某些部分进行了修改,这也是SPA单页应用的特点,其所有的活动局限于一个Web页面中,非懒加载的页面仅在该Web页面初始化时加载相应的HTML、JavaScript、CSS文件,一旦页面加载完成,SPA不会进行页面的重新加载或跳转,而是利用JavaScript动态的变换HTML,默认Hash模式是通过锚点实现路由以及控制组件的显示与隐藏来实现类似于页面跳转的交互.
Memory History不会在地址栏被操作或读取,这就可以解释如何实现服务器渲染的,同时其也非常适合测试和其他的渲染环境例如React Native,和另外两种History的一点不同是我们必须创建它,这种方式便于测试.
1
|
const history = createMemoryHistory(location);
|
我们来实现一个非常简单的Browser History模式与Hash History模式的实现,因为H5的pushState方法不能在本地文件协议file://运行,所以运行起来需要搭建一个http://环境,使用webpack、Nginx、Apache等都可以,回到Browser History模式路由,能够实现history路由跳转不刷新页面得益与H5提供的pushState()、replaceState()等方法以及popstate等事件,这些方法都是也可以改变路由路径,但不作页面跳转,当然如果在后端不配置好的情况下路由改编后刷新页面会提示404,对于Hash History模式,我们的实现思路相似,主要在于没有使用pushState等H5的API,以及监听事件不同,通过监听其hashchange事件的变化,然后拿到对应的location.hash更新对应的视图.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
<!-- Browser History -->
<!DOCTYPE html>
<html lang=
"en"
>
<head>
<meta charset=
"UTF-8"
>
<title>Router</title>
</head>
<body>
<ul>
<li><a href=
"/home"
rel=
"external nofollow"
>home</a></li>
<li><a href=
"/about"
rel=
"external nofollow"
>about</a></li>
<div id=
"routeView"
></div>
</ul>
</body>
<script>
function
Router() {
this
.routeView =
null
;
// 组件承载的视图容器
this
.routes = Object.create(
null
);
// 定义的路由
}
// 绑定路由匹配后事件
Router.prototype.route =
function
(path, callback) {
this
.routes[path] = () =>
this
.routeView.innerHTML = callback() ||
""
;
};
// 初始化
Router.prototype.init =
function
(root, rootView) {
this
.routeView = rootView;
// 指定承载视图容器
this
.refresh();
// 初始化即刷新视图
root.addEventListener(
"click"
, (e) => {
// 事件委托到root
if
(e.target.nodeName ===
"A"
) {
e.preventDefault();
history.pushState(
null
,
""
, e.target.getAttribute(
"href"
));
this
.refresh();
// 触发即刷新视图
}
})
// 监听用户点击后退与前进
// pushState与replaceState不会触发popstate事件
window.addEventListener(
"popstate"
,
this
.refresh.bind(
this
),
false
);
};
// 刷新视图
Router.prototype.refresh =
function
() {
let path = location.pathname;
console.log(
"refresh"
, path);
if
(
this
.routes[path])
this
.routes[path]();
else
this
.routeView.innerHTML =
""
;
};
window.Router =
new
Router();
Router.route(
"/home"
,
function
() {
return
"home"
;
});
Router.route(
"/about"
,
function
() {
return
"about"
;
});
Router.init(document, document.getElementById(
"routeView"
));
</script>
</html>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
<!-- Hash History -->
<!DOCTYPE html>
<html lang=
"en"
>
<head>
<meta charset=
"UTF-8"
>
<title>Router</title>
</head>
<body>
<ul>
<li><a href=
"#/home"
rel=
"external nofollow"
>home</a></li>
<li><a href=
"#/about"
rel=
"external nofollow"
>about</a></li>
<div id=
"routeView"
></div>
</ul>
</body>
<script>
function
Router() {
this
.routeView =
null
;
// 组件承载的视图容器
this
.routes = Object.create(
null
);
// 定义的路由
}
// 绑定路由匹配后事件
Router.prototype.route =
function
(path, callback) {
this
.routes[path] = () =>
this
.routeView.innerHTML = callback() ||
""
;
};
// 初始化
Router.prototype.init =
function
(root, rootView) {
this
.routeView = rootView;
// 指定承载视图容器
this
.refresh();
// 初始化触发
// 监听hashchange事件用以刷新
window.addEventListener(
"hashchange"
,
this
.refresh.bind(
this
),
false
);
};
// 刷新视图
Router.prototype.refresh =
function
() {
let hash = location.hash;
console.log(
"refresh"
, hash);
if
(
this
.routes[hash])
this
.routes[hash]();
else
this
.routeView.innerHTML =
""
;
};
window.Router =
new
Router();
Router.route(
"#/home"
,
function
() {
return
"home"
;
});
Router.route(
"#/about"
,
function
() {
return
"about"
;
});
Router.init(document, document.getElementById(
"routeView"
));
</script>
</html>
|
ReactRouter
的实现,commit id
为eef79d5
,TAG
是4.4.0
,在这之前我们需要先了解一下history
库,history
库,是ReactRouter
依赖的一个对window.history
加强版的history
库,其中主要用到的有match
对象表示当前的URL
与path
的匹配的结果,location
对象是history
库基于window.location
的一个衍生。ReactRouter
将路由拆成了几个包: react-router
负责通用的路由逻辑,react-router-dom
负责浏览器的路由管理,react-router-native
负责react-native
的路由管理。BrowserRouter
组件为例,BrowserRouter
在react-router-dom
中,它是一个高阶组件,在内部创建一个全局的history
对象,可以监听整个路由的变化,并将history
作为props
传递给react-router
的Router
组件,Router
组件再会将这个history
的属性作为context
传递给子组件。
1
2
3
4
5
6
7
8
|
// packages\react-router-dom\modules\HashRouter.js line 10
class BrowserRouter extends React.Component {
history = createHistory(
this
.props);
render() {
return
<Router history={
this
.history} children={
this
.props.children} />;
}
}
|
接下来我们到Router组件,Router组件创建了一个React Context环境,其借助context向Route传递context,这也解释了为什么Router要在所有Route的外面。在Router的componentWillMount中,添加了history.listen,其能够监听路由的变化并执行回调事件,在这里即会触发setState。当setState时即每次路由变化时 -> 触发顶层Router的回调事件 -> Router进行setState -> 向下传递 nextContext此时context中含有最新的location -> 下面的Route获取新的nextContext判断是否进行渲染.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
// line packages\react-router\modules\Router.js line 10
class Router extends React.Component {
static computeRootMatch(pathname) {
return
{ path:
"/"
, url:
"/"
, params: {}, isExact: pathname ===
"/"
};
}
constructor(props) {
super
(props);
this
.state = {
location: props.history.location
};
// This is a bit of a hack. We have to start listening for location
// changes here in the constructor in case there are any <Redirect>s
// on the initial render. If there are, they will replace/push when
// they mount and since cDM fires in children before parents, we may
// get a new location before the <Router> is mounted.
this
._isMounted =
false
;
this
._pendingLocation =
null
;
if
(!props.staticContext) {
this
.unlisten = props.history.listen(location => {
if
(
this
._isMounted) {
this
.setState({ location });
}
else
{
this
._pendingLocation = location;
}
});
}
}
componentDidMount() {
this
._isMounted =
true
;
if
(
this
._pendingLocation) {
this
.setState({ location:
this
._pendingLocation });
}
}
componentWillUnmount() {
if
(
this
.unlisten)
this
.unlisten();
}
render() {
return
(
<RouterContext.Provider
children={
this
.props.children ||
null
}
value={{
history:
this
.props.history,
location:
this
.state.location,
match: Router.computeRootMatch(
this
.state.location.pathname),
staticContext:
this
.props.staticContext
}}
/>
);
}
}
|
我们在使用时都是使用Router来嵌套Route,所以此时就到Route组件,Route的作用是匹配路由,并传递给要渲染的组件props,Route接受上层的Router传入的context,Router中的history监听着整个页面的路由变化,当页面发生跳转时,history触发监听事件,Router向下传递nextContext,就会更新Route的props和context来判断当前Route的path是否匹配location,如果匹配则渲染,否则不渲染,是否匹配的依据就是computeMatch这个函数,在下文会有分析,这里只需要知道匹配失败则match为null,如果匹配成功则将match的结果作为props的一部分,在render中传递给传进来的要渲染的组件。Route接受三种类型的render props,<Route component>、<Route render>、<Route children>,此时要注意的是如果传入的component是一个内联函数,由于每次的props.component都是新创建的,所以React在diff的时候会认为进来了一个全新的组件,所以会将旧的组件unmount再re-mount。这时候就要使用render,少了一层包裹的component元素,render展开后的元素类型每次都是一样的,就不会发生re-mount了,另外children也不会发生re-mount.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
// \packages\react-router\modules\Route.js line 17
class Route extends React.Component {
render() {
return
(
<RouterContext.Consumer>
{context => {
invariant(context,
"You should not use <Route> outside a <Router>"
);
const location =
this
.props.location || context.location;
const match =
this
.props.computedMatch
?
this
.props.computedMatch
// <Switch> already computed the match for us
:
this
.props.path
? matchPath(location.pathname,
this
.props)
: context.match;
const props = { ...context, location, match };
let { children, component, render } =
this
.props;
// Preact uses an empty array as children by
// default, so use null if that's the case.
if
(Array.isArray(children) && children.length === 0) {
children =
null
;
}
if
(
typeof
children ===
"function"
) {
children = children(props);
// ...
}
return
(
<RouterContext.Provider value={props}>
{children && !isEmptyChildren(children)
? children
: props.match
? component
? React.createElement(component, props)
: render
? render(props)
:
null
:
null
}
</RouterContext.Provider>
);
}}
</RouterContext.Consumer>
);
}
}
|
我们实际上我们可能写的最多的就是Link这个标签了,所以我们再来看一下<Link>组件,我们可以看到Link最终还是创建一个a标签来包裹住要跳转的元素,在这个a标签的handleClick点击事件中会preventDefault禁止默认的跳转,所以实际上这里的href并没有实际的作用,但仍然可以标示出要跳转到的页面的URL并且有更好的html语义。在handleClick中,对没有被preventDefault、鼠标左键点击的、非_blank跳转的、没有按住其他功能键的单击进行preventDefault,然后push进history中,这也是前面讲过的路由的变化与 页面的跳转是不互相关联的,ReactRouter在Link中通过history库的push调用了HTML5 history的pushState,但是这仅仅会让路由变化,其他什么都没有改变。在Router中的listen,它会监听路由的变化,然后通过context更新props和nextContext让下层的Route去重新匹配,完成需要渲染部分的更新.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
// packages\react-router-dom\modules\Link.js line 14
class Link extends React.Component {
handleClick(event, history) {
if
(
this
.props.onClick)
this
.props.onClick(event);
if
(
!event.defaultPrevented &&
// onClick prevented default
event.button === 0 &&
// ignore everything but left clicks
(!
this
.props.target ||
this
.props.target ===
"_self"
) &&
// let browser handle "target=_blank" etc.
!isModifiedEvent(event)
// ignore clicks with modifier keys
) {
event.preventDefault();
const method =
this
.props.replace ? history.replace : history.push;
method(
this
.props.to);
}
}
render() {
const { innerRef, replace, to, ...rest } =
this
.props;
// eslint-disable-line no-unused-vars
return
(
<RouterContext.Consumer>
{context => {
invariant(context,
"You should not use <Link> outside a <Router>"
);
const location =
typeof
to ===
"string"
? createLocation(to,
null
,
null
, context.location)
: to;
const href = location ? context.history.createHref(location) :
""
;
return
(
<a
{...rest}
onClick={event =>
this
.handleClick(event, context.history)}
href={href}
ref={innerRef}
/>
);
}}
</RouterContext.Consumer>
);
}
}
|
每日一题 。
https://github.com/WindrunnerMax/EveryDay 。
参考 。
https://zhuanlan.zhihu.com/p/44548552 https://github.com/fi3ework/blog/issues/21 https://juejin.cn/post/6844903661672333326 https://juejin.cn/post/6844904094772002823 https://juejin.cn/post/6844903878568181768 https://segmentfault.com/a/1190000014294604 https://github.com/youngwind/blog/issues/109 http://react-guide.github.io/react-router-cn/docs/guides/basics/Histories.html 。
到此这篇关于ReactRouter的实现方法的文章就介绍到这了,更多相关ReactRouter的实现内容请搜索我以前的文章或继续浏览下面的相关文章希望大家以后多多支持我! 。
原文链接:https://www.cnblogs.com/WindrunnerMax/p/14322330.html 。
最后此篇关于ReactRouter的实现方法的文章就讲到这里了,如果你想了解更多关于ReactRouter的实现方法的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
背景: 我最近一直在使用 JPA,我为相当大的关系数据库项目生成持久层的轻松程度给我留下了深刻的印象。 我们公司使用大量非 SQL 数据库,特别是面向列的数据库。我对可能对这些数据库使用 JPA 有一
我已经在我的 maven pom 中添加了这些构建配置,因为我希望将 Apache Solr 依赖项与 Jar 捆绑在一起。否则我得到了 SolarServerException: ClassNotF
interface ITurtle { void Fight(); void EatPizza(); } interface ILeonardo : ITurtle {
我希望可用于 Java 的对象/关系映射 (ORM) 工具之一能够满足这些要求: 使用 JPA 或 native SQL 查询获取大量行并将其作为实体对象返回。 允许在行(实体)中进行迭代,并在对当前
好像没有,因为我有实现From for 的代码, 我可以转换 A到 B与 .into() , 但同样的事情不适用于 Vec .into()一个Vec . 要么我搞砸了阻止实现派生的事情,要么这不应该发
在 C# 中,如果 A 实现 IX 并且 B 继承自 A ,是否必然遵循 B 实现 IX?如果是,是因为 LSP 吗?之间有什么区别吗: 1. Interface IX; Class A : IX;
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我正在阅读标准haskell库的(^)的实现代码: (^) :: (Num a, Integral b) => a -> b -> a x0 ^ y0 | y0 a -> b ->a expo x0
我将把国际象棋游戏表示为 C++ 结构。我认为,最好的选择是树结构(因为在每个深度我们都有几个可能的移动)。 这是一个好的方法吗? struct TreeElement{ SomeMoveType
我正在为用户名数据库实现字符串匹配算法。我的方法采用现有的用户名数据库和用户想要的新用户名,然后检查用户名是否已被占用。如果采用该方法,则该方法应该返回带有数据库中未采用的数字的用户名。 例子: “贾
我正在尝试实现 Breadth-first search algorithm , 为了找到两个顶点之间的最短距离。我开发了一个 Queue 对象来保存和检索对象,并且我有一个二维数组来保存两个给定顶点
我目前正在 ika 中开发我的 Python 游戏,它使用 python 2.5 我决定为 AI 使用 A* 寻路。然而,我发现它对我的需要来说太慢了(3-4 个敌人可能会落后于游戏,但我想供应 4-
我正在寻找 Kademlia 的开源实现C/C++ 中的分布式哈希表。它必须是轻量级和跨平台的(win/linux/mac)。 它必须能够将信息发布到 DHT 并检索它。 最佳答案 OpenDHT是
我在一本书中读到这一行:-“当我们要求 C++ 实现运行程序时,它会通过调用此函数来实现。” 而且我想知道“C++ 实现”是什么意思或具体是什么。帮忙!? 最佳答案 “C++ 实现”是指编译器加上链接
我正在尝试使用分支定界的 C++ 实现这个背包问题。此网站上有一个 Java 版本:Implementing branch and bound for knapsack 我试图让我的 C++ 版本打印
在很多情况下,我需要在 C# 中访问合适的哈希算法,从重写 GetHashCode 到对数据执行快速比较/查找。 我发现 FNV 哈希是一种非常简单/好/快速的哈希算法。但是,我从未见过 C# 实现的
目录 LRU缓存替换策略 核心思想 不适用场景 算法基本实现 算法优化
1. 绪论 在前面文章中提到 空间直角坐标系相互转换 ,测绘坐标转换时,一般涉及到的情况是:两个直角坐标系的小角度转换。这个就是我们经常在测绘数据处理中,WGS-84坐标系、54北京坐标系
在软件开发过程中,有时候我们需要定时地检查数据库中的数据,并在发现新增数据时触发一个动作。为了实现这个需求,我们在 .Net 7 下进行一次简单的演示. PeriodicTimer .
二分查找 二分查找算法,说白了就是在有序的数组里面给予一个存在数组里面的值key,然后将其先和数组中间的比较,如果key大于中间值,进行下一次mid后面的比较,直到找到相等的,就可以得到它的位置。
我是一名优秀的程序员,十分优秀!