The introduction

Routing, which renders different components based on different paths, can be implemented in two ways:

HashRouter: Uses hash to switch routes BrowserRouter: uses H5 API to switch routesCopy the code

Implement the React-Router by hand and thoroughly understand the principle of the React-Router. I will do it in this order.

  • Two ways to implement routing
  • Implement the react – the router – the dom
  • Implement Router and Route
  • Implement hashHistory
  • Realize the browserHistory
  • The application of regular expressions
  • Implement matchPath
  • To realize the Switch
  • Redirect and Link are implemented
  • Implement nested routines by
  • Protected route
  • Implement NavLink
  • Implement WithRouter, Prompt
  • Implement hooks in react-Router

Props Route parameters: history: historical object location: path object match: matching parameter

Implementation of hashRouter

function createHashHistory() {
    let action;
    const listeners = [];
    const historyStack = [];/ / history stack
    let historyIndex = -1;
    let state;
    function listen(listener){
        listeners.push(listener);
        return () = > {
            const index = listeners.indexOf(listener);
            listeners.splice(index, 1); }}const hashChange = () = > {
        let pathname = window.location.hash.slice(1);
        Object.assign(history, { action, location: { pathanme, state }})
    }
    if(! action || action ==='PUSH') {
        historyStack[++historyIndex] = history.location;
    }else if(action === 'REPLACE'){
        historyStack[historyIndex] = history.location;
    }
    listeners.forEach(listener= > listener(history.location));
}
window.addEventListener('hashChange', hashChange);

function push(pathname, nextState) {
/* history.push('user', {name: 'tom'}) ; History. Push ({pathname: '/ user', state: {name: "user management"}}) * /
    action = 'PUSH';
    if(typeof pathname === 'object') {
        state = pathname.state;
        pathname = pathname.pathname;
    }else {
        state = nextState;
    }
    window.location.hash = pathname;
}

function replace(pathname, nextState) {
    if(typeof pathname === 'object') {
        pathname = pathname.pathname;
        state = pathname.state;
    }else {
        state = nextState;
    }
    window.location.hash = pathname;
}

/* Go is implemented through the history stack */

function go(n) {
    action = 'POP';
    historyIndex += n;
    const nextLocation = historyStack[historyIndex];
    state = nextLocation.state;
    window.location.hash = nextLocation.pathname;
}

function goBack(-1) {
    go(-1);
}

function goForward() {
    go(1);
}
/* History in hashHistory has no compatibility issues */
const history = {
    action: 'POP'.location: { pathname: "/".state: undefined },
    go,
    goBack,
    goForward,
    push,
    replace,
    listen
}
action = 'PUSH';
if(window.location.hash) {
    hashChange();
}else {
    window.location.hash = '/';
}
return history;
Copy the code

How browserHistory is implemented

Using THE H5 API to implement route switching, the HTML5 specification provides us with a history interface, history provides us with two methods and an event: History.pushstate () and history.replacEstate (), window.onpopState.

History. pushState(stateObject, title, url), contains three parameters. The first parameter stores the stateObject corresponding to the url, which can be retrieved in the onpopstate event, or in the history object. The second parameter is the title, The third parameter is the set URL pushState and pushes a record of the set URL onto the browser's history stack, ReplaceState (stateObject, title, URL) and change the current pointer to the top of the history stack: This interface is the same as pushState, The only difference is that replaceState replaces the current history in the browser's history stack with the specified URL. Note that replaceState does not alter the current pointer to the browser's history stack, history. onpopState, which is a property of the window This event is triggered by calls to the browser's forward and back, as well as history.forward, history.back, and history.go, which modify the current pointer to the history stack without changing document. An onPopState event is emitted whenever the current pointer changesCopy the code
function createBrowserHistory() {
    const globalHistory = window.history;
    const listeners = [];
    let action, state, message;
    
    function go(n) {
        globalHistory.go(n);
    }
    
    function goForward() {
        go(1);
    }
    function goBack(n) {
        go(-1);
    }
    function listen(listener) {
        listeners.push(listener);
        return () = > {
            let index = listeners.indexOf(listener);
            listeners.splice(index, 1); }}function setState(newState) {
        Object.assign(history, newState);
        listeners.forEach(listener= > listener(history.location));
    }
    function push(pathname, nextState) {
        action = 'PUSH';
        if(typeof pathname === 'object') {
            state = pathname.state;
            pathname = pathname.pathname;
        }else { state = nextState }
        if(messgage) {
        let showMessage = message({ pathname });
        let allow = window.confirm(showMessage);
        if(! allow)return;
    }
    globalHistory.pushState(state, null, pathname);
    let location = { state, pathname };
    setState({ action, location });
    }
    /* This function is used when backing up or moving forward. The browser supports */ by default
    window.onpopstate = (event) = > {
        setState({ action: 'POP'.location: { pathname: window.location.pathname, state: globalHistory.state }})
    }
    function block(newMessage) {
        message = newMessage;
        return () = > message = null;
    }
    const history = {
        action: 'POP';
        location: { pathname: window.location.pathname, state: globalHistory.state },
        go,
        goForward,
        goBack,
        push,
        listen,
        block
    }
    return history;
}
Copy the code

Implement matchPath

import pathToRegexp from 'path-to-regexp';

const cache = {};

function compilePath(path, options = {}) {
    let cacheKey = path + JSON.stringify(options);
    if (cache[cacheKey]) return cache[cacheKey];

    const keys = [];// Process path parameters
    const regexp = pathToRegexp(path, keys, options);
    let result = { keys, regexp };
    cache[cacheKey] = result;
    return { keys, regexp };
}
/* pathname: the actual pathname of the browser. Route Props Path Component Exact Path Path of the Route Exact Matching strict Matching Sensitive Case sensitive */
/* Matches the path to match */
function matchPath(pathname, options = {}) {
    let { path = '/', exact = false, strict = false, sentive = false } = options;
    let { keys, regexp } = compilePath(path, { end: exact, strict, sentive });
    const match = regexp.exec(pathname);
    if(! match)return null;
    const [url, ...values] = match;
    const isExact = pathname === url;
    /* Exact but not exact */
    if(exact && ! isExact)return null;
    return {
        path,// The path from Route
        url,// From the url in the browser address
        isExact,// Is an exact match
        params: keys.reduce((memo, key, index) = > {
            memo[key.name] = values[index];
            returnmemo; }}}, {})export default matchPath;
Copy the code
  • CustomEvent events: use the customEvent constructor
window.addEventListener('eventName'.(e) = > {
    console.log('111', e);
})
let event = new CustomEvent('eventName', {
    detail: {
    message: 'Hello World'}})window.dispatchEvent(event);
Copy the code

Use of regular expressions

  • Path-to-regext converts the path to a re that matches the real path

Regular expression visualization tools: Jex.im/Regulex /#! F…

  • Nested routines do not require additional code in the source code, Route is already supported
For the rest, see:Github.com/China-forre…