[javascript] 중복 기록 항목을 작성하고 이전 항목을 겹쳐 쓰는 pushState

기록을 업데이트하면서 페이지를 탐색하기 위해 history pushStatereplaceState메소드 를 사용하는 웹 앱을 만들었습니다 .

스크립트 자체는 거의 완벽하게 작동합니다. 페이지를 올바르게로드하고 던져야 할 때 페이지 오류를 발생시킵니다. 그러나 pushState여러 중복 항목을 기록하고 이전 항목을 교체 하는 이상한 문제가 기록에 있습니다.

예를 들어 다음을 순서대로 수행한다고 가정 해 보겠습니다.

  1. index.php를 불러 오십시오 (이력 : Index)

  2. profile.php로 이동합니다 (이력 : Profile, Index).

  3. search.php로 이동합니다 (기록은 검색, 검색, 색인입니다).

  4. dashboard.php로 이동

그런 다음 마지막으로 내 역사에서 가장 최근에 나온 것입니다.

대시
보드
대시 보드 대시 보드
검색
색인

이것의 문제점은 사용자가 앞으로 또는 뒤로 단추를 클릭 할 때 잘못된 페이지로 리디렉션되거나 다시 한 번 돌아가려면 여러 번 클릭해야한다는 것입니다. 그들이 가서 역사를 확인하면 말이되지 않습니다.

이것이 내가 지금까지 가진 것입니다.

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);
        }
    }
});

모든 도움을 주셔서 감사
합니다.



답변

당신은 가지고 있습니까 onpopstate 핸들러를?

그렇다면, 역사를 추진하지 않는지도 확인하십시오. 히스토리 목록에서 일부 항목이 제거 / 대체되는 것은 큰 신호일 수 있습니다. 실제로이 SO 답변을 참조하십시오 :

history.pushState ()는 새로운 상태를 최신 기록 상태로 설정합니다. 그리고 window.onpopstate는 설정 한 상태 사이를 탐색 (뒤로 / 앞으로) 할 때 호출됩니다.

따라서 window.onpopstate가 호출 될 때 pushState를 수행하지 마십시오. 이렇게하면 새 상태가 마지막 상태로 설정되고 앞으로 이동할 것이 없습니다.

한 번 설명 한 것과 똑같은 문제가 있었지만 실제로 버그를 이해하려고 시도하여 결국 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도 놓치지 않을 것이고, 그것을 호출하는 코드를 찾을 수 있습니다 🙂


답변