自己實(shí)現(xiàn)一個react-router
期望最簡單的使用效果
export default () => {
return (
<>
<Link to="/a">菜單a</>
<Link to="/b">菜單b</>
</>
<Router>
<Route path="/a"><A /></Route>
<Route path="/b"><B /></Route>
</Router>
)
}
Router一般分HashRouter和BrowserHistoryRouter,原理區(qū)別分別是對location.hash和history api的處理
先寫一些公用組件
// Link
const Link = ({ children }) => <a>children</a>
// Route
const Route = () => {}
實(shí)現(xiàn)HashRouter
原理是在Link上綁定點(diǎn)擊更改hash值事件,利用瀏覽器監(jiān)聽hash值的變化做相應(yīng)的動作(渲染相應(yīng)組件),因?yàn)槭呛瘮?shù)組件,會用到useEffect,useState
// HashRouter
const HashRouter = ({ children }) => {
const [hash, setHash] = useState(window.location.hash)
// hash變更觸發(fā)
const hashChange = e => {
const { newURL } = e
setHash(newURL.slice(newURL.indexOf('#')))
}
// 記得銷毀時要清除綁定
useEffect(() => {
window.addEventListener('hashchange', hashChange)
return window.removeEventListener('hashchange', hashChange)
}, [])
// 為什么不用children遍歷,因?yàn)閏hildren有3種情況(1. 沒子組件會返回undefined 2.單個子組件返回一個對象 3.多個子組件返回數(shù)組),而React.Children會自行判斷,比較安全,當(dāng)然要多個null兜底,因?yàn)榉祷豼ndefined也會報錯
return React.Children.map(children, child => {
if(child.type.name === 'Route') {
if(child.props.path === hash) {
return child.props.children
}
return null
}
return child.props.children
}) || null
}
相應(yīng)Link也要添加點(diǎn)擊邏輯
const Link = ({ to, children, handleClick }) => {
const handleClick = () => window.location.hash = to
<span onClick={handleClick}>{children}</span>
}

加一點(diǎn)排版
最簡單的功能就這樣完成了
...
<div id='menu'>
<div><Link to='a'>a組件</Link></div<
</div>
<div id='container'>
<HashRouter>
<Route path='/a'><A /></Route>
</HashRouter>
</div>
接著搞嵌套路由看看
export default () => <div style={containerStyle}>
<div style={menuStyle}>
<div><Link to='#/a'>a組件</Link></div>
<div><Link to='#/b'>b組件</Link></div>
<div><Link to='#/c'>C組件</Link></div>
</div>
<div style={contentStyle}>
<HashRouter>
<Route path="#/a"><A /></Route>
<Route path="#/b"><B /></Route>
<Route path="#/c"><C /></Route>
</HashRouter>
</div>
</div>
const C1 = () => <div>我是C1</div>
const C2 = () => <div>我是C2</div>
const C = () => (
<div>
<Link to='#/c/c1'>C1組件</Link>
<Link to='#/c/c2'>C2組件</Link>
<div style={contentStyle}>
<h2>c下面的組件</h2>
<HashRouter>
<Route path="#/c/c1"><C1 /></Route>
<Route path="#/c/c2"><C2 /></Route>
</HashRouter>
</div>
</div>
)

#/c的效果

點(diǎn)擊C1
點(diǎn)擊C1或C2的Link,發(fā)現(xiàn)整個C都消失了,分析到因?yàn)楫?dāng)前hash應(yīng)當(dāng)是#/c/c1,而C組件本身對于要在#/c的時候才會渲染,用全等判斷自然不會渲染出來,子組件C1和C2肯定也不會出來

hash全等
將全等改成includes,發(fā)現(xiàn)效果出來了
if (child.type.name === 'Route') {
if (hash.includes(child.props.path)) {
return child.props.children
}
return null
}

改動后
待續(xù)更新BrowserRouter