То же самое относится и к HashRouter из react-router-dom.

внешний интерфейс React.js

1 Функция, которую необходимо реализовать

Когда мы используем React для разработки проектов, это в основном одностраничное приложение, и без роутинга нам не обойтись. Маршрутизация кажется загадочной, когда мы просто моделируем ее основные функции, мы обнаруживаем, что это то же самое. В этой статье будет подробно рассказаноreact-router-domизHashRouterосновная логика реализации.

Функции, реализованные в данной работе, в основном включают в себя:

  • HashRouter
  • Route
  • Link
  • MenuLink
  • Switch
  • Redirect

2 Реализованная логика

Не будем сначала говорить о том, как пишется код, а сначала посмотрим на картинку.HashRouterЧто за чертовщина:

HashRouter实现逻辑图1
Что ж, некоторые люди обязательно скажут, что это за круги, не беспокойтесь об этом, подождите, пока я медленно объясню:

  • HashRouterЭто большой контейнер, он управляет тем, как он рендерится, так чем он управляется?Вы можете догадаться из его названия, то естьwindow.location.hash.
  • когдаHashRouterКогда он начнет рендеринг, он займет свое собственноеpathnameАтрибуты такие же, как и в его животеRouteизpathСовпадение, если оно совпадает, оно будет отображатьсяRouteизcomponentсоответствующий компонент.
  • LinkКак переключать маршруты?Это очень просто, достаточно пройтиthis.props.history.push(path)изменитьHashRouterсерединаpathnameсвойства, которые, в свою очередь, приводятRoute们Повторите рендеринг, снова сопоставьте наш маршрут и, наконец, поменяйте маршруты.

Введена простая логика, теперь давайте посмотрим, как она реализована, как показано на следующем рисунке:

HashRouter实现逻辑图2

  • HashRouterявляется унаследованнымReact.Componentкласс, в этом классеstateвключатьlocation, мониторингhashизменения на дискеRouteповторный рендеринг компонента, а такжеhistoryАтрибут, вы можете переключить маршрутизацию страницы.
  • Функции, которые будут реализованы в этой статье, включаютRoute,Link,MenuLink,Switch,RedirectRouteОснова – это ядро,MenuLinkИ некоторые рендеры с определенной логикой находятся вRouteреализовано на основе .
  • RouteСуществует три типа переменных, которые можно получить в компоненте, в том числеcomponent,render,childrenrender,childrenвсе функции,renderзаключается в отображении элементов в соответствии с определенной логикой,childrenиспользуется для рендерингаMenuLink, обе функции получают текущий маршрутprops, возвращаемое значение функции — это отображаемый элемент.
  • SwitchРеализованная логика заключается в возвратеchildrenсредний каблукhashПервый "дочерний элемент", который соответствует.

3 Особая логика кода

(1) HashRouter

HashRouterБудуwindow.loacation.hashсо своимstateкрючок, изменив свой собственныйstateУскоряет повторный рендеринг страницы.

import React, {Component} from 'react';
import PropTypes from 'prop-types';

export default class HashRouter extends Component {
    constructor() {
        super();
        this.state = {
            location: {
                pathname: window.location.hash.slice(1) || '/', // 当前页面的hash值
                state: {}   //保存的状态
            }
        };
    }
    
    // 定义上下文的变量类型
    static childContextTypes = {
        location: PropTypes.object,
        history: PropTypes.object
    }
    
    // 定义上下文的变量
    getChildContext() {
        return {
            location: this.state.location,
            history: {
                push: (path) => { // 就是更新 window.hash值
                    if (typeof path === 'object') {
                        let {pathname, state} = path;
                        this.setState({
                            location: {
                                ...this.state.location,
                                state // {from: '/profile'}
                            }
                        }, () => {
                            window.location.hash = pathname;
                        })
                    } else {
                        window.location.hash = path;
                    }
                }
            }
        }
    }
    
    render() {
        return this.props.children; // 渲染页面元素
    }
    
