Maison  >  Article  >  interface Web  >  Analyse détaillée de la bibliothèque d'historique principale dans React Router

Analyse détaillée de la bibliothèque d'historique principale dans React Router

不言
不言original
2018-08-14 11:01:563098parcourir

Cet article vous présente une analyse détaillée de la bibliothèque d'historique de base de React Router. Elle a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer.

Avant-propos

Utiliser React pour développer des applications légèrement plus complexes. React Router est presque le seul choix pour la gestion du routage. Bien que React Router ait subi quatre mises à jour de version majeures et que ses fonctions soient devenues de plus en plus abondantes, quelle que soit la manière dont il évolue, sa dépendance fondamentale à l'égard de la bibliothèque d'historique n'a pas changé. Jetons un coup d'œil aux fonctions fournies par cette bibliothèque avec plus de 4 000 étoiles sur github.

Objet historique HTML5

En parlant de bibliothèque d'histoire, pensez-vous que ce mot vous est un peu familier ? Oui, un nouvel objet historique portant le même nom a également été ajouté à la spécification HTML5. Jetons un coup d'œil aux problèmes que cet objet d'historique est utilisé pour résoudre.

À l'époque où jQuery dominait le front-end, la mise à jour de la page sans actualisation via une requête ajax était une méthode de traitement de page très populaire à cette époque. Le prototype de SPA évoluait à cette époque. Afin de marquer les modifications apportées à la page afin que les éléments corrects de la page puissent toujours être affichés après l'actualisation, la valeur de hachage de l'URL est généralement modifiée pour localiser la page de manière unique. Mais cela soulève un autre problème : les utilisateurs ne peuvent pas utiliser avant/arrière pour changer de page.

Afin de résoudre ce problème, l'objet historique a vu le jour. Lorsque l'URL ou le hachage de la page change, le navigateur insère automatiquement la nouvelle URL dans l'objet historique. Un tableau d'état sera conservé à l'intérieur de l'objet historique pour enregistrer les modifications apportées à l'URL. Lorsque le navigateur effectue des opérations avant/arrière, il appelle en fait la méthode correspondante de l'objet historique (forward/back) pour récupérer l'état correspondant pour changer de page.

En plus d'exploiter l'URL, l'objet historique fournit également deux méthodes qui peuvent mettre à jour l'état interne sans utiliser l'URL, à savoir pushState et replaceState. Vous pouvez également stocker des données supplémentaires dans l'état, puis les récupérer via onpopstate dans l'événement event.state. Si vous souhaitez une compréhension plus approfondie de l'objet historique, vous pouvez vous référer à ici et ici.

La relation entre la bibliothèque d'historique et l'objet d'historique HTML5

Retournons et regardons la bibliothèque d'historique. Il fait essentiellement les 4 choses suivantes :

  1. Apprendre du concept d'objet historique HTML5 et étendre certaines fonctions basées sur celui-ci

  2. Fournit 3 types d'historique : browserHistory, hashHistory, memoryHistory et maintient une API unifiée

  3. Prend en charge la fonction de publication/abonnement lorsque l'historique change, la fonction d'abonnement peut être automatiquement déclenchée

  4. Fournit des fonctions pratiques telles que l'interception de saut, la confirmation de saut et le nom de base

Comparez certaines similitudes et différences entre les deux API. Ce qui suit est la bibliothèque d'historique :

const history = {
    length,        // 属性,history中记录的state的数量
    action,        // 属性,当前导航的action类型
    location,      // 属性,location对象,封装了pathname、search和hash等属性
    push,          // 方法,导航到新的路由,并记录在history中
    replace,       // 方法,替换掉当前记录在history中的路由信息
    go,            // 方法,前进或后退n个记录
    goBack,        // 方法,后退
    goForward,     // 方法,前进
    canGo,         // 方法,是否能前进或后退n个记录
    block,         // 方法,跳转前让用户确定是否要跳转
    listen         // 方法,订阅history变更事件
  };
Ce qui suit est l'objet d'historique HTML5 :

const history = {
    length,         // 属性,history中记录的state的数量
    state,          // 属性,pushState和replaceState时传入的对象
    back,           // 方法,后退
    forward,        // 方法,前进
    go,             // 方法,前进或后退n个记录
    pushState,      // 方法,导航到新的路由,并记录在history中
    replaceState    // 方法,替换掉当前记录在history中的路由信息
}

// 订阅history变更事件
window.onpopstate = function (event) {
    ...
}
Comme le montre la comparaison, la relation entre les deux est très étroite, et la bibliothèque d'histoire peut être considérée comme un surensemble de l'objet historique et un objet historique plus puissant.

Analyse du code source createHashHistory

Ci-dessous, nous prenons l'un des trois types d'historique, hashHistory, comme exemple pour analyser le code source de l'historique et voir ce qu'il fait. Voyons d'abord comment il gère les changements de hachage.

