Before the introduction

This article comes from Liu Shuang, a student from the front end team of Push-ah. It mainly introduces the front-end routing related content and interprets the source code of React-Router. Feel free to discuss in the comments section!!

What is routing

The term route is usually heard in network engineering. Front-end engineering refers to the concept of back-end routing. When the browser detects the change of the route, it displays the page corresponding to the route. In the early days, the concept of routing was the process of rerendering the page layout and content based on URL changes, and this process was implemented on the server side. What he describes is the mapping between urls and functions.In single-page applications on the web front end, routing describes the mapping between the URL and the UI. The remarkable feature of this mapping isURL changes do not cause page refreshes.支那

Two routing modes

The purpose of URL change is to update THE UI. At the same time, the page cannot be refreshed. If we want to update the page view UI, we need to listen for URL changes. So there are two things to consider when implementing a routing engine on the front end

  1. URL changes do not brush the page,
  2. How do I listen for URL changes

There are two ways to implement these standards in the front end

Hash routing

Hash routes are often referred to as anchors. That is, add a # to the URL, and after the # is the hash routing part. You can listen for hash route changes by listening for events

window.addEventListener("hashchange".function(){
	console.log("Route change")})window.onhashchange = function(){
	console.log("Route change")}Copy the code

The history of routing

The history object represents the navigation record of the user in the current window. This object does not expose the URLS that the user has visited, but can be used to move forward and backward through methods. HTML5 adds new methods pushState and replaceState to add and replace historical entries. The syntax is as follows.

history.pushState(state, title[, url]) history.replaceState(state, title[, url]) - state: A state object associated with a specified url that is passed into the callback function when the popState event is triggered. If this object is not needed, fill in herenull- title: Most browsers currently ignore this parameter. - URL: Specifies the URL of the new history entry. The new url must be identical with the current urlCopy the code

Use POPState to listen for route changes

window.addEventListener('popstate'.function(){
	console.log("Route change")})Copy the code

React-Router

In this column we only discuss the source code, which corresponds to version 5.2 history: responsible for the browser page, the link change notifys the current page that the location object has changed, and the developer renders the content based on the change. Router: monitors the changes of the page object and starts to render the page again. Route: Displays the content corresponding to the specific Route address based on the location information of the page after the page is rendered.

BrowserRouter and Router

When you first use the React-Router, you need the following methods:

import { createBrowserHistory } from 'history'
import { Router } from 'react-router'

const BrowserRouter = React.cloneElement(Router, { history: createBrowserHistory() })

export default() = > (<BrowserRouter>.</BrowserRouter>
)
/ / or

export default() = > (<Router history={createBrowserHistory()}>.</Router>
)
Copy the code

After react-Router V4, you can directly use BrowserRouter as shown below

import { BrowserRouter } from 'react-router-dom'

export default() = > (<BrowserRouter>.</BrowserRouter>
)
Copy the code

As you can see, BrowserRouter is a layer of encapsulation of the Router component, passing in the History property. The main part of the Router source code

// packages/react-router-dom/modules/BrowserRouter.js

class BrowserRouter extends React.Component {
  history = createHistory(this.props);

  render() {
    return <Router history={this.history} children={this.props.children} />; }}Copy the code
// packages/react-router/modules/Router.js
class Router extends React.Component {
  static computeRootMatch(pathname) {
    return { path: "/".url: "/".params: {}, isExact: pathname === "/" };
  }

  constructor(props) {
    super(props);

    this.state = {
      // BrowserRouter The history passed in
      location: props.history.location
    };

    if(! props.staticContext) {// Listen for URL changes
      this.unlisten = props.history.listen(location= > {
        // This does not affect the overall logic}); }}// Use context to pass information such as history location to children
  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>); }}Copy the code

** Summary: ** Based on the source code, you can see that Router processing content is not much, 1. Define a context to pass relevant information to children; 2. Listen for URL changes to change the current state.

Route

The source code for Route is also quite simple, matching the page to be displayed based on the information passed in, as shown below

// packages/react-router/modules/Route.js
class Route extends React.Component {
  // Consumer context, which is used to take over the previous incoming information
  render() {
    return( <RouterContext.Consumer> {context => { invariant(context, "You should not use <Route> outside a <Router>"); / / the location information If the users to use the incoming information, otherwise, use the Provider to const location = this. Props. The location | | context. The location; // Match: {path,url,isExact,params} 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; // There are some judgments to be skipped... let { children, component, render } = this.props; // Start rendering children, <Route exact path="/" component={Home} /> // 2. <Route exact path="/" component={Home} /> // 2. <Route exact path="/" render={props=>{return <Home />}}  /> // 3. <Route exact path="/"><Home /> </Route> return ( <RouterContext.Provider value={props}> {props.match ? children ? typeof children === "function" ? children(props) : children : component ? React.createElement(component, props) : render ? render(props) : null : typeof children === "function" ? children(props) : null} </RouterContext.Provider> ); }} </RouterContext.Consumer> ); }}Copy the code

** Summary: **Route is used to render components, based on the matching URL, the path property of the Route, to match the content to render. There are three ways to render Component Children. It uses triadic operations in it, and it’s very long, but it’s not hard to understand. This is the logic of rendering children. If there are no children, judge whether there is a component, and finally judge whether there is a render method.

Switch

If a Route component is wrapped by a Switch, then the matched URL returns the first matched element of the wrapped Route. The core code is as follows:

// packages/react-router/modules/Switch.js
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;
Copy the code

**Switch **Switch **Switch **Switch **Switch **

Conclusion:

Overall, the react-Router source code is not difficult to understand, it is a few simple judgments. Compared with this article, there is no introduction to how to use the React-Router, just a simple interpretation of the source code. The next article will implement a simple version of the React-Router. Implement the API described earlier.