-
Notifications
You must be signed in to change notification settings - Fork 0
/
React-Router原理.html
174 lines (150 loc) · 4.7 KB
/
React-Router原理.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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>React-Router</title>
<!-- 加载 React。-->
<!-- 注意: 部署时,将 "development.js" 替换为 "production.min.js"。-->
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/prop-types@15.6/prop-types.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
</body>
<!-- 实现一个简单的React-Router -->
<script type="text/babel">
let instances = []; // 用来存储页面中的 Router
const register = (comp) => instances.push(comp);
const unRegister = (comp) => instances.splice(instances.indexOf(comp), 1);
\
const historyPush = (path) => {
window.history.pushState({}, null, path);
instances.forEach(instance => instance.forceUpdate())
};
window.addEventListener('popstate', () => {
// 遍历所有 Route,强制重新渲染所有 Route
instances.forEach(instance => instance.forceUpdate());
});
// 判断 Route 的 path 参数与当前 url 是否匹配
const matchPath = (pathname, options) => {
const { path, exact = false } = options;
const match = new RegExp(`^${path}`).exec(pathname);
if (!match) return null;
const url = match[0];
const isExact = pathname === url;
if (exact && !isExact) return null;
return {
path,
url
}
};
class Link extends React.Component {
static propTypes = {
to: PropTypes.string
};
handleClick = (event) => {
event.preventDefault();
const { to } = this.props;
historyPush(to);
};
render() {
const { to, children } = this.props;
return (
<a href={to} onClick={this.handleClick}>
{children}
</a>
)
}
}
class Route extends React.Component {
static propTypes = {
path: PropTypes.string,
component: PropTypes.func,
exact: PropTypes.bool
};
componentWillMount() {
register(this);
}
render() {
const { path, component, exact } = this.props;
const match = matchPath(window.location.pathname, { path, exact });
// Route 跟当前 url 不匹配,就返回 null
if (!match) return null;
if (component) {
return React.createElement(component);
}
}
componentWillUnMount() {
unRegister(this);
}
}
// 这里之所以要导出一个 jsHistory,
// 是为了方便使用者在 JS 中直接控制导航
const jsHistory = {
pushState: historyPush
};
</script>
<script type="text/babel">
const App = () => (
<div>
<ul className="nav">
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/topics">Topics</Link></li>
</ul>
<BtnHome />
<BtnAbout />
<BtnTopics />
<hr />
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/topics" component={Topics} />
</div>
);
const Home = () => (
<div>
<h2>Home</h2>
</div>
);
const About = () => (
<div>
<h2>About</h2>
</div>
);
const Topics = ({ match }) => (
<div>
<h2>Topics</h2>
</div>
);
class BtnHome extends React.Component {
render() {
return (
<button onClick={jsHistory.pushState.bind(this, '/')}>Home</button>
)
}
}
class BtnAbout extends React.Component {
render() {
return (
<button onClick={jsHistory.pushState.bind(this, '/about')}>About</button>
)
}
}
class BtnTopics extends React.Component {
render() {
return (
<button onClick={jsHistory.pushState.bind(this, '/topics')}>Topics</button>
)
}
}
</script>
<!-- 挂载 App组件 -->
<script type="text/babel">
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
</script>
</html>