    componentDidMount() {
        window.location.hash = window.location.hash.slice(1) || '/';
        // 监听window的hash的变化,驱动页面的重新刷新
        window.addEventListener('hashchange', () => {
            this.setState({
                location: {
                    ...this.state.location,
                    pathname: window.location.hash.slice(1) || '/'
                }
            });
        })
    }
}

(2) Route

RouteОсновная логика рендеринга заключается в преобразовании собственногоpathи текущая страницаhashВыполняется сопоставление, и соответствующий элемент отображается, если он соответствует, и ничего не отображается, если он не соответствует.

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import pathToRegexp from 'path-to-regexp'

export default class Route extends Component {
    // 定义上下文context的类型
    static contextTypes = {
        location: PropTypes.object,
        history: PropTypes.object
    }
    
    render() {
        // 解构传入Route的props
        let {path, component: Component, render, children} = this.props;
        
        // 解构上下文的属性
        let {location, history} = this.context;
        let props = {
            location,
            history
        };
        
        // 将传入Route的path和当前的hash进行匹配
        let keys = [];
        let regexp = pathToRegexp(path, keys, {end: false});
        keys = keys.map(key => key.name);
        
        let result = location.pathname.match(regexp);
        
        if (result) { // 匹配上了
            let [url, ...values] = result;
            props.match = {
                path,
                url,
                params: keys.reduce((memo, key, index) => { // 获取匹配到的参数
                    memo[key] = values[index];
                    return memo;
                }, {})
            };
            
            if (Component) { // 普通的Route
                return <Component {...props} />;
            } else if (render) { // 特定逻辑的渲染
                return render(props);
            } else if (children) { // MenuLink的渲染
                return children(props);
            } else {
                return null;
            }
        } else { // 没有匹配上
            if (children) { // MenuLink的渲染
                return children(props);
            } else {
                return null;
            }
        }
    }
}

(3) Redirect

RedirectПросто сделай одно, измениHashRouterизstate, драйвер выполняет повторную визуализацию.

import React, {Component} from 'react';
import PropTypes from 'prop-types';

export default class Redirect extends Component {
    // 定义上下文context的Type
    static contextTypes = {
        history: PropTypes.object
    }
    
    componentDidMount() {
        // 跳转到目标路由
        this.context.history.push(this.props.to);
    }
    
    render() {
        return null;
    }
}

(4) MenuLink

import React, {Component} from 'react';
import Route from "./Route";
import Link from './Link'

export default ({to, children}) => {
    // 如果匹配到了,就给当前组件一个激活状态的className
    return <Route path={to} children={props => (
        <li className={props.match ? "active" : ""}>
            <Link to={to}>{children}</Link>
        </li>
    )
    }/>
}

(5) Link

LinkОн должен отображаться в теге, затем давать событие щелчка и изменять его при нажатии.HashRouterсостояние, которое управляет повторным рендерингом.

import React, {Component} from 'react';
import PropTypes from 'prop-types';

export default class Link extends Component {
    static contextTypes = {
        history: PropTypes.object
    }
    
    render() {
        return (
            <a onClick={() => this.context.history.push(this.props.to)}>{this.props.children}</a>
        )
    }
}

(6) Switch

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import pathToRegexp from 'path-to-regexp';

export default class Switch extends Component {
    static contextTypes = {
        location: PropTypes.object
    }
    
    render() {
        let {pathname} = this.context.location;
        
        let children = this.props.children;
        for (let i = 0, l = children.length; i < l; i++) {
            let child = children[i];
            let path = child.props.path;
            
            if (pathToRegexp(path, [], {end: false}).test(pathname)) {
                // 将匹配到的第一个元素返回
                return child;
            }
        }
        return null
    }
}

4 написать в конце

Хорошо, эти функции были введены, вы правы?HashRouterВы понимаете принцип? В этой статье публикуется только часть кода, если он вам нужен, прочтите его.demoВы можете попробовать вручную.

использованная литература: