Router包括BrowserRouter HashRouter MemoryRouter
BrowserRouter使用HTML5提供的history API(pushState.replaceState和popstate)来保持UI和URL的同步
HashRouter使用URL的hash的部分(即window.location.hash)来保持UI和URL的同步
app.js
import React from "react";
import {BrowserRouter as Router} from "./kreact-router-dom/BrowserRouter";
import Route from "./k-react-router-dom/Route";
import Link from "./k-react-router-dom/Link";
import Switch from "./k-react-routerdom/Switch";
import HomePage from "./pages/HomePage";
import UserPage from "./pages/UserPage";
import LoginPage from "./pages/LoginPage";
import PrivateRoute from "./pages/PrivateRoute";
function App() {
return (
<div className="App">
<Router>
<Link to="/">⾸首⻚页</Link>
<Link to="/user">
⽤用户中⼼心
</Link>
<Link to="/login">登录</Link>
<Link to="/children">children</Link>
<Link to="/render">render</Link>
<Link to="/search/123">搜索-动态路路由</Link>
{/* <Switch location={{pathname: "/"}}>*/}
<Switch>
<Route exact path="/" component={HomePage} />
{/* <Route path="/user" component={UserPage} /> */}
<PrivateRoute path="/user" component={UserPage} />
<Route path="/login" component={LoginPage} />
<Route path="/children" children={()=> <div>children</div>} />
<Route path="/render" render={() =><div>render</div>} />
<Route path="/search/:id" component={searchComponent} />
</Switch>
</Router>
</div>
);
};
export default App;
function searchComponent(props) {
const {id} = props.match.params;
console.log("searchComponent-props", props);
//sy-log
return (
<div>
<div>searchComponent-{id}</div>
<Link to={"/search/" + id + "/detail"}>详情-嵌套路路由</Link>
<Route path={"/search/:id/detail"} component={DetailComponent} />
</div>
);
}
function DetailComponent(props) {
const {id} = props.match.params;
console.log("DetailComponent-props", props);
//sy-log
return <div>DetailComponent</div>;
}
实现BrowserRouter
import React, {Component} from "react";
import {createBrowserHistory} from "history";
import matchPath from "./matchPath";
const RouterConetext=React.createContext();
export class BrowserRouter extends Component{
constructor(props){
super(props)
this.state={};
this.histroy=createBrowserHistory();
this.state={
location:this.history.location;
}
this.unlisten=this.history.listen(location=>{
this.setState({
location:location
})
})
}
componentWillUnmount(){
if(this.unlisten){
this.unlisten();
}
}
render(){
<RouterContext.Provider
children={this.props.children}
value={{history:this.history,location:this.state.location}}
/>
}
}
实现Route
路由配置 匹配检测 内容渲染
export class Route extends Component{
render(){
return(
<RouterContext.Consumer>
{context=>{
const location=this.props.location||context.location;
const match=this.props.computedMatch
?this.props.computedMatch
:this.props.path
?matchPath(location.pathname,this.props)
:context.match;
const props={
...context,
location,
match
};
let {path,children,component,render}=this.props;
return(
<RouterContext.Provider value={props}>
{
match
?children
?typeof children==="function"
?children(props)
:children
:component
?React.createElement(component,props)
:render
?render(props)
:null
:typeof children==="function"
?children(props)
:null
}
</RouteContext.Propvider>
)
}
}
</RouterContext.Consumer>
)
}
}
实现Link
export class Link extends Component{
static contextType=RouterContext;
handleClick=event=>{
event.preventDefault();
this.context.history.push(this.props.to)
}
render(){
const {to}=this.props;
return (
<a href={to} onClick={this.hnadleClick}>
{this.props.children}
</a>
)
}
}
实现Switch
export class Switch extends Component{
render(){
<RouterContext.Consumer>
{ context=>{
loaction=context.location;
let element,match;
React.children.forEach(this.props.children,child=>{
if(match==null && React.isValidElement(child)){
element=child;
const path=children.props.path;
match=path?matchPath(location.pathname,{...child.props,path}:context.match
}
})
return match?Reat.cloneElement(element,{location}):null;
}
}
</RouterContext.Consumer>
}
}
实现Redirect
import React, {Component} from "react";
import {RouterContext} from "./RouterContext";
export default class Redirect extends component{
render(){
return (
<RouterContext.Consumer>
{
context=>{
const {history}=this.conetxt;
const {to} =this.props;
return <LifeCyle onMount={()=>history.replace(to)}/>
}
}
</RouterContext.Consumer>
)
}
}
class LifeCyle extends Component{
componentDidMount(){
if(this.props.onMount){
this.props.onMount.call(this,this);
}
}
render(){
return null;
}
}