⚑ Web API Router
监听地址栏的变动:
- requestAnimationFrame 动态侦测
- window.addEventListener("hashchange", handler);
判断浏览器是否支持 onhashchange 事件
if (('onhashchange' in window) && ((typeof document.documentMode === 'undefined') || document.documentMode == 8)) {
window.onhashchange = function () {
console.log(location.hash);
};
}
主程序,提供 Blog、UseEffect、Passthrough 等任意个组件以测试:
import React, {useState, useEffect} from "react"
import ReactDOM from 'react-dom';
import router from '/router.js';
import Blog from '/cp/keyedList.js';
import UseEffect from '/cp/useEffect.js';
import Passthrough from '/cp/passthrough.js';
let Category = () => {
const [n, setN] = React.useState(0);
return (
<>
<div className="grid">
<div className="col15">
<h1>Router</h1>
<a href="#/start" onClick={router.start}>Start</a>
<a href="#/stop" onClick={router.stop}>Stop</a>
</div>
<div className="col15">
<h1>Module</h1>
<a onClick={ev => setN(n)} href="#/blog">Blog</a>
<a onClick={ev => setN(n)} href="#/useEffect">useEffect</a>
<a onClick={ev => setN(n)} href="#/hr">hr</a>
<a onClick={ev => setN(n)} href="#/Passthrough">Passthrough</a>
</div>
</div>
<div className="grid">
<router.Root>
<router.Route path="/hr" cp={<div style={{width:'100%'}}><hr />🚩</div>} />
<router.Route path="/useEffect" cp={<UseEffect />} />
<router.Route path="/blog" cp={<Blog />} />
<router.Route path="/Passthrough" cp={<Passthrough />} />
</router.Root>
</div>
</>
)
}
ReactDOM.render(
<Category />,
document.getElementById('root')
);
router.js
import React from 'react';
// Polyfill
window.requestAnimationFrame = (function () {
if (!window.cancelAnimationFrame) {
window.cancelAnimationFrame = function (id) {
clearTimeout(id);
};
}
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
let router = ((requestAnimationFrame,cancelAnimationFrame)=>{
let that, requestid, prev = {}, setS,
fs = ['host', 'hostname', 'href', 'origin', 'pathname', 'port', 'protocol', 'hash'];
fs.map(it=>prev[it]=location[it]);
let monitor = () => {
if(prev.href !== location.href){
fs.map(it=>prev[it]=location[it]);
//console.log('change', location.hash);
setS && setS();
}
requestid = requestAnimationFrame(monitor);
}
let isMe = (path) => {
return '#'+path === location.hash;
}
return (that = {
start: ()=>{
that.stop()
monitor();
},
stop: ()=>{
cancelAnimationFrame(requestid);
},
Root: (props) => {
const [state, setState] = React.useState(false);
setS = () => setState(!state);
let found;
props.children && props.children.map(it => {
if(isMe(it.props.path)){
console.log('Root', isMe(it.props.path), it.props.path, it);
found = (<>{it.type(it.props)}</>);
}
})
return found;
},
Route: (props) => {
//console.log('Route', isMe(props.path), props.path, location.hash);
return( isMe(props.path)? <>{props.cp}</>:null );
}
});
})(requestAnimationFrame,cancelAnimationFrame)
router.start();
export default router;