Backbone.js 防止主干驱动SPA内的页面导航

Backbone.js 防止主干驱动SPA内的页面导航,backbone.js,browser-history,pushstate,backbone-routing,Backbone.js,Browser History,Pushstate,Backbone Routing,理由 在我的BB应用程序中,我允许来自用户的快速输入,这些用户在后台定期排队并发送到服务器。我目前遇到的问题是,如果用户离开页面,他们会有效地放弃队列中任何挂起的更改 因此,基本上我想做的是在用户离开之前通知他们,让他们有机会等待更改被保存,而不仅仅是退出和放弃 本质 因此,对于用户刷新或尝试导航到外部URL的一般情况,我们可以处理该事件。当我们处于SPA环境中时,页面之间的切换不会导致页面刷新,这就变得有点棘手了 我的直接想法是对所有锚使用全局单击事件处理程序,并验证是否允许单击,这将适用于站

理由

在我的BB应用程序中,我允许来自用户的快速输入,这些用户在后台定期排队并发送到服务器。我目前遇到的问题是,如果用户离开页面,他们会有效地放弃队列中任何挂起的更改

因此,基本上我想做的是在用户离开之前通知他们,让他们有机会等待更改被保存,而不仅仅是退出和放弃

本质

因此,对于用户刷新或尝试导航到外部URL的一般情况,我们可以处理该事件。当我们处于SPA环境中时,页面之间的切换不会导致页面刷新,这就变得有点棘手了

我的直接想法是对所有锚使用全局单击事件处理程序,并验证是否允许单击,这将适用于站点内链接导航。然而,这是通过浏览器的后退/前进按钮进行导航

我还看了一眼,乍一看,它似乎正是我所需要的。但是,使用中描述的简单案例,路由仍在执行中

问题


如何在主干SPA中拦截所有场景的导航?

直接链接导航

使用全局事件处理程序捕获所有单击事件

$(document).on('click', 'a[href^="/"]', function (e) {
    var href = $(e.currentTarget).attr('href');
    e.preventDefault();
    if (doSomeValidation()) {
        router.navigate(href, { trigger: true });
    }
});
页面刷新/外部URL导航

窗口上处理
onbeforeuload
事件

$(window).on('beforeunload', function (e) {
    if (!doSomeValidation()) {
        return 'Leaving now will may result in data loss';
    }
});
浏览器后退/前进按钮导航

幕后使用最终利用的。根据您传递给的选项以及浏览器的功能,API将挂接到
onhashchange
事件或
onpopstate
事件

深入研究主干.history.start的源代码很明显,无论您是否使用push state,都会使用相同的事件处理程序,即

因此,我们可以重写此方法并在其中执行验证

var originalCheckUrl = Backbone.history.checkUrl;
Backbone.history.checkUrl = function (e) {
    if (doSomeValidation()) {
        return originalCheckUrl.call(this, e);
    } else {
        // re-push the current page into the history (at this stage it's been popped)
        window.history.pushState({}, document.title, Backbone.history.fragment);
        // cancel the original event
        return false;
    }
};
var originalCheckUrl = Backbone.history.checkUrl;
Backbone.history.checkUrl = function (e) {
    if (doSomeValidation()) {
        return originalCheckUrl.call(this, e);
    } else {
        // re-push the current page into the history (at this stage it's been popped)
        window.history.pushState({}, document.title, Backbone.history.fragment);
        // cancel the original event
        return false;
    }
};