React-router source code
Screenshots of the source code with key notes, simple version of the implementationGithub.com/upxin/react…
Overview of Routing
1. When the user refreshes the page, the browser relocates resources (sends requests) based on the current URL by default. This action is not necessary for a SPA, because a SPA, as a single page, will have only one resource corresponding to it anyway. In this case, if the normal request-refresh process is followed, the user’s forward and backward operations cannot be recorded. For the server, a single page application is a URL and a set of resources, so that “different URLS” can be used to map different view contents and perceive changes in URL. Once we sense that, we use JS to generate different content for it based on those changes. 2. Block the user’s refresh operation to prevent the server from blindly responding and returning the resource content that does not meet the expectation, and completely digest the refresh operation in the front-end logic. 3. The way to implement front-end routing hash and history we (take hash as an example, the source code implementation of the two is not very different)
Take a look at this flowchart to get your thoughts straight
React Router management and architecture
The React Router is also managed by Monorepo. Monorepo allows the same repository to manage multiple independent projects at the same time, and each project can reference each other. Router is then referenced by the router-dom directory that is the same as router
Take hash as an example, and then we will mainly look at two files. The main idea is reflected in one is that the Router represents (Provider) transmitting routing information, and the Switch represents (Consumer) matching legitimate components
/ * * * * that we usually use the < the Router > < Switch > < the Route > < / Route > < the Route > < / Route > < / Switch > < / Router > * /
Copy the code
import React from "react";
import { Router } from "react-router";
// There is no real difference in implementation between the two modes, except for the different API used for the history library to listen for address changes
//import { createBrowserHistory as createHistory } from "history";
import { createHashHistory as createHistory } from "history";
class HashRouter extends React.Component {
history = createHistory(this.props);
render() {
// Pass in history, which is the main listening API
return <Router history={this.history} children={this.props.children} />; }}export default HashRouter;
Copy the code
Router = Router = Router = Router = Router
class Router extends React.Component {
constructor(props) {
// Initializes listening for URL changes and saves the changed location assignment to _pendingLocation
this.unlisten = props.history.listen(location= > {
if (this._isMounted) {
this.setState({ location });
} else {
this._pendingLocation = location; }}); }componentDidMount() {
this._isMounted = true;
if (this._pendingLocation) {
// Reset the location
this.setState({ location: this._pendingLocation }); }}render() {
return (
<RouterContext.Provider// This is the casereact creatContexttheProvider
value={{
history: this.props.history// Incominglocation
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>); }}Copy the code
Let’s talk about Switch, which actually matches a component and returns
import React from "react";
import RouterContext from "./RouterContext.js";
import matchPath from "./matchPath.js";
/** * The public API for rendering the first
that matches. */
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; If a match is matched, set the value of match not null, and return the current elment. A plugin called path-to-regexp is used to generate a regex from the path passed in by route to match the pathname of location, whether it is a strong match or a fuzzy match. So there must be a re generated by the child's path to match the PathName, 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
## For example, look at the picture below
‘/search/robot’ redirects to the child route ‘/search/robot/identify’, but ‘/search/robot’ does not add exact. Because the location pathname we collected is ‘/search/robot/identify’, the robot component of ‘/search/robot’ will also be rendered because it is not an exact match. If it is an exact match, the Robot component will not render and the child routing component will not exist, then the route address pathname is ‘/search/ Robot /identify’, and the route component will produce the re roughly /^/search/ Robot (? :/ #?)? (? = / / #? | | $) / [] I, that means any content/search/robot followed will render robot components, robot components apply colours to a drawing, its subcomponents can render.
Finally, there are Provider and Consumer pollify versions of the router source code
class Provider extends Component<ProviderProps<T>> {
// Publish subscribe center
emitter = createEventEmitter(this.props.value);
static childContextTypes = {
[contextProp]: PropTypes.object.isRequired
};
// This method passes a context to a child component
getChildContext() {
return {
[contextProp]: this.emitter }; }}class Consumer extends Component<ConsumerProps<T>, ConsumerState<T>> {
getValue(): T {
if (this.context[contextProp]) {
// getChildContext returns the value this.context which is the react native API
return this.context[contextProp].get();
} else {
return defaultValue;
}
}
onUpdate = (newValue: any, changedBits: number) = > {
const observedBits: number = this.observedBits | 0;
if((observedBits & changedBits) ! = =0) {
this.setState({ value: this.getValue() }); }};render() {
return onlyChild(this.props.children)(this.state.value); }}Copy the code