我发现此 bug 也会导致浏览历史记录中的历史条目有些损坏,我认为这是一个更严重的问题。更具体地说,当导航由 JS 处理时,旧 URL 会被分配一个新标题。在 Firefox 中几乎总是会发生这种情况,但在 Chrome 中却很少见(到目前为止我只发现了一个实例:从 /top 进入一个主题)。
@sam 说得对,这是由修改 URL 和标题的操作顺序引起的。我希望有某种最佳实践指南发布在某个地方,说明正确的顺序是什么:始终在historyAPI 之后更新标题。
我尝试进行了一些调试(我想提前说明,我非常不愿构建任何中等规模的 npm 相关 项目)。最初,我使用了以下代码进行了一些侦察:
Object.defineProperty(document, "title", {
get() {
return document.head.querySelector('title').innerText;
},
set(t) {
console.debug('title', t, new Error("title!").stack)
debugger;
document.head.querySelector('title').innerText = t;
},
enumerable: true,
configurable: true,
});
let ___push___ = window.history.pushState;
window.history.pushState = function(...args) {
console.debug('push', args[0], new Error().stack);
debugger;
___push___.apply(this, args);
};
let ___replace___ = window.history.replaceState;
window.history.replaceState = function(...args) {
console.debug('replace', args[0], new Error().stack);
debugger;
___replace___.apply(this, args);
};
它确实表明,在historyAPI 操作之前,几乎总会有一个标题修改。不幸的是,它没有提供更多帮助:将运行时发生的事情与源代码联系起来非常困难——感兴趣的操作分散得相当厉害,调用堆栈具有误导性,因为执行已经让步给了 Ember 的事件循环,而且总的来说,Firefox 中的调试体验非常糟糕。切换到 Chrome 后,我取得了一些成功。长话短说,据我所知,Ember 会自行决定何时更新 URL。另一方面,标题的修改可以从两个地方发起:[1] 和 [2]
只有后者在historyAPI 调用之前执行。它们“共同的代码祖先”位于此处:
// Run all the necessary enter/setup/exit hooks
this.setupContexts(newState, transition); // <-- title change initiated here, by triggering `model.title:change` event
// Check if a redirect occurred in enter/setup
if (transition.isAborted) {
// TODO: cleaner way? distinguish b/w targetRouteInfos?
this.state!.routeInfos = this.currentRouteInfos!;
return Promise.reject(logAbort(transition));
}
this._updateURL(transition, newState); // <-- url change initiated here
有趣的是,“router.js 是 Ember.js 使用的路由微库。”
现在我想将处理此问题的工作交给一个与 discourse 代码库有远程熟悉的人,而不是我。
编辑:忘记提了:document.title在(两者)被修改[2]和 URL 被 history API 修改之后,被[1]修改了一次或多次。