基本原理
监听path/url的变化,根据path和component之间的关系,触发组件进行mount/unmount,同时使用context注入上下文
使用案例
const {BrowserRouter,Route, Routes} = require('react-route-dom')
function App(){
return <BrowserRouter>
<header>
<a href='./'>首页</a>
<a href='./list'>列表</a>
<a href='./about'>关于</a>
<a href='./hot'>热点</a>
</header>
<Routes>
<Route path="/list" element={<div>列表页面</div>}></Route>
<Route path="/about" element={<div>关于页面</div>}></Route>
<Route path="/list" element={<div>热点页面</div>}></Route>
</Routes>
</BrowserRouter>
}
源码实现
import React,{useMemo,useRef,useContext } from 'react'
const NavigationContext = createContext({})
const LocationContext = createContext({})
export function BrowserRouter({children}){
let historyRef = useRef()
if(historyRef.current == null){
history.current = createBrowserHistory()
}
let history = historyRef.current
let [state,setState] = useState({
action: history.action,
location = history.location
})
useLayoutEffect(()=>history.listen(setState),[history])
return <Router
children = {children}
location = {state.location}
navigator = {history}
navigationType = {state.action}
/>
}
function Router({children,location: locationProp,navigator}){
const navigationContext = useMemo(()=>({navigator}),[navigator])
const locationContext = useMemo(()=>({location: locationProp},[]locationProp))
return <NavigatonContext.Provider value = {navigationContext}>
<LocationContext.Provider value = {locationContext} children = {children}></LocationContext.Provider>
</NavigatonContext>
}
function useLocation(){
return useContext(LocationContext).location
}
function useNavigate(){
return useContext(NavigationContext).navigator
}
export const Routes = ({childrem})=>{
useRoutes(createRouterFromChildren(children))
}
export useRoutes(routes){
let location = useLocation()
let currentPath = location.pathname || '/'
let route = null
routes.forEach(({path,element})=>{
let match = currentPath.match(new RegExp(`^${path}`))
if(match){
route = element
}
})
return route
}
export const createRouterFromChildren = (children)=>{
let routes = []
React.Children.forEach(children,(node)=>{
let route = {
element: node.props.element,
path: node.props.path
}
})
if(node.props.children){
route.children = createRouterFromChildren(node.props.children)
}
routes.push(route)
}