기록을 업데이트하면서 페이지를 탐색하기 위해 history
pushState
및 replaceState
메소드 를 사용하는 웹 앱을 만들었습니다 .
스크립트 자체는 거의 완벽하게 작동합니다. 페이지를 올바르게로드하고 던져야 할 때 페이지 오류를 발생시킵니다. 그러나 pushState
여러 중복 항목을 기록하고 이전 항목을 교체 하는 이상한 문제가 기록에 있습니다.
예를 들어 다음을 순서대로 수행한다고 가정 해 보겠습니다.
-
index.php를 불러 오십시오 (이력 : Index)
-
profile.php로 이동합니다 (이력 : Profile, Index).
-
search.php로 이동합니다 (기록은 검색, 검색, 색인입니다).
-
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도 놓치지 않을 것이고, 그것을 호출하는 코드를 찾을 수 있습니다 🙂