pushState создает повторяющиеся записи истории и перезаписывает предыдущие

15

Я создал веб - приложение , которое использует history pushStateи replaceStateметоды для того , чтобы перемещаться по страницам , а также обновление истории.

Сам скрипт работает почти идеально; он будет загружать страницы правильно, и выдает ошибки страниц, когда их нужно выбросить. Тем не менее, я заметил странную проблему, когда pushStateв историю помещаются несколько повторяющихся записей (и заменяются записи перед ними).

Например, допустим, я делаю следующее (по порядку):

  1. Загрузите index.php (история будет: Index)

  2. Перейдите к profile.php (история будет: Профиль, Индекс)

  3. Перейдите к search.php (история будет: Поиск, Поиск, Индекс)

  4. Перейдите к dashboard.php

И, наконец, вот что всплывет в моей истории (в порядке от самых последних до самых старых):

Приборная панель
Приборная панель
Приборная панель
Search
Index

Проблема заключается в том, что когда пользователь нажимает кнопки «вперед» или «назад», они либо перенаправляются на неправильную страницу, либо вынуждены нажимать несколько раз, чтобы вернуться назад один раз. Это, и не будет никакого смысла, если они пойдут и проверит свою историю.

Это то, что я до сих пор:

var Traveller = function(){
    this._initialised = false;

    this._pageData = null;
    this._pageRequest = null;

    this._history = [];
    this._currentPath = null;
    this.abort = function(){
        if(this._pageRequest){
            this._pageRequest.abort();
        }
    };
    // initialise traveller (call replaceState on load instead of pushState)
    return this.init();
};

/*1*/Traveller.prototype.init = function(){
    // get full pathname and request the relevant page to load up
    this._initialLoadPath = (window.location.pathname + window.location.search);
    this.send(this._initialLoadPath);
};
/*2*/Traveller.prototype.send = function(path){
    this._currentPath = path.replace(/^\/+|\/+$/g, "");

    // abort any running requests to prevent multiple
    // pages from being loaded into the DOM
    this.abort();

    return this._pageRequest = _ajax({
        url: path,
        dataType: "json",
        success: function(response){
            // render the page to the dom using the json data returned
            // (this part has been skipped in the render method as it
            // doesn't involve manipulating the history object at all
            window.Traveller.render(response);
        }
    });
};
/*3*/Traveller.prototype.render = function(data){
    this._pageData = data;
    this.updateHistory();
};
/*4*/Traveller.prototype.updateHistory = function(){
    /* example _pageData would be:
    {
        "page": {
            "title": "This is a title",
            "styles": [ "stylea.css", "styleb.css" ],
            "scripts": [ "scripta.js", "scriptb.js" ]
        }
    }
    */
    var state = this._pageData;
    if(!this._initialised){
        window.history.replaceState(state, state.title, "/" + this._currentPath);
        this._initialised = true;
    } else {
        window.history.pushState(state, state.title, "/" + this._currentPath);  
    }
    document.title = state.title;
};

Traveller.prototype.redirect = function(href){
    this.send(href);
};

// initialise traveller
window.Traveller = new Traveller();

document.addEventListener("click", function(event){
    if(event.target.tagName === "a"){
        var link = event.target;
        if(link.target !== "_blank" && link.href !== "#"){
            event.preventDefault();
            // example link would be /profile.php
            window.Traveller.redirect(link.href);
        }
    }
});

Вся помощь приветствуется,
ура.

GROVER.
источник
Таким образом, вы хотите распространять изменение истории, только если цель отличается от последней записи?
Алуан Хаддад
Это происходит на первый взгляд случайно? Или конкретные страницы всегда вызывают повторяющиеся записи в истории.
SamVK
@AluanHaddad нет. Я хочу распространять изменение истории только в том случае, если есть изменение истории (т. Е. Когда пользователь перемещается по сайту). Как и в обычной ситуации ... (переход от индекса к профилю к поиску в StackOverflow вернул бы именно то, что было в моей истории)
GROVER.
@SamVK Не имеет значения, на какой странице после перехода с начальной страницы загрузки (т. Е. Index.php) записи будут дублироваться.
Гровер.
1
Ну, я не совсем уверен в этом, поэтому пишу в комментариях. Я вижу, что вы добавляете вещи из истории браузера в свою updateHistoryфункцию. Теперь, updateHistoryможет быть, вызывается дважды, во-первых, когда вы инициализируете Traveler ( window.Traveller = new Traveller();, constructor-> init-> send-> render-> updateHistory), затем также redirectиз clickEventListener. Я не проверял это, просто дикие догадки, поэтому добавлял это как комментарий, а не как ответ.
Акшит Арора

Ответы:

3

У вас есть обработчик onpopstate ?

Если да, то проверьте там также, если вы не настаиваете на истории. То, что некоторые записи удалены / заменены в списке истории, может быть большим признаком. В самом деле, посмотрите этот так ответ :

Имейте в виду, что history.pushState () устанавливает новое состояние как новейшее состояние истории. И window.onpopstate вызывается при навигации (назад / вперед) между состояниями, которые вы установили.

Поэтому не нажимайте pushState при вызове window.onpopstate, так как это установит новое состояние в качестве последнего состояния, а затем не к чему идти.

Однажды у меня была точно такая же проблема, как вы описали, но на самом деле это было вызвано тем, что я возвращался назад и вперед, чтобы попытаться понять ошибку, которая в конечном итоге вызовет обработчик popState. Из этого обработчика он будет вызывать history.push. Итак, в конце у меня также было несколько дублированных записей, а некоторые отсутствовали без какого-либо логического объяснения.

Я удалил вызов history.push, заменил его на history.replace после проверки некоторых условий и после того, как он заработал как брелок :)

РЕДАКТИРОВАТЬ -> СОВЕТ

Если вы не можете определить, какой код вызывает history.pushState:

Попробуйте перезаписать функции history.pushState и replaceState следующим кодом:

window.pushStateOriginal = window.history.pushState.bind(window.history);
window.history.pushState = function () {
    var args = Array.prototype.slice.call(arguments, 0);
    let allowPush  = true;
    debugger;
    if (allowPush ) {
        window.pushStateOriginal(...args);
    }
}
//the same for replaceState
window.replaceStateOriginal = window.history.replaceState.bind(window.history);
window.history.replaceState = function () {
    var args = Array.prototype.slice.call(arguments, 0);
    let allowReplace  = true;
    debugger;
    if (allowReplace) {
        window.replaceStateOriginal(...args);
    }
}

Затем каждый раз, когда точки останова срабатывают, взгляните на стек вызовов.

В консоли, если вы хотите предотвратить pushState, просто введите allowPush = false;или allowReplace = false;перед возобновлением. Таким образом, вы не пропустите ни одного файла history.pushState, а сможете подняться и найти код, который его вызывает :)

Bonjour123
источник
Я делаю, но это не толкает и не заменяет историю вообще: / просто отображает страницу
GROVER.
Очень странно. Попробуйте переписать функцию history.push с помощью следующего кода: const hP = history.pushState history.pushState = (loc) => {debugger; hP (loc)} и сделать то же самое для history.replaceState. Затем каждый раз, когда точки останова срабатывают, посмотрите на стек вызовов
Bonjour123
Я проверил стек вызовов, и все, кажется, сделано правильно - но история не хочет работать. Почему Мозилла должна сделать все так сложно :(
Гровер.
Спасибо :) А если вы попробуете на Chrome, какие у вас результаты?
Bonjour123