Lịch sử javascript

Ứng dụng một trang [SPA] là một trang web hoạt động dựa trên việc kết xuất lại nội dung trang web theo chỉ định của người dùng [vd khi người dùng nhấp vào 1 liên kết] mà không thực hiện yêu cầu lên máy chủ để tìm nạp lại toàn bộ HTML cho trang . Nghe thì có vẻ trìu tượng, và trên thực tế thì có rất nhiều cách để thực hiện công việc implement 1 single-page app, tuy nhiên nhìn chung lại thì hầu hết chúng ta đều được xây dựng dựa trên những bộ API gốc và cơ chế của . Hiểu được những điều này chính là yếu tố cốt lõi để nắm được nguyên tắc hoạt động của 1 ứng dụng một trang

Phân loại SPA ?

1 Ứng dụng một trang có thể quản lý trạng thái dựa trên nguồn bên ngoài [ví dụ địa chỉ URL] hoặc tự nó có cơ chế quản lý trạng thái riêng. So sánh 2 loại với nhau, 1 SPA sử dụng cách thứ hai [internal state] có một hạn chế - đó là nó chỉ có duy nhất 1 đầu vào [entry] - cách khác là bạn chỉ có thể truy cập vào ứng dụng từ . Trong quá trình người dùng điều hướng trong ứng dụng, sẽ không có cách nào để thực hiện sự thay đổi bên ngoài trình duyệt. Điều này dẫn đến một số hạn chế, ví dụ như trường hợp sử dụng bạn muốn chia sẻ 1 nội dung nào đó, khi đó người được chia sẻ cũng chỉ có thể truy cập từ gốc và bạn lại phải mất công giải thích cách