// 构造hashHistory对象
const createHashHistory = (props = {}) => {
    ...
    const globalHistory = window.history;    // 引用HTML5 history对象
    ...
    // transitionManager负责控制是否进行跳转,以及跳转后要通知到的订阅者,后面会详细讨论
    const transitionManager = createTransitionManager();
    ...
    // 注册history变更回调的订阅者
    const listen = listener => {
        const unlisten = transitionManager.appendListener(listener);
        checkDOMListeners(1);

        return () => {
            checkDOMListeners(-1);
            unlisten();
        };
    };
    
    // 监听hashchange事件
    const checkDOMListeners = delta => {
        listenerCount += delta;

        if (listenerCount === 1) {
            window.addEventListener(HashChangeEvent, handleHashChange);
        } else if (listenerCount === 0) {
            window.removeEventListener(HashChangeEvent, handleHashChange);
        }
    };
    
    // hashchange事件回调
    const handleHashChange = () => {
        ...
        // 构造内部使用的location对象,包含pathname、search和hash等属性
        const location = getDOMLocation();    
        ...
        handlePop(location);
    };
    
    // 处理hash变更逻辑
    const handlePop = location => {
        ...
        const action = "POP";
        // 给用户展示确认跳转的信息(如果有的话),确认后通知订阅者。如果用户取消跳转,则回退到之前状态
        transitionManager.confirmTransitionTo(location, action, getUserConfirmation, ok => {
            if (ok) {
                setState({action, location});    // 确认后通知订阅者
            } else {
                revertPop(location);             // 取消则回退到之前状态
            }
        });
    };
    
    // 更新action,location和length属性,并通知订阅者
    const setState = nextState => {
        Object.assign(history, nextState);

        history.length = globalHistory.length;

        transitionManager.notifyListeners(history.location, history.action);
    };
    ...
}
Ce qui précède est la logique de gestion des changements de hachage passifs. En une phrase, cela peut être résumé comme suit : abonnez-vous à l'événement de changement de hachage, déterminez si le changement est vraiment nécessaire, mettez à jour vos propres attributs. si une modification est nécessaire, et informez l'abonné. Pas besoin. Les modifications sont rétablies à l'état précédent.

Jetons un coup d'œil à ce que fait transitionManager. Concentrez-vous sur le contenu lié à la publication/l'abonnement et ignorez le contenu lié au saut de confirmation de l'utilisateur.

const createTransitionManager = () => {
    ...
    // 内部维护的订阅者列表
    let listeners = [];

    // 注册订阅者
    const appendListener = fn => {
        let isActive = true;

        const listener = (...args) => {
            if (isActive) fn(...args);
        };

        listeners.push(listener);

        return () => {
            isActive = false;
            listeners = listeners.filter(item => item !== listener);
        };
    };

    //通知订阅者
    const notifyListeners = (...args) => {
        listeners.forEach(listener => listener(...args));
    };
    ...
}
Le code ici est clair en un coup d'œil, il s'agit de maintenir une liste d'abonnés et de notifier les fonctions concernées lorsque le hachage change.

Ce qui précède est le contenu lié à la mise à jour passive lorsque le hachage change. Jetons un coup d'œil au code lié à la mise à jour active. En prenant

comme exemple, push est similaire. replace

const push = (path, state) => {
    ...
    const action = "PUSH";
    const location = createLocation(path, undefined, undefined, history.location);

    transitionManager.confirmTransitionTo(location, action, getUserConfirmation, ok => {
        if (!ok)     // 如果取消,则不跳转
            return;
        ...
        pushHashPath(encodedPath);        // 用新的hash替换到url当中
        ...
        setState({action, location});     // 更新action,location和length属性,并通知订阅者

    });
};

// 用新的hash替换到url当中
const pushHashPath = path => (window.location.hash = path);
Lorsque le navigateur effectue des opérations avant et arrière, la bibliothèque d'historique est en fait implémentée en exploitant l'objet historique HTML5.

const globalHistory = window.history;

const go = n => {
    ...
    globalHistory.go(n);
};

const goBack = () => go(-1);

const goForward = () => go(1);
Lorsque

est appelé, le hachage changera, déclenchant l'événement hashchange, puis la bibliothèque d'historique informera les abonnés concernés du changement. window.history.go

Résumé

Cet article fournit une introduction plus approfondie à la bibliothèque d'historique dont dépend le cœur de React Router. À partir du nouvel objet history en HTML5, nous comparons sa relation inextricable avec la bibliothèque d'historique et utilisons hashHistory comme exemple pour analyser en détail les détails d'implémentation de son code.

Enfin, passons en revue ce que la bibliothèque d'historique a fait :

  1. Apprenons du concept d'objet historique HTML5 et développons certaines fonctions basées sur celui-ci

  2. Fournit 3 types d'historique : browserHistory, hashHistory, memoryHistory et maintient une API unifiée

  3. Prend en charge la fonction de publication/abonnement, lorsque l'historique change d'heure, la fonction qui peut déclencher automatiquement l'abonnement

  4. Fournit des fonctions pratiques telles que l'interception de saut, la confirmation de saut et le nom de base

Bien que la bibliothèque d'historique soit la dépendance principale de React Router , mais il ne dépend pas de React lui-même. Si votre projet comporte un scénario de manipulation d'historique, vous pouvez également l'introduire dans le projet.

Recommandations associées :

Comment utiliser h5 pour implémenter des composants de tri par glisser-déposer (avec code)

Comment HTML5 résout le problème d'effondrement de margin-top (Code ci-joint)

Quelles sont les balises et les règles communes en HTML5 ? Introduction aux balises et règles html5


Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn