.back和.pushState - 两个历史的故事

For*_*rdi 6 javascript html5 history

这不是一个问题,而是一个有趣问题的结果.它也是一种"从我的失败中学习"

我正在尝试为IE浏览器编写HTML5历史记录的单元测试(使用window.hash替代状态维护).duckpunch按预期工作,在用户测试期间,我在IE,Chrome和Firefox中获得了一致的结果.

问题出在哪里就是单元测试.在其中我执行history.pushState(),. renplaceState,.back()和.forward()的各种组合.这些在Firefox和IE中运行良好,但Chrome给了我彻底不一致的结果.以下答案解释了原因.

For*_*rdi 9

考虑以下:

var originalPath = window.location.pathname.toString();
history.pushState({ value: 'Foo' }, '', 'state-1');
history.pushState({ value: 'Bar' }, '', 'state-2');

history.pushState({ value: 'Baz' }, '', 'state-3');
history.back();
history.back();
console.log(history.state.value);
//So we can hit refresh and see the results again...
setTimeout(function () {
    history.replaceState(null, '', originalPath);
}, 250);
Run Code Online (Sandbox Code Playgroud)

人们会期望这段代码返回'Foo' - 在Firefox和我的IE浏览器中,这正是它的作用 - 但在Chrome中,它以'Baz'响应.

经过一番调查后,我解决了这个问题:IE和Firefox同步更新历史记录,如果需要加载任何页面,则转为异步.Chrome似乎立即异步.

证据:

window.onpopstate = function () {
    console.log('ping');
}
history.pushState({ value: 'Foo' }, '', 'state-1');
history.back();
console.log('pong');
Run Code Online (Sandbox Code Playgroud)

在Firefox中,这会返回'ping'; 'pong' - 表示事件是作为history.back()调用的一部分调度的.在Chrome中,这会返回'pong'; 'ping' - 表示事件被放入队列中进行调度.

如果这个事件调度模型没有被用来管理历史和位置对象的状态,那就不会那么糟糕了 - 但显然是这样.

window.onpopstate = function () {
    console.log('Event...', history.state, window.location.pathname.toString());
}
history.pushState({ value: 'Foo' }, '', 'state-1');
history.back();
console.log('Inline...', history.state, window.location.pathname.toString());
Run Code Online (Sandbox Code Playgroud)

这是一个有趣的怪癖,需要使用jQuery Deferred链来正确地进行单元测试.我对此并不特别高兴,但你能做什么?


daw*_*daw 2

为了在单元测试中处理异步返回事件,我使用了HistoryJSJasmine。这是更新历史事件中的计数器以跟踪 chrome 处理事件的时间和 Jasmine 的异步支持以阻止单元测试的情况,直到我们看到事件更改:

增加计数器:

History.Adapter.bind(window, 'statechange', function() {

    // Increment the counter
    window.historyEventCounter++;

});
Run Code Online (Sandbox Code Playgroud)

Jasmine 异步单元测试。waitsFor 将阻塞,直到历史事件发生:

describe('back navigation', function () {
    it('should change url', function () {

        var initialHistoryEventCount;

        // Cache the initial count

        runs(function() {
            initialHistoryEventCount = window.historyEventCounter;
            History.back();
        });

        // Block until the event counter changes

        waitsFor(function() {
            return (initialHistoryEventCount !== app.historyEventCounter);
        }, "The page should be navigated back", 1000);

        // Now confirm the expected back behaviour. Event failures will time-out.

        runs(function() {
            expect(window.location.href).toEqual("http:// my back url");

            // ... more tests about the page state
        });
    }
}
Run Code Online (Sandbox Code Playgroud)