在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
ReactRouter的实现
描述
Browser History
location / { try_files $uri $uri/ /index.html; } Hash History
Memory History
const history = createMemoryHistory(location); 实现我们来实现一个非常简单的 <!-- Browser History --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Router</title> </head> <body> <ul> <li><a href="/home" rel="external nofollow" >home</a></li> <li><a href="/about" rel="external nofollow" >about</a></li> <div id="routeView"></div> </ul> </body> <script> function Router() { this.routeView = null; // 组件承载的视图容器 this.routes = Object.create(null); // 定义的路由 } // 绑定路由匹配后事件 Router.prototype.route = function (path, callback) { this.routes[path] = () => this.routeView.innerHTML = callback() || ""; }; // 初始化 Router.prototype.init = function(root, rootView) { this.routeView = rootView; // 指定承载视图容器 this.refresh(); // 初始化即刷新视图 root.addEventListener("click", (e) => { // 事件委托到root if (e.target.nodeName === "A") { e.preventDefault(); history.pushState(null, "", e.target.getAttribute("href")); this.refresh(); // 触发即刷新视图 } }) // 监听用户点击后退与前进 // pushState与replaceState不会触发popstate事件 window.addEventListener("popstate", this.refresh.bind(this), false); }; // 刷新视图 Router.prototype.refresh = function () { let path = location.pathname; console.log("refresh", path); if(this.routes[path]) this.routes[path](); else this.routeView.innerHTML = ""; }; window.Router = new Router(); Router.route("/home", function() { return "home"; }); Router.route("/about", function () { return "about"; }); Router.init(document, document.getElementById("routeView")); </script> </html> <!-- Hash History --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Router</title> </head> <body> <ul> <li><a href="#/home" rel="external nofollow" >home</a></li> <li><a href="#/about" rel="external nofollow" >about</a></li> <div id="routeView"></div> </ul> </body> <script> function Router() { this.routeView = null; // 组件承载的视图容器 this.routes = Object.create(null); // 定义的路由 } // 绑定路由匹配后事件 Router.prototype.route = function (path, callback) { this.routes[path] = () => this.routeView.innerHTML = callback() || ""; }; // 初始化 Router.prototype.init = function(root, rootView) { this.routeView = rootView; // 指定承载视图容器 this.refresh(); // 初始化触发 // 监听hashchange事件用以刷新 window.addEventListener("hashchange", this.refresh.bind(this), false); }; // 刷新视图 Router.prototype.refresh = function () { let hash = location.hash; console.log("refresh", hash); if(this.routes[hash]) this.routes[hash](); else this.routeView.innerHTML = ""; }; window.Router = new Router(); Router.route("#/home", function() { return "home"; }); Router.route("#/about", function () { return "about"; }); Router.init(document, document.getElementById("routeView")); </script> </html> 分析
// packages\react-router-dom\modules\HashRouter.js line 10 class BrowserRouter extends React.Component { history = createHistory(this.props); render() { return <Router history={this.history} children={this.props.children} />; } } 接下来我们到 // line packages\react-router\modules\Router.js line 10 class Router extends React.Component { static computeRootMatch(pathname) { return { path: "/", url: "/", params: {}, isExact: pathname === "/" }; } constructor(props) { super(props); this.state = { location: props.history.location }; // This is a bit of a hack. We have to start listening for location // changes here in the constructor in case there are any <Redirect>s // on the initial render. If there are, they will replace/push when // they mount and since cDM fires in children before parents, we may // get a new location before the <Router> is mounted. this._isMounted = false; this._pendingLocation = null; if (!props.staticContext) { this.unlisten = props.history.listen(location => { if (this._isMounted) { this.setState({ location }); } else { this._pendingLocation = location; } }); } } componentDidMount() { this._isMounted = true; if (this._pendingLocation) { this.setState({ location: this._pendingLocation }); } } componentWillUnmount() { if (this.unlisten) this.unlisten(); } render() { return ( <RouterContext.Provider children={this.props.children || null} value={{ history: this.props.history, location: this.state.location, match: Router.computeRootMatch(this.state.location.pathname), staticContext: this.props.staticContext }} /> ); } } 我们在使用时都是使用 // \packages\react-router\modules\Route.js line 17 class Route extends React.Component { render() { return ( <RouterContext.Consumer> {context => { invariant(context, "You should not use <Route> outside a <Router>"); const location = this.props.location || context.location; const match = this.props.computedMatch ? this.props.computedMatch // <Switch> already computed the match for us : this.props.path ? matchPath(location.pathname, this.props) : context.match; const props = { ...context, location, match }; let { children, component, render } = this.props; // Preact uses an empty array as children by // default, so use null if that's the case. if (Array.isArray(children) && children.length === 0) { children = null; } if (typeof children === "function") { children = children(props); // ... } return ( <RouterContext.Provider value={props}> {children && !isEmptyChildren(children) ? children : props.match ? component ? React.createElement(component, props) : render ? render(props) : null : null} </RouterContext.Provider> ); }} </RouterContext.Consumer> ); } } 我们实际上我们可能写的最多的就是 // packages\react-router-dom\modules\Link.js line 14 class Link extends React.Component { handleClick(event, history) { if (this.props.onClick) this.props.onClick(event); if ( !event.defaultPrevented && // onClick prevented default event.button === 0 && // ignore everything but left clicks (!this.props.target || this.props.target === "_self") && // let browser handle "target=_blank" etc. !isModifiedEvent(event) // ignore clicks with modifier keys ) { event.preventDefault(); const method = this.props.replace ? history.replace : history.push; method(this.props.to); } } render() { const { innerRef, replace, to, ...rest } = this.props; // eslint-disable-line no-unused-vars return ( <RouterContext.Consumer> {context => { invariant(context, "You should not use <Link> outside a <Router>"); const location = typeof to === "string" ? createLocation(to, null, null, context.location) : to; const href = location ? context.history.createHref(location) : ""; return ( <a {...rest} onClick={event => this.handleClick(event, context.history)} href={href} ref={innerRef} /> ); }} </RouterContext.Consumer> ); } } 每日一题 https://github.com/WindrunnerMax/EveryDay 参考 https://zhuanlan.zhihu.com/p/44548552 https://github.com/fi3ework/blog/issues/21 https://juejin.cn/post/6844903661672333326 https://juejin.cn/post/6844904094772002823 https://juejin.cn/post/6844903878568181768 https://segmentfault.com/a/1190000014294604 https://github.com/youngwind/blog/issues/109 http://react-guide.github.io/react-router-cn/docs/guides/basics/Histories.html 到此这篇关于ReactRouter的实现方法的文章就介绍到这了,更多相关ReactRouter的实现内容请搜索极客世界以前的文章或继续浏览下面的相关文章希望大家以后多多支持极客世界! |
请发表评论