React
路由实现的两种方式HashRouter
: 基于 window.onhashchange
实现BrowserRouter
:基于 HTML5 popstate event
实现Router
组件容器,经过建立上下文的方式,传递以下数据给后代组件使用;componentDidMount
/ useEffect
,订阅 路由变化 的函数,修改相应的 路径 匹配渲染对应的组件HashRouter
监听的是 hashchange
事件BrowserRouter
监听的是 pushstate
& popstate
事件注意:
HTML5
中并无pushstate
原生事件, 须要自行实现。html
下文中实现的方式均为 Functional Component
函数式组件react
RouterContext.js
import { createContext } from 'react';
export default createContext();
复制代码
HashRouter
实现当 一个窗口的 hash
(URL
中 #
后面的部分)改变时就会触发 hashchange
事件浏览器
import React, { useEffect, useState } from 'react';
import RouterContext from './RouterContext';
export default function () {
const location = {
pathname: window.location.hash.slice(1) || '/',
state: null,
}
let locationState = null;
let [ initialLocation, setInitialLocation ] = useState(location);
useEffect(() => {
window.addEventListener('hashchange', () => {
setInitialLocation({
...initialLocation,
pathname: window.location.hash.slice(1) || '/',
state: locationState,
})
})
window.location.hash = window.location.hash || '#/';
// 赋值时加 #, 取值时无 #
});
const history = {
location: initialLocation,
push(to) {
if (history.prompt) {
const target = typeof to === 'string' ? { pathname: to } :to;
const yes = window.confirm(prompt(target));
if (!yes) return;
}
if (typeof to === 'object') {
const { pathname, state } = to;
locationState = state;
window.location.hash = pathname;
} else {
window.location.hash = to;
}
},
block(prmopt) {
history.prompt = prompt;
},
unblock() {
history.prompt = null;
},
}
const routerValue = {
history,
location: initialLocation
};
return (
<RouterContext value = { routerValue }></RouterContext>
)
}
复制代码
BrowserRouter
实现调用 history.pushState()
或者 history.replaceState()
不会触发 popstate
事件. popstate
事件只会在浏览器某些行为下触发, 好比点击后退、前进按钮(或者在JavaScript中调用 history.back()
、history.forward()
、history.go()
方法).app
import RouterContext from "./RouterContext"
import React, { useEffect, useState } from "react"
export default function (props) {
let location = {
pathname: window.location.pathname,
state: null,
}
let [ initialLocation, setInitialLocation ] = useState( location );
useEffect(() => {
window.onpushstate = (state, pathname) => {
setInitialLocation({
...initialLocation,
pathname,
state,
})
}
window.onpopstate = (event) => {
setInitialLocation({
...initialLocation,
pathname: window.location.pathname,
state: event.state,
})
}
});
const globalHistory = window.history;
let history = {
location: initialLocation,
push(to) {
if (history.prompt) {
let target = typeof to === 'string' ? { pathname: to } : to;
let yes = window.confirm(history.prompt(target));
if (!yes) return;
}
if (typeof to === 'object') {
let { pathname, state } = to;
globalHistory.pushState(state, null, pathname);
} else {
globalHistory.pushState(null, null, to);
}
},
block(prompt) {
history.prompt = prompt;
},
unblock() {
history.prompt = null;
}
}
const routerValue = {
history,
location: initialLocation,
};
return (
<RouterContext.Provider value={ routerValue }> { props.children } </RouterContext.Provider> ) } 复制代码
pushState
方法方法体内部增长 onpushstate
触发事件ide
index.html
<script>
!( history => {
const pushState = history.pushState;
history.pushState = function (state, title, url) {
if (typeof window.onpushstate === 'function') {
window.onpushState(state, url);
}
pushState.apply(history, arguments);
}
})(window.history)
</script>
复制代码