Với cách tiếp cận thứ nhất - SPA dựa trên vị trí - bạn có thể chia sẻ một đường liên kết cho người khác và có thể chắc chắn rằng bất cứ ai truy cập vào đường liên kết đó cũng sẽ thấy được một nội dung như nhau [giả sử

Vì vậy, bài viết này sẽ tập trung vào cơ chế quản lý nhà nước dựa trên vị trí

Vị trí mồi

Khi địa chỉ URL là thứ mà người dùng cuối nhìn thấy và tương tác, thì ứng dụng SPA sẽ hoạt động với hàm

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
0. Hàm này cho phép ta tách rời và làm việc với từng phần của địa chỉ URL mà không phải phân tích cú pháp tay

Đối với một ứng dụng SPA, chỉ có 3 phần trong địa chỉ URL là quan trọng.

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
1,
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
2 và
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
3 [hay còn được gọi là chuỗi truy vấn], còn
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
4 hay
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
5 thì có thể bỏ qua

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
1 là phần quan trọng nhất trong 3 phần vì nó quyết định xem nội dung nào được hiển thị.
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
7hash
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
8/images?of=mountains
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
9/images
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
00?of=mountains` sẽ quy định thêm nội dung nào có thể hiển thị trong trang hình ảnh đó

Kết hợp tuyến đường

SPA phụ thuộc vào bộ định tuyến. Bộ định tuyến là một tập hợp danh sách các tuyến đường, mỗi tuyến đường sẽ tương ứng với một vị trí mà nó phù hợp với

Một tuyến đường có thể được xác định [

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
01] hoặc có thể bao gồm các thành phần động [
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
02 - với
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
03 là giá trị bất kỳ]

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
4

Trong quá trình người dùng điều hướng trong ứng dụng, vị trí sẽ được so sánh để tìm ra tuyến đường tương ứng với mình [thường chỉ là so sánh phần

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
1 của vị trí]. Sau khi tìm được tuyến đường tương ứng, bộ định tuyến sẽ thực hiện nhiệm vụ kết xuất lại ứng dụng nội dung cho người dùng [việc kết xuất lại có nhiều cách, một số trong đó là mô phỏng theo mẫu quan sát - LTV sẽ cung cấp cho bộ định tuyến một chức năng có nhiệm vụ kết xuất

Điều hướng trong ứng dụng

việc điều hướng bên trong ứng dụng cũng có một vấn đề thú vị cần giải quyết. Khi người dùng nhấp vào một đường dẫn [thường là 1 thẻ

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
05], thông thường các trình duyệt đều có các hành vi mặc định gắn với một sự kiện để thực hiện điều hướng. Với 1 SPA, ta sẽ muốn ghi đè lại hành vi mặc định này của trình duyệt [có thể sử dụng hàm
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
06 của JS - hay như các SPA framework hiện nay đều đã hỗ trợ mặc định cho vấn đề này]. Lúc này, hành vi mặc định của trình duyệt sẽ bị ghi đè lên, vị trí không còn tự động thay đổi nữa;

Tuy nhiên, có thể bạn vẫn cần biết một chút về cách mà trình duyệt mặc định xử lý điều hướng

Trình duyệt xử lý vị trí như thế nào?

Mỗi trình duyệt tab có một thứ gọi là "bối cảnh trình duyệt". Bối cảnh trình duyệt được quản lý thứ cấp một "lịch sử phiên" - về bản chất là một mảng

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
07

Một mục này sẽ chứa các thông tin về một vị trí. URL của vị trí,

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
08 tương ứng với vị trí, trạng thái đã được đăng nhiều kỳ, cũng như một vài thuộc tính khác. Mỗi mục nhập đều có một chỉ mục đi kèm theo thứ tự của nó trong mảng lịch sử phiên. Bối cảnh trình duyệt cũng giữ thông tin về mục nhập hiện đang được sử dụng

Tài liệu?

Khi trình duyệt thực hiện điều hướng, một yêu cầu sẽ được gửi tới máy chủ và trình duyệt sử dụng phản hồi nhận về để tạo một đối tượng

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
08. This object description page [ cây DOM của trang. ] và các phương thức hàm để tương tác với nó. Ví dụ, hàm
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
10 chính là hàm để tương tác với
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
08 của mục nhập vị trí hiện tại

Lịch sử phiên

Môi trường khi người dùng nhấp vào một liên kết và điều hướng, trình duyệt tab sẽ tích hợp thêm vào lịch sử phiên. Mỗi 1 điều hướng sẽ tạo một yêu cầu tới máy chủ và tạo một mục nhập mới [bao gồm 1

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
08]

Khi người dùng nhấn nút quay lại trình duyệt, trình duyệt sẽ sử dụng mục nhập hiện tại để xác định mục nhập mới [

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
13].
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
08 của mục nhập trước đó sẽ được tải lại vào trình duyệt

Lúc này, khi người dùng nhấp vào một liên kết, các mục nào nằm phía sau mục hiện tại [những trang trước khi quay lại] sẽ bị xóa và thay thế bởi mục mới

Trong trường hợp người dùng điều hướng đến đúng trang hiện tại [vị trí mới có cùng

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
1,
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
3 và
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
2 với vị trí hiện tại], mục hiện tại sẽ bị thay thế mà không ảnh hưởng gì đến các mục khác

Trên đây là cơ chế hoạt động của điều hướng, tuy nhiên, đích đến của 1 SPA đó là thực hiện điều hướng mà không cần phải yêu cầu tới máy chủ. Vậy làm cách nào để SPA thực hiện được công việc đó?

API lịch sử

Ban đầu, SPA hoạt động dựa trên cơ chế là ta có thể thay đổi

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
2 của vị trí và trình duyệt sẽ tạo mục nhập vị trí mới mà không cần gửi yêu cầu tới máy chủ. Cách này hoạt động, nhưng không đẹp cho lắm. p Sau này, nguyên một bộ API mới được xây dựng - History API - với mục đích hỗ trợ tận răng cho việc phát triển SPA

Thay vì khởi động chắc chắn là một

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
08 với mỗi vị trí, API lịch sử sẽ sử dụng lại
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
08 hiện tại, chỉ cập nhật nó cho phù hợp với vị trí mới

API lịch sử có 3 chức năng chính.

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
21,
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
22 và
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
23. 3 hàm này [và các hàm còn lại của API lịch sử] đều có thể được gọi là thông qua
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
24

Ghi chú. về vấn đề hỗ trợ - tất cả các trình duyệt mới nhất hiện nay đều đã hỗ trợ History API. Phiên bản IE nhỏ hơn 9 không hỗ trợ, nhưng ta cũng không việc gì phải quan tâm đến họ

pushState[] và replaceState[]

Cả 2 hàm

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
21 và ‘replaceState[]` đều có chung các đối số

  • đối số đầu tiên là
    history.pushState[null, '', '/next-location'];
    history.replaceState[null, '', '/replace-location'];
    
    // attaching state to an entry
    history.pushState[{ msg: 'Hi!' }, '', '/greeting'];
    
    // while on medium.com
    history.pushState[null, '', '//www.google.com'];
    // throws a DOMException
    
    26. Đối số này có thể là null;
  • đối số thứ 2 là
    history.pushState[null, '', '/next-location'];
    history.replaceState[null, '', '/replace-location'];
    
    // attaching state to an entry
    history.pushState[{ msg: 'Hi!' }, '', '/greeting'];
    
    // while on medium.com
    history.pushState[null, '', '//www.google.com'];
    // throws a DOMException
    
    27
  • đối số thứ 3 là
    history.pushState[null, '', '/next-location'];
    history.replaceState[null, '', '/replace-location'];
    
    // attaching state to an entry
    history.pushState[{ msg: 'Hi!' }, '', '/greeting'];
    
    // while on medium.com
    history.pushState[null, '', '//www.google.com'];
    // throws a DOMException
    
    28 - là địa chỉ mà ta muốn điều hướng tới. Đây có thể là 1 URL đầy đủ, hoặc chỉ ở dạng đường dẫn tương đối, nhưng nó luôn phải thuộc về ứng dụng hiện tại [chung
    history.pushState[null, '', '/next-location'];
    history.replaceState[null, '', '/replace-location'];
    
    // attaching state to an entry
    history.pushState[{ msg: 'Hi!' }, '', '/greeting'];
    
    // while on medium.com
    history.pushState[null, '', '//www.google.com'];
    // throws a DOMException
    
    5 và
    history.pushState[null, '', '/next-location'];
    history.replaceState[null, '', '/replace-location'];
    
    // attaching state to an entry
    history.pushState[{ msg: 'Hi!' }, '', '/greeting'];
    
    // while on medium.com
    history.pushState[null, '', '//www.google.com'];
    // throws a DOMException
    
    4], nếu không, trình duyệt sẽ gặp lỗi
    history.pushState[null, '', '/next-location'];
    history.replaceState[null, '', '/replace-location'];
    
    // attaching state to an entry
    history.pushState[{ msg: 'Hi!' }, '', '/greeting'];
    
    // while on medium.com
    history.pushState[null, '', '//www.google.com'];
    // throws a DOMException
    
    41
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException

hàm

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
21 sẽ thêm 1 mục vào lịch sử phiên phía sau mục hiện tại. Nếu như có mục nào đó nằm phía sau mục hiện tại rồi, chúng sẽ bị thay thế bởi mục mới. Cơ chế này cũng giống như cơ chế bình thường khi ta nhấp vào thẻ
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
05 ở trên

hàm

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
22 sẽ thay thế mục nhập mới bằng mục nhập chính hiện tại trong lịch sử phiên. Các mục nhập khác đều không bị ảnh hưởng. Cơ chế này tương tự với cơ chế được nhắc đến ở trên - nhấp vào một đường liên kết với
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
45 giống URL hiện tại - nhưng
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
22 khác ở chỗ nó cho phép thay thế mục hiện tại với bất kỳ vị trí mới nào

đi[]

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
23 là một cách để mô phỏng lại 2 nút quay lại và chuyển tiếp của trình duyệt

hàm

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
23 chỉ nhận 1 đối số. number of entry to get out of the history. Một số dương tương đương với hành động phía trước, số âm tương đương với nút quay lại, số 0 [hoặc
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
49] tương đương với tải lại trang

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
0

Ngoài ra còn có 2 hàm

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
0
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
1 - chúng tương đương với
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
2 và
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
3

Tiểu bang

Một trong các thuộc tính của mục nhập là trạng thái, 2 hàm

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
21 và
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
22 cũng chứa trạng thái trong tên của mình. Vì vậy, trạng thái là gì ?

State is data [data] mount with a entry. Nó cố định chuyển hướng - có nghĩa là khi bạn thêm 1 trạng thái vào một mục nhập, điều hướng đi, sau đó quay trở lại mục nhập trước đó, trạng thái sẽ vẫn ở đó. Trạng thái được gắn vào mục nhập bằng 2 hàm

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
21 và
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
22, và có thể được lấy ra bằng
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
8

Có một số ràng buộc cho trạng thái. Thứ nhất, it must be serialize before. Thứ 2, nêu rõ giới hạn về kích thước [video trên Firefox là 640k]. Cuối cùng, khi chúng ta điều hướng trực tiếp vào 1 URL, trạng thái của nó sẽ được đặt mặc định là

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
9, nếu như logic của trang web phụ thuộc vào
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
26 để hiển thị, chúng ta có thể gặp vấn đề khi người dùng truy cập trực tiếp . Do đó,
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
26 có thể hữu ích khi được sử dụng để lưu trữ những dữ liệu không được hiển thị, ví dụ như một khóa để chứa một địa chỉ URL với mục đích cho người dùng mới đăng nhập về phía trước

Điều hướng trong SPA sử dụng API lịch sử

Vậy 1 app SPA sử dụng History API như thế nào ? . Trình xử lý đó có thể gọi

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
21 và
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
22 để thực hiện điều hướng mà không cần ping tới máy chủ. Tuy nhiên, API lịch sử chỉ cập nhật lịch sử phiên, vì vậy trình xử lý cũng cần tương tác với cả bộ định tuyến để cho bộ định tuyến biết vị trí mới

Có nhiều cách để xử lý 1 handler như thế này. Nếu như bạn sử dụng những framework như Vue hay React, bạn có thể viết như ví dụ sau

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
1

Hoặc, nếu muốn, ta có thể thêm trình xử lý sự kiện toàn cầu cho mọi nút nhấp để thực hiện điều hướng trong ứng dụng, ghi đè lại mặc định và thay thế bằng lời gọi API lịch sử. Một ví dụ bạn có thể xem là với bộ định tuyến roadtrip

Việc sử dụng API lịch sử giúp cho việc điều khiển điều hướng trong ứng dụng dễ dàng hơn. Tuy nhiên, ta vẫn phải xử lý thêm 1 trường hợp nữa. khi người dùng nhấp vào 2 nút quay lại và chuyển tiếp của trình duyệt

Xử lý với 2 nút lùi và tiến

Khi 2 nút back và foward được nhấp [cũng như khi

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
05 được gọi], trình duyệt sẽ thực hiện 1 lời gọi
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
06. Để bắt được sự kiện này, ta phải thêm 1 trình lắng nghe sự kiện

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
2

Lịch sử phiên sẽ được cập nhật ngay khi sự kiện được gọi, lúc này chúng ta cần thông báo cho bộ định tuyến biết rằng vị trí hiện tại đã thay đổi

Điều hướng bằng cách thay đổi trực tiếp trên thanh địa chỉ

Nếu người dùng thay đổi URL bằng cách chỉnh sửa trực tiếp trên thanh địa chỉ, công việc này sẽ tạo một

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
08 mới. API lịch sử lúc này chỉ dừng việc tải lại với các mục nhập có cùng
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
08 - nghĩa là cuộc gọi `history. go[] hoặc nhấp vào 2 nút tiến/lùi sau khi điều này sẽ thực hiện việc tải lại toàn bộ trang

Còn về phía máy chủ thì sao

Hầu hết các SPA đều chạy trên client, tuy nhiên file mã nguồn vẫn phải hướng tới từ đâu đó

Bài viết Single-Page app và server sẽ nói thêm về những cách này .

Nguồn

Trang tài liệu của Mozilla có những bài viết rất đầy đủ về History also as Location API

Ngoài ra, hiện nay hầu hết các SPA framework đều tự xây dựng nên những bộ wrapper riêng của mình cho History API. Ví dụ như lịch sử gói được sử dụng bởi

history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
09, hoặc
history.pushState[null, '', '/next-location'];
history.replaceState[null, '', '/replace-location'];

// attaching state to an entry
history.pushState[{ msg: 'Hi!' }, '', '/greeting'];

// while on medium.com
history.pushState[null, '', '//www.google.com'];
// throws a DOMException
10 của Vue cũng tự triển khai một bộ API cho riêng mình

Chủ Đề