Analysis of front-end router

Time:2022-1-3

Writing is not easy. Reprinting in any form is prohibited without the permission of the author!
If you think the article is good, you are welcome to pay attention, praise and share!
Blog original link: analysis of front-end routing

Introduction to front end routing

What front-end routing

The concept of routing first appeared in the back end. It navigates to the specific HTML through the URL requested by the user. The current front-end routing is different

Compared with traditional routing, it does not need server parsing. It can be implemented through hash function or history API. In front-end development, I

You can set the access path for routing, and switch the display of components according to the mapping relationship between the path and components. The whole process is the same

It is implemented in one ⻚ and does not involve the jump between ⻚ which is what we often call single ⻚ response (SPA).

What does front-end routing bring

In terms of multiple phase response (MPA), spa has the following advantages:

  • HTML ⻚⾯ jump is not involved. There is no need to reload ⻚⾯ for content change, and press ⼒⼩ on the server.
  • It only involves the switching between components, so the jump is smooth and the user experience is good.
  • ⻚⾯ the effect will be cool (⽐ such as transition animation when switching ⻚⾯ content).
  • Component development is convenient.

However, spa also has the following disadvantages:

  • ⾸ screen loading is too slow.
  • Not conducive to SEO.
  • The complexity is much higher.

⽤ original ⽣jsImplement front-end routing

What front-end routing

The concept of routing first appeared in the back end. It navigates to the specific HTML through the URL requested by the user. The current front-end routing is different from

Traditional routing does not require server parsing, but can be implemented through hash function or H5 history API. Front end development

In, we can enable ⽤ routing to set access paths and switch the display of components according to the mapping relationship between paths and components

It is implemented in the same ⻚ and does not involve the jump between ⻚ which is what we often call single ⻚ response (SPA).

Original ⽣jsImplement front-end routing

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>lesson2</title>
  </head>
  <body>
    <ul>
      <li>< a href = "# / home" > Home Page</a></li>
      <li>< a href = "# / user" > User Center</a></li>
      <li>< a href = "# / login" > login</a></li>
    </ul>

    <div id="view"></div>
  </body>

  <script>
    let view = null;
    window.addEventListener("DOMContentLoaded", onLoad);

    //Listen for hash changes
    window.addEventListener("hashchange", onHashChange);

    function onLoad() {
      view = document.getElementById("view");
      onHashChange();
    }

    function onHashChange() {
      switch (location.hash) {
        case "#/home":
          view. InnerHTML = "home page";
          break;
        case "#/user":
          view. InnerHTML = "user center";
          break;
        case "#/login":
          view. InnerHTML = "login";
          break;
      }
    }
  </script>
</html>

Introduction to environment configuration and react router

resources

  1. React official website
  2. react-router

target

  1. Master CRA environment
  2. Master the basic use of react router

Knowledge points

Quick start

npx create-react-app router-nut
cd router-nut
yarn start

Configure less and decorator

yarn add @craco/craco craco-less @babel/plugin-proposal-decorators

Add craco.com under the root directory config. JS file

//* remember to restart after configuration
const CracoLessPlugin = require("craco-less");

module.exports = {
  babel: {
    //Used to support the decorator
    plugins: [["@babel/plugin-proposal-decorators", {legacy: true}]]
  },
  plugins: [
    {
      plugin: CracoLessPlugin
    }
  ]
};

Modify package json

 "scripts": {
    "start": "craco start",
    "build": "craco build",
    "test": "craco test"
  },

Introduction to react router

The react router contains three libraries: react router, react router Dom and react router native. The react router provides the most basic routing function. In actual use, we will not directly install react router, but choose to install react router DOM (used in browser) or react router native (used in RN) according to the application running environment. Both react router Dom and react router native rely on react router. Therefore, during installation, react router will be automatically installed to create web applications.

install

yarn add react-router-dom

Browserrouter vs. hashrouter

  1. Hashrouter is the simplest. It does not need server-side rendering. It can distinguish paths by # the browser. Browserrouter needs the server to return different HTML for different URLs. The back-end configuration can be referred to.
  2. Browserrouter uses the HTML5 history API (pushstate, replacestate and popup events) to synchronize the UI of the page with the URL.
  3. Hashrouter does not support location Key and location State, the dynamic route jump needs to pass through? Pass parameters.
  4. Hash history can run without any configuration of the server. If you’re just getting started, use it. However, we do not recommend using it in the actual online environment, because every web application should be eager to use itbrowserHistory

MemoryRouter

Save the URL history in memory<Router>(do not read or write the address bar). Useful in test and non browser environments, such as react native.

Basic use

The idea that everything is a component is pursued in react router-Router, link-Link, routing-Route, exclusive-Switch, redirection-RedirectAll exist as components

import {BrowserRouter as Router, Route, Link} from "react-router-dom";
import HomePage from "./pages/HomePage";
import UserPage from "./pages/UserPage";
import LoginPage from "./pages/LoginPage";

function App() {
  return (
    <div className="App">
      <Router>
        < link to = "/" > Home Page < / link >
        < link to = "/ user" > User Center < / link >
        < link to = "/ login" > login < / link >

        {/ * add exact to the root route to achieve exact matching * /}
        <Route exact path="/" component={HomePage} />
        <Route path="/user" component={UserPage} />
        <Route path="/login" component={LoginPage} />
      </Router>
    </div>
  );
}

export default App;

Three ways route renders content

resources

  1. React official website
  2. react-router

target

  1. Master three ways of route rendering
  2. Master 404 routing

Knowledge points

Route rendering priority: children > component > render.

The three can receive the same [route props], including match, location and history, but when they do not match, the children’s match is null.

These three methods are mutually exclusive. You can only use one.

import React, {useState} from "react";
import {BrowserRouter as Router, Route, Link, Switch} from "react-router-dom";
import HomePage from "./pages/HomePage";
import UserPage from "./pages/UserPage";
import LoginPage from "./pages/LoginPage";
import _404Page from "./pages/_404Page";

function App() {
  const [count, setCount] = useState(0);
  return (
    <div className="App">
      <button
        onClick={() => {
          setCount(count + 1);
        }}>
        add: {count}
      </button>
      <Router>
        < link to = "/" > Home Page < / link >
        < link to = "/ user" > User Center < / link >
        < link to = "/ login" > login < / link >
        {/ * exclusive route * /}
        <Switch>
          <Route
            path="/"
            exact
            //children={children}
            component={HomePage}

            // !  React will be called when rendering the component CreateElement. If the following anonymous function is used, a new anonymous function will be generated each time,
            // !  As a result, the types of generated components are always different. At this time, repeated uninstallation and mounting will occur
            //component={() => <HomePage />}

            // render={render}
          />
          <Route path="/user" component={UserPage} />
          <Route path="/login" component={LoginPage} />
          <Route component={_404Page} />
        </Switch>
      </Router>
    </div>
  );
}

export default App;

function children(props) {
  console.log("children props", props); //sy-log
  return <div>children</div>;
}

function render(props) {
  console.log("props props", props); //sy-log
  return <div>render</div>;
}

children:func

Sometimes, no matter whether the location matches or not, you need to render some content. At this time, you can use children.

Except that it will be rendered regardless of whether the location matches or not, other working methods are exactly the same as render.

import React, {Component} from "react";
import ReactDOM from "react-dom";
import {BrowserRouter as Router, Link, Route} from "react-router-dom";

function ListItemLink({to, name, ...rest}) {
  return (
    <Route
      path={to}
      children={({match}) => (
        <li className={match ? "active" : ""}>
          <Link to={to} {...rest}>
            {name}
          </Link>
        </li>
      )}
    />
  );
}

export default class RouteChildren extends Component {
  render() {
    return (
      <div>
        <h3>RouteChildren</h3>
        <Router>
          <ul>
            < listitemlink to = "/ somewhere" name = "link 1" / >
            < listitemlink to = "/ somewhere else" name = "link 2" / >
          </ul>
        </Router>
      </div>
    );
  }
}

render:func

But when you use render, all you call is a function. But like component, it can access all [route props].

import React from "react";
import ReactDOM from "react-dom";
import {BrowserRouter as Router, Route} from "react-router-dom";

//Convenient inline rendering
ReactDOM.render(
  <Router>
    <Route path="/home" render={() => <div>Home</div>} />
  </Router>,
  node
);

// wrapping/composing
//Pass the route parameter to your component
function FadingRoute({component: Component, ...rest}) {
  return (
    <Route {...rest} render={routeProps => <Component {...routeProps} />} />
  );
}

ReactDOM.render(
  <Router>
    <FadingRoute path="/cool" component={Something} />
  </Router>,
  node
);

component: component

Render only when the location matches.

be careful

When you usecomponentRoute will use your specified components and react CreateElement creates a new [react element]. This means that when you provide an inline function, each render will create a new component. This will result in not updating existing components, but uninstalling and then mounting a new component. Therefore, when using inline rendering of inline functions, use render or children.

The core rendering code of route is as follows:

Analysis of front-end router

404 page

Set a route without path at the end of the route list, indicating that it must match

<Switch>
  <Route path="/" exact component={HomePage} />
  <Route path="/user" component={UserPage} />
  <Route path="/login" component={LoginPage} />
  <Route component={_404Page} />
</Switch>

Dynamic routing

resources

  1. React official website
  2. react-router

target

  1. Dynamic routing

Knowledge points

Dynamic routing

Dynamic routing is defined in the form of: ID

Define route:

<Route path="/product/:id" component={Product} />

Add navigation link:

< link to = {"/ product / 123"} > search < / link >

Create the search component and get the parameters:

function Product({location, match}) {
  console.log("match", match); //sy-log
  const {id} = match.params;
  return <h1>Product-{id}</h1>;
}

Nested Route

resources

  1. React official website
  2. react-router

(bid)

  • Master nested routing

Knowledge points

Nested Route

The route component is nested in other ⻚ components, resulting in a nested relationship

Modify product, add new and details

<Route path={url + "/detail"} component={Detail} />
function Product({match}) {
    console.log("match", match); //sy-log
    const {params, url} = match;
    const {id} = params;
    return (
        <div> 
            <h1>Search-{id}</h1> 
            < link to = {URL + "/ detail"} > details < / link > 
            <Route path={url + "/detail"} component={Detail} />
        </div> );
}

⼿ write implementationBrowserRouterRouteLink

resources

  1. React official website
  2. react-router

(bid)

  • Preliminary implementation of react router

Knowledge points

Cross level data transmission context

import React from "react";

//Cross level data transmission using context
//Step1: create context object
export const RouterContext = React.createContext();

//Step 2: use the provider of the context object to pass value

//Step3: subcomponent consumption value: consumer, usecontext, contexttype

realizationRouter

import React, {Component} from "react";
import {RouterContext} from "./Context";

export default class Router extends Component {
    static computeRootMatch(pathname) {
        return {path: "/", url: "/", params: {}, isExact: pathname === "/"};
    }

    constructor(props) {
        super(props);
        this.state = {
            location: props.history.location
        };

        this.unlisten = props.history.listen(location => {
            this.setState({
                location
            });
        });
    }

    componentWillUnmount() {
        if (this.unlisten) {
            this.unlisten();
        }
    }

    render() {
        return (
            <RouterContext.Provider
                value={{
                    history: this.props.history,
                    location: this.state.location,
                    match: Router.computeRootMatch(this.state.location.pathname)
                }}>
                {this.props.children}
            </RouterContext.Provider>
        );
    }
}

realizationBrowserRouter

BrowserRouter: History initialization and downward transfer of history management object, location change listening

import React, {Component} from "react";
import {createBrowserHistory} from "history";
import Router from "./Router";

export default class BrowserRouter extends Component {
    constructor(props) {
        super(props);
        this.history = createBrowserHistory();
    }

    render() {
        return <Router history={this.history}>{this.props.children}</Router>;
    }
}

realizationRoute

Routing configuration, matching detection, content rendering

//Match gives priority to rendering according to the mutual exclusion rule. The order of rendering is children component render null. If children are functions, they are directly rendered by nodes
//Do not match children or null (render function only)

export default class Route extends Component {
    render() {
        return (
            <RouterContext.Consumer>
                {context => {
                    //Take priority from props
                    const location = this.props.location || context.location;
                    //Take priority from props计算
                    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;
                    //Match renders one of the three: children component render or null
                    //Do not match, render children or null
                    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}
                        </RouterContext.Provider>
                    );
                }}
            </RouterContext.Consumer>);
    }
}

realizationLink

Link. JS: jump links and handle click events

import React from "react";
import {RouterContext} from "./RouterContext";


export default function Link({to, children, ...restProps}) {
  const context = React.useContext(RouterContext);
  const handleClick = e => {
    e.preventDefault();
    context.history.push(to);
  };
  return (
    <a href={to} {...restProps} onClick={handleClick}>
      {children}
    </a>
  );
}

realizationSwitch

Render the ⼀ th node < route > or < redirect > matching the address.

import React, {Component, isValidElement} from "react";
import {RouterContext} from "./Context";
import matchPath from "./matchPath";

export default class Switch extends Component {
    render() {
        return (
            <RouterContext.Consumer>
                {context => {
                    const {location} = context;
                    let match, element;
                    // children element | array
                    React.Children.forEach(this.props.children, child => {
                        if (match == null && React.isValidElement(child)) {
                            element = child;
                            const {path} = child.props;
                            match = path
                                ? matchPath(location.pathname, child.props)
                                : context.match;
                        }
                    });
                    return match
                        ? React.cloneElement(element, {computedMatch: match})
                        : null;
                }}
            </RouterContext.Consumer>
        );
    }
}

Analysis of front-end routing

Nuggets: front end LeBron

Zhihu: front end LeBron

Continue to share technical blog, pay attention to WeChat official account.

Analysis of front-end router

Recommended Today

Vue2 technology finishing 3 – Advanced chapter – update completed

3. Advanced chapter preface Links to basic chapters:https://www.cnblogs.com/xiegongzi/p/15782921.html Link to component development:https://www.cnblogs.com/xiegongzi/p/15823605.html 3.1. Custom events of components 3.1.1. Binding custom events There are two implementation methods here: one is to use v-on with vuecomponent$ Emit implementation [PS: this method is a little similar to passing from child to parent]; The other is to use ref […]