我已经创建了一个Web应用程序,该应用程序使用history
pushState
和replaceState
方法来浏览页面,同时更新历史记录。
脚本本身几乎可以完美运行;它将正确加载页面,并在需要抛出页面时抛出页面错误。但是,我注意到一个奇怪的问题,pushState
会将多个重复的条目(并替换之前的条目)推入历史记录。
例如,假设我(按顺序)执行以下操作:
加载index.php(历史记录为:索引)
导航至profile.php(历史记录为:配置文件,索引)
导航到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);
}
}
});
感谢所有帮助,
干杯。
答案 0 :(得分:3)
您有 onpopstate 处理程序吗?
如果是,请在此处检查是否未推送历史记录。在历史记录列表中删除/替换了某些条目可能是一个大信号。确实,请参见以下SO answer:
请记住,history.pushState()会将新状态设置为最新的历史状态。在已设置的状态之间(向后/向前)导航时,将调用window.onpopstate。
因此,在调用window.onpopstate时不要使用pushState,因为这会将新状态设置为最后一个状态,然后没有任何继续前进的地方。
我曾经遇到过与您描述的完全相同的问题,但这实际上是由我反复尝试理解该错误引起的,该错误最终会触发popState处理程序。然后从该处理程序中调用history.push。因此,最后,我还有一些重复的条目,而有些则缺少,没有任何逻辑上的解释。
在检查了一些条件之后,我将对history.push的调用删除,将其替换为history.replace,并且在工作后就像一个超级魅力:)
编辑-> 提示
如果您找不到正在调用履历的代码。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,并且可以向上查找并调用它的代码:)
答案 1 :(得分:0)
我在一个非常简单的应用程序中遇到了同样的问题,我只在代码中从本地调用window.history.pushState()。一切在FireFox中都运行良好,但存在您提到的重复/覆盖问题。原来解决方法是更改:
document.title = "My New Title";
window.history.pushState(null, null, '/some/path');
到
window.history.pushState(null, null, '/some/path');
document.title = "My New Title";
...现在在两个浏览器中都像梦一样运作。