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


15

기록을 업데이트하면서 페이지를 탐색하기 위해 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);
        }
    }
});

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


대상이 마지막 항목과 다른 경우에만 히스토리 변경 사항을 전파 하시겠습니까?
Aluan Haddad

이런 일이 무작위로 발생합니까? 또는 특정 페이지가 항상 중복 기록 항목을 발생시키는 페이지입니까?
SamVK

@AluanHaddad 아니오 히스토리 변경이있는 경우 (즉, 사용자가 사이트를 탐색 할 때) 히스토리 변경 사항 만 전파하려고합니다. 일반적인 상황에서 일어날 일과 비슷합니다 ... (IndexOverflow에서 검색하기 위해 인덱스에서 프로파일로 이동하면 내 기록에서 정확하게 반환됩니다)
GROVER.

@SamVK 초기로드 페이지 (예 : index.php)에서 탐색 한 후 어떤 페이지가 중요하지 않더라도 항목은 스스로 복제됩니다.
GROVER.

1
글쎄, 나는 의견에 글을 쓰는 것에 대해 확실하지 않습니다. updateHistory함수 에 브라우저 기록을 추가하고 있음을 알았습니다 . 이제 updateHistoryTraveler ( window.Traveller = new Traveller();, constructor-> init-> send-> render-> updateHistory)를 초기화 할 때 eventListener redirect에서 두 번 호출 될 수 있습니다 click. 나는 그것을 시험하지 않았고, 단지 거친 추측이기 때문에 답이 아닌 주석으로 추가했습니다.
Akshit Arora

답변:


3

당신은 가지고 있습니까 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도 놓치지 않을 것이고, 그것을 호출하는 코드를 찾을 수 있습니다 :)


나는 할 수 있지만, 밀거나 전혀 역사를 대체하지 않습니다 / 그냥 페이지를 렌더링
그로버합니다.

정말 이상합니다. 다음 코드로 history.push 함수를 덮어 쓰십시오 . const hP = history.pushState history.pushState = (loc) => {디버거; hP (loc)} 및 history.replaceState에 대해 동일하게 수행하십시오. 그런 다음 중단 점이 분류 될 때마다 호출 스택을 살펴보십시오
Bonjour123

통화 스택을 확인했는데 모든 것이 올바르게 완료된 것으로 보이지만 기록은 작동하지 않습니다. 모질라가 왜 모든 것을 그렇게 어렵게 만들어야 하는가 :(
GROVER.

감사합니다 :) 그리고 Chrome을 사용하면 어떤 결과가 있습니까?
Bonjour123
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.