在iOS 9测试版中,引入了一个错误(https://openradar.appspot.com/22186109),这意味着将window.location.hash
设置为某个新值,直到"某个时间"后来。 "某些时间的确切含义"是争论,但似乎至少要到下一个js事件循环滴答。
举个例子,你会做这样的事情:
location.hash = "abcd";
console.log(location.hash); // probably not "abcd"
setImmediate(function () {
console.log(location.hash); // probably "abcd"
});
这在我们的网站中引起了一个问题,即使用带有hashchange导航的backbone.js路由器。想象一下,在这个例子的开头,路线是' foo'和之前的' foo'在历史上是' quux'。
我们这样做:
router.navigate('bar', { trigger: true, replace: false });
将调用backbone.js历史类并调用路由器来处理导航:
function onFooRoute() {
router.navigate('baz', { trigger: true, replace: true });
}
所以我们开始了#foo',去了' bar'然后去了巴斯' (对于最后一个,将replace设置为true)。
我们希望我们应该最终选择“baz”,然后回到历史会让我们回到“foo”。
我们看到的问题是完成了所有这些,一旦所有事情都安定下来,通过window.history.back()
返回历史记录,我们就会回到' quux'而不是' foo'。看来,由于这个bug,条目' bar'不会进入浏览器历史记录堆栈,因此导航到' baz'取代' foo'而不是' bar'。
我的问题是如何最好地解决这个问题?我可以修补backbone.js来解决这个问题吗?
答案 0 :(得分:0)
我们通过覆盖骨干中的几种方法解决了这个问题:
function isIOS9UIWebView() {
var userAgent = window.navigator.userAgent;
return /(iPhone|iPad|iPod).* OS 9_\d/.test(userAgent) && !/Version\/9\./.test(userAgent);
}
//override the backbone.history.loadUrl() and backbone.history.navigate()
//to fix the navigation issue (location.hash not change immediately) on iOS9
if (isIOS9UIWebView()) {
Backbone.history.loadUrl = function (fragment?: any, oldHash?: any) {
fragment = this.fragment = this.getFragment(fragment);
return _.any(this.handlers, function (handler: any): boolean {
if (handler.route.test(fragment)) {
function runCallback() {
handler.callback(fragment);
}
function wait() {
if (oldHash === location.hash) {
window.setTimeout(wait, 50);
} else {
runCallback();
}
}
wait();
return true;
}
});
};
Backbone.history.navigate =
// Attempt to load the current URL fragment. If a route succeeds with a
// match, returns `true`. If no defined routes matches the fragment,
// returns `false`.
function (fragment, options) {
var pathStripper = /#.*$/;
if (!Backbone.History.started) return false;
if (!options || options === true) options = { trigger: !!options };
var url = this.root + (fragment = this.getFragment(fragment || ''));
// Strip the hash for matching.
fragment = fragment.replace(pathStripper, '');
if (this.fragment === fragment) return;
this.fragment = fragment;
// Don't include a trailing slash on the root.
if (fragment === '' && url !== '/') url = url.slice(0, -1);
var oldHash = location.hash;
// If pushState is available, we use it to set the fragment as a real URL.
if (this._hasPushState) {
this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
// If hash changes haven't been explicitly disabled, update the hash
// fragment to store history.
} else if (this._wantsHashChange) {
this._updateHash(this.location, fragment, options.replace);
if (this.iframe && (fragment !== this.getFragment(this.getHash(this.iframe)))) {
// Opening and closing the iframe tricks IE7 and earlier to push a
// history entry on hash-tag change. When replace is true, we don't
// want this.
if (!options.replace) this.iframe.document.open().close();
this._updateHash(this.iframe.location, fragment, options.replace);
}
// If you've told us that you explicitly don't want fallback hashchange-
// based history, then `navigate` becomes a page refresh.
} else {
return this.location.assign(url);
}
if (options.trigger) return this.loadUrl(fragment, oldHash);
}
}