PushState tạo các mục lịch sử trùng lặp và ghi đè lên các mục trước đó


15

Tôi đã tạo một ứng dụng web sử dụng các phương thức history pushStatereplaceStateđể điều hướng qua các trang trong khi cập nhật lịch sử.

Kịch bản tự hoạt động gần như hoàn hảo; nó sẽ tải các trang một cách chính xác và ném lỗi trang khi chúng cần được ném. Tuy nhiên, tôi đã nhận thấy một vấn đề kỳ lạ là pushStatesẽ đẩy nhiều mục, trùng lặp (và thay thế các mục trước đó) vào lịch sử.

Ví dụ: giả sử tôi làm như sau (theo thứ tự):

  1. Tải lên index.php (lịch sử sẽ là: Index)

  2. Điều hướng đến profile.php (lịch sử sẽ là: Hồ sơ, Chỉ mục)

  3. Điều hướng đến search.php (lịch sử sẽ là: Tìm kiếm, Tìm kiếm, Lập chỉ mục)

  4. Điều hướng đến dashboard.php

Cuối cùng, đây là những gì sẽ xuất hiện trong lịch sử của tôi (theo thứ tự gần đây nhất đến cũ nhất):

Bảng điều khiển Bảng
điều
khiển Chỉ mục
tìm kiếm

Vấn đề với điều này là khi người dùng nhấp vào nút chuyển tiếp hoặc quay lại, họ sẽ được chuyển hướng đến trang không chính xác hoặc phải nhấp nhiều lần để quay lại một lần. Điều đó, và sẽ vô nghĩa nếu họ đi và kiểm tra lịch sử của họ.

Đây là những gì tôi có cho đến nay:

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);
        }
    }
});

Tất cả sự giúp đỡ được đánh giá cao,
Chúc mừng.


Vì vậy, bạn chỉ muốn tuyên truyền một sự thay đổi lịch sử nếu mục tiêu khác với mục cuối cùng?
Aluan Haddad

Đây có phải là xảy ra ngẫu nhiên? Hoặc là các trang cụ thể luôn là những trang gây ra các mục lịch sử trùng lặp.
SamVK

@AluanHaddad no Tôi chỉ muốn tuyên truyền thay đổi lịch sử nếu có thay đổi lịch sử (nghĩa là khi người dùng đang điều hướng qua trang web). Tương tự như những gì sẽ xảy ra trong một tình huống bình thường ... (đi từ chỉ mục đến hồ sơ để tìm kiếm trên StackOverflow sẽ trả lại chính xác điều đó trong lịch sử của tôi)
GROVER.

@SamVK Không quan trọng trang nào, sau khi điều hướng xung quanh từ trang tải lên ban đầu (ví dụ: index.php), các mục sẽ tự sao chép.
LỚP.

1
Vâng, tôi không hoàn toàn chắc chắn về nó để viết bình luận. Tôi thấy rằng bạn đang thêm những thứ lịch sử trình duyệt trong updateHistorychức năng của bạn . Bây giờ, updateHistorycó thể được gọi hai lần, lần thứ nhất, khi bạn đang khởi tạo Traveller ( window.Traveller = new Traveller();, constructor-> init-> send-> render-> updateHistory), sau đó cũng redirecttừ từ clickeventListener. Tôi đã không kiểm tra nó, chỉ là phỏng đoán hoang dã, vì vậy thêm nó dưới dạng một nhận xét và không phải là một câu trả lời.
Akshit Arora

Câu trả lời:


3

Bạn có một trình xử lý onpopstate ?

Nếu có, thì hãy kiểm tra ở đó nếu bạn không đi vào lịch sử. Một số mục được xóa / thay thế trong danh sách lịch sử có thể là một dấu hiệu lớn. Thật vậy, xem câu trả lời SO này :

Hãy nhớ rằng history.pushState () đặt trạng thái mới là trạng thái lịch sử mới nhất. Và window.onpopstate được gọi khi điều hướng (lùi / tiến) giữa các trạng thái mà bạn đã đặt.

Vì vậy, đừng PushState khi window.onpopstate được gọi, vì điều này sẽ đặt trạng thái mới là trạng thái cuối cùng và sau đó không có gì để tiếp tục.

Tôi đã từng có chính xác vấn đề giống như bạn mô tả, nhưng thực ra là do tôi quay lại và tiến lên để cố gắng hiểu lỗi, cuối cùng sẽ kích hoạt trình xử lý popState. Từ trình xử lý đó, sau đó nó sẽ gọi history.push. Vì vậy, cuối cùng, tôi cũng có một số mục trùng lặp, và một số thiếu, không có bất kỳ lời giải thích hợp lý.

Tôi đã xóa cuộc gọi đến history.push, thay thế nó bằng history.replace sau khi kiểm tra một số điều kiện và sau khi nó hoạt động như một bùa mê :)

CHỈNH SỬA -> MIPO

Nếu bạn không thể xác định được mã nào đang gọi history.pushState:

Hãy thử bằng cách ghi đè các hàm history.pushState và thay thếState bằng mã sau:

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);
    }
}

Sau đó, mỗi khi các điểm dừng được chuyển hướng, hãy xem ngăn xếp cuộc gọi.

Trong bảng điều khiển, nếu bạn muốn ngăn PushState, chỉ cần nhập allowPush = false;hoặc allowReplace = false;trước khi tiếp tục. Bằng cách này, bạn sẽ không bỏ lỡ bất kỳ history.pushState nào và có thể đi lên và tìm mã gọi nó :)


Tôi làm, nhưng nó hoàn toàn không thúc đẩy hoặc thay thế lịch sử: / chỉ hiển thị trang
GROVER.

Thật kỳ lạ. Hãy thử ghi đè hàm history.push bằng mã sau: const hP = history.pushState history.pushState = (loc) => {debugger; hP (loc)} và làm tương tự cho history.replaceState. Sau đó, mỗi khi các điểm dừng được chuyển hướng, hãy xem ngăn xếp cuộc gọi
Bonjour123

Tôi đã kiểm tra ngăn xếp cuộc gọi và mọi thứ dường như được thực hiện chính xác - nhưng lịch sử không muốn hoạt động. Tại sao Mozilla phải làm mọi thứ trở nên khó khăn như vậy :(
GROVER.

Cảm ơn :) Và nếu bạn thử trên Chrome, bạn có kết quả gì?
Bonjour123
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.