1. Router implementation
The Router is implemented based on the Context. The Router is used to pass history.
import React from "react"; import PropTypes from "prop-types"; import warning from "tiny-warning"; import HistoryContext from "./HistoryContext.js"; import RouterContext from "./RouterContext.js"; class Router extends React.Component { static computeRootMatch(pathname) { return { path: "/", url: "/", params: {}, isExact: pathname === "/" }; } constructor(props) { ... } componentDidMount() { ... } componentWillUnmount() { ... } render() { return ( <RouterContext.Provider value={{ history: this.props.history, location: this.state.location, match: Router.computeRootMatch(this.state.location.pathname), staticContext: this.props.staticContext }} > <HistoryContext.Provider children={this.props.children || null} value={this.props.history} /> </RouterContext.Provider> ); }}export default Router;Copy the code
HistoryContext and RouterContext are implemented based on mini-create-react-Context.
// RouteContext.js import createNamedContext from "./createNameContext"; const context = /*#__PURE__*/ createNamedContext("Router"); export default context; // HistoryContext.js import createNamedContext from "./createNameContext"; const historyContext = /*#__PURE__*/ createNamedContext("Router-History"); export default historyContext; // createNameContext.jsimport createContext from "mini-create-react-context"; const createNamedContext = name => { const context = createContext(); context.displayName = name; return context; }; export default createNamedContext;Copy the code
2. Implementation of Route
Route is used to match a single path after rendering component. Route is implemented based on RouterContext.consumer.
Match: children > Component > render
1, What is the function of children
Is function, executes children(props) and returns the value
Instead of function, return children
React.createElement(Component, props)
3, render(props)
Render children, Component, render null
Typeof children === “function”, function, children(props), null
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) && isEmptyChildren(children)) { children = null; } return ( <RouterContext.Provider value={props}> {props.match ? children ? typeof children === "function" ? __DEV__ ? evalChildrenDev(children, props, this.props.path) : children(props) : children : component ? React.createElement(component, props) : render ? render(props) : null : typeof children === "function" ? __DEV__ ? evalChildrenDev(children, props, this.props.path) : children(props) : null} </RouterContext.Provider> ); }} </RouterContext.Consumer> ); }}Copy the code
3, the Switch
Switch is used to render the first matching Route.
class Switch extends React.Component { render() { return ( <RouterContext.Consumer> {context => { invariant(context, "You should not use <Switch> outside a <Router>"); const location = this.props.location || context.location; let element, match; Child react.children. ForEach (this.props. Children, child => { if (match == null && React.isValidElement(child)) { element = child; const path = child.props.path || child.props.from; match = path ? matchPath(location.pathname, { ... child.props, path }) : context.match; }}); return match ? React.cloneElement(element, { location, computedMatch: match }) : null; }} </RouterContext.Consumer> ); }}Copy the code