本文只代表自己粗陋的观点,有很多错误,欢迎指出错误
接上面的一篇《手写一个react-router-dom简易版(一)》
HashRouter
基本思路
- 同BrowserRouter思路一致,主要是监听路由变化不一致
- 使用window.addEventListener(“hashchange”)监听
- HashRouter.js添加HashRouter模式
import React from "react";
import { Provider } from "./context";
import History from "./history";
const url = require("url");
class HashRouter extends React.Component {
constructor() {
super();
this.state = {
pathname: window.location.hash.slice(1) || "/",
count: 0,
};
}
componentDidMount() {
window.location.hash= window.location.hash || "/"
window.addEventListener("hashchange", () => {
this.setState({
pathname: window.location.hash.slice(1) || "/",
});
});
}
render() {
let value = {
type: "HashRouter",
//history添加HashRouter模式
history: new History("HashRouter"),
location: Object.assign(
{
pathname: "/",
},
url.parse(this.state.pathname, true)
)
};
return <Provider value={value}>{this.props.children}</Provider>;
}
}
export default HashRouter;
history添加HashRouter模式
完善代码
const url = require("url");
class History {
constructor(type) {
this.type = type || "BrowserRouter";
}
push(path) {
//添加多种模式兼容
switch (this.type) {
case "BrowserRouter":
if (typeof path === "string") {
window.history.pushState(null, "", path);
}
if (typeof path === "object") {
let obj = {
pathname: path.path || "/",
query: path.query || {},
};
const formatUrl = url.format(obj);
window.history.pushState(null, "", formatUrl);
}
break;
case "HashRouter":
if (typeof path === "string") {
window.location.hash = path.indexOf("/") === 0 ? path : "/" + path;
}
if (typeof path === "object") {
let obj = {
pathname: path.path || "/",
query: path.query || {},
};
const formatUrl = url.format(obj);
window.location.hash =
formatUrl.indexOf("/") === 0 ? formatUrl : "/" + formatUrl;
}
break;
}
}
}
export default History;
BrowserRouter代码优化
完善代码
import React from "react";
import { Provider} from "./context";
import History from "./history";
const url = require("url");
class BrowserRouter extends React.Component {
constructor() {
super();
this.state = {
pathname: window.location.pathname || "/",
};
}
componentDidMount() {
window.addEventListener("pushState", () => {
console.log("pushState");
this.setPathname();
});
}
setPathname = () => {
this.setState(
{
pathname: window.location.pathname || "/",
},(v) => {
console.log(this.state);
}
);
};
render() {
let value = {
type: "BrowserRouter",
//添加BrowserRouter模式
history: new History("BrowserRouter"),
location: Object.assign(
{
pathname: "/",
},
url.parse(this.state.pathname, true)
)
};
return (
<Provider value={value}>
{this.props.children}
</Provider>
);
}
}
export default BrowserRouter;
Switch
import React from 'react'
import { Consumer } from './context'
class Switch extends React.Component {
render () {
return (
<Consumer>
{
(state) => {
for (let i = 0; i < this.props.children.length; i++) {
const path = (this.props.children[i].props && this.props.children[i].props.path) || '/'
const reg = new RegExp('^' + path+'$')
if (reg.test(state.location.pathname)) {
return this.props.children[i]
}else if(this.props.children[i].props.to){
return this.props.children[i]
}
}
}
}
</Consumer>
)
}
}
export default Switch
Redirect
需要与Switch组件配合使用,重定向到对应视图
import React from 'react'
import { Consumer } from './context'
class Switch extends React.Component {
render () {
return (
<Consumer>
{
(state) => {
for (let i = 0; i < this.props.children.length; i++) {
const path = (this.props.children[i].props && this.props.children[i].props.path) || '/'
const reg = new RegExp('^' + path+'$')
if (reg.test(state.location.pathname)) {
return this.props.children[i]
}else if(this.props.children[i].props.to){
return this.props.children[i]
}
}
}
}
</Consumer>
)
}
}
export default Switch
总结
其实react-router-dom设计思路就是订阅-发布模式,通过包装组件(生产者),发布通知给消费组件(消费者)调用前端浏览器来改变url,再通过react数据变化,改变对应的视图变化,很多未实现的功能,大体思路都是一致的,其他组件就不一一实现了,有兴趣的读者可以按照这种思路,实现其他组件,其实vue-router设计思路与这种方式大体一致