Cách xây dựng giao diện người dùng JavaScript linh hoạt

theanh

Administrator
Nhân viên
Những thứ trên web có thể hỏng — tỷ lệ cược chống lại chúng ta. Rất nhiều thứ có thể xảy ra sai sót: một yêu cầu mạng không thành công, một thư viện của bên thứ ba bị hỏng, một tính năng JavaScript không được hỗ trợ (giả sử JavaScript thậm chí khả dụng), một CDN ngừng hoạt động, một người dùng có hành vi bất ngờ (họ nhấp đúp vào nút gửi), danh sách còn dài.

May mắn thay, chúng ta với tư cách là kỹ sư có thể tránh hoặc ít nhất là giảm thiểu tác động của sự cố trong các ứng dụng web mà chúng ta xây dựng. Tuy nhiên, điều này đòi hỏi một nỗ lực có ý thức và thay đổi tư duy theo hướng nghĩ về những kịch bản không vui nhiều như những kịch bản vui vẻ.

Trải nghiệm người dùng (UX) không cần phải là tất cả hoặc không có gì — chỉ cần những gì có thể sử dụng được. Tiền đề này, được gọi là sự suy giảm duyên dáng, cho phép một hệ thống tiếp tục hoạt động khi một số bộ phận của nó không hoạt động — giống như một chiếc xe đạp điện trở thành một chiếc xe đạp thông thường khi pin của nó hết. Nếu có lỗi thì chỉ chức năng phụ thuộc vào lỗi đó mới bị ảnh hưởng.

Giao diện người dùng (UI) phải thích ứng với chức năng mà chúng có thể cung cấp, đồng thời cung cấp càng nhiều giá trị cho người dùng cuối càng tốt.

Tại sao phải có khả năng phục hồi​

Khả năng phục hồi là nội tại của web.

Trình duyệt bỏ qua các thẻ HTML không hợp lệ và các thuộc tính CSS không được hỗ trợ. Thái độ tự do này được gọi là Luật Postel, được Jeremy Keith truyền tải một cách tuyệt vời trong Thiết kế web phục hồi:
“Ngay cả khi có lỗi trong HTML hoặc CSS, trình duyệt vẫn sẽ cố gắng xử lý thông tin, bỏ qua bất kỳ phần nào mà nó không thể phân tích cú pháp.”
JavaScript không dễ tha thứ như vậy. Khả năng phục hồi là yếu tố bên ngoài. Chúng ta hướng dẫn JavaScript phải làm gì nếu có điều gì đó bất ngờ xảy ra. Nếu yêu cầu API không thành công, chúng ta sẽ phải chịu trách nhiệm phát hiện lỗi và sau đó quyết định phải làm gì. Và quyết định đó ảnh hưởng trực tiếp đến người dùng.

Khả năng phục hồi xây dựng lòng tin với người dùng. Trải nghiệm lỗi sẽ phản ánh không tốt về thương hiệu. Theo Kim và Mauborgne, sự tiện lợi (tính khả dụng, dễ tiêu thụ) là một trong sáu đặc điểm liên quan đến một thương hiệu thành công, khiến sự suy giảm tinh tế đồng nghĩa với nhận thức về thương hiệu.

UX mạnh mẽ và đáng tin cậy là tín hiệu của chất lượng và độ tin cậy, cả hai đều tác động đến thương hiệu. Người dùng không thể thực hiện tác vụ vì có thứ gì đó bị hỏng sẽ tự nhiên phải đối mặt với sự thất vọng mà họ có thể liên tưởng đến thương hiệu của bạn.

Thường thì lỗi hệ thống được coi là "trường hợp góc cạnh" — những điều hiếm khi xảy ra, tuy nhiên, web có nhiều góc cạnh. Các trình duyệt khác nhau chạy trên các nền tảng và phần cứng khác nhau, tôn trọng sở thích của người dùng và chế độ duyệt (Safari Reader/công nghệ hỗ trợ), được phục vụ cho các vị trí địa lý với độ trễ và tính không liên tục khác nhau làm tăng khả năng xảy ra sự cố không như mong muốn.

Bình đẳng lỗi​

Giống như nội dung trên trang web có thứ bậc, các lỗi — những thứ không ổn — cũng tuân theo thứ bậc. Không phải tất cả các lỗi đều như nhau, một số lỗi quan trọng hơn những lỗi khác.

Chúng ta có thể phân loại lỗi theo tác động của chúng. XYZ không hoạt động ngăn cản người dùng đạt được mục tiêu của họ như thế nào? Câu trả lời thường phản ánh thứ bậc nội dung.

Ví dụ: tổng quan bảng điều khiển về tài khoản ngân hàng của bạn chứa dữ liệu có tầm quan trọng khác nhau. Tổng giá trị số dư của bạn quan trọng hơn thông báo nhắc bạn kiểm tra tin nhắn trong ứng dụng. Phương pháp ưu tiên của MoSCoW phân loại cái trước là phải có, còn cái sau là nên có.



Nếu thông tin chính không khả dụng (ví dụ: yêu cầu mạng không thành công), chúng ta phải minh bạch và thông báo cho người dùng, thường là thông qua thông báo lỗi. Nếu không có thông tin thứ cấp, chúng tôi vẫn có thể cung cấp trải nghiệm cốt lõi (phải có) trong khi ẩn thành phần bị suy giảm một cách khéo léo.



Biết khi nào hiển thị thông báo lỗi hay không có thể được biểu diễn bằng một cây quyết định đơn giản:



Phân loại sẽ loại bỏ mối quan hệ 1-1 giữa lỗi và thông báo lỗi trong UI. Nếu không, chúng ta có nguy cơ làm phiền người dùng và làm lộn xộn UI bằng quá nhiều thông báo lỗi. Được hướng dẫn bởi hệ thống phân cấp nội dung, chúng ta có thể chọn lọc những lỗi nào được hiển thị trên UI và những lỗi nào xảy ra mà người dùng cuối không biết.


Phòng bệnh hơn chữa bệnh​

Y học có câu nói phòng bệnh hơn chữa bệnh.

Áp dụng vào bối cảnh xây dựng giao diện người dùng phục hồi, việc ngăn ngừa lỗi xảy ra ngay từ đầu được mong muốn hơn là phải phục hồi sau lỗi. Loại lỗi tốt nhất là loại lỗi không xảy ra.

Có thể cho rằng không bao giờ đưa ra giả định, đặc biệt là khi sử dụng dữ liệu từ xa, tương tác với các thư viện của bên thứ ba hoặc sử dụng các tính năng ngôn ngữ mới hơn. Sự cố ngừng hoạt động hoặc thay đổi API không theo kế hoạch cùng với trình duyệt mà người dùng chọn hoặc phải sử dụng nằm ngoài tầm kiểm soát của chúng tôi. Mặc dù chúng tôi không thể ngăn chặn các sự cố ngoài tầm kiểm soát xảy ra, nhưng chúng tôi có thể tự bảo vệ mình khỏi các tác động (phụ) của chúng.

Thực hiện cách tiếp cận phòng thủ hơn khi viết mã giúp giảm lỗi lập trình viên phát sinh do đưa ra giả định. Bi quan hơn lạc quan ủng hộ khả năng phục hồi. Ví dụ mã bên dưới quá lạc quan:
Mã:
const debitCards = useDebitCards();return ( [LIST] {debitCards.map(card => { 
[*] {card.lastFourDigits} })} [/LIST]);
Nó giả định rằng thẻ ghi nợ tồn tại, điểm cuối trả về một Mảng, mảng chứa các đối tượng và mỗi đối tượng có một thuộc tính có tên là lastFourDigits. Việc triển khai hiện tại buộc người dùng cuối phải kiểm tra các giả định của chúng tôi. Sẽ an toàn hơn và thân thiện hơn với người dùng nếu các giả định này được nhúng vào mã:
Mã:
const debitCards = useDebitCards();if (Array.isArray(debitCards) && debitCards.length) { return ( [LIST] {debitCards.map(card => { if (card.lastFourDigits) { return 
[*] {card.lastFourDigits} } })} [/LIST] );}return "Something else";
Sử dụng phương thức của bên thứ ba mà không kiểm tra trước xem phương thức đó có khả dụng hay không cũng là lạc quan:
Mã:
stripe.handleCardPayment(/* ... */);
Đoạn mã trên giả định rằng đối tượng stripe tồn tại, đối tượng này có thuộc tính có tên là handleCardPayment và thuộc tính đó là một hàm. Sẽ an toàn hơn và do đó có tính phòng thủ hơn nếu chúng tôi xác minh các giả định này trước:
Mã:
if ( typeof stripe === 'object' && typeof stripe.handleCardPayment === 'function') { stripe.handleCardPayment(/* ... */);}
Cả hai ví dụ đều kiểm tra xem một thứ gì đó có khả dụng hay không trước khi sử dụng. Những người quen thuộc với phát hiện tính năng có thể nhận ra mẫu này:
Mã:
if (navigator.clipboard) { /* ... */}
Chỉ cần hỏi trình duyệt xem nó có hỗ trợ API Clipboard hay không trước khi cố gắng cắt, sao chép hoặc dán là một ví dụ đơn giản nhưng hiệu quả về khả năng phục hồi. Giao diện người dùng có thể thích ứng trước bằng cách ẩn chức năng clipboard khỏi các trình duyệt không được hỗ trợ hoặc khỏi những người dùng chưa cấp quyền.



Thói quen duyệt web của người dùng là một lĩnh vực khác nằm ngoài tầm kiểm soát của chúng ta. Mặc dù chúng ta không thể chỉ định cách sử dụng ứng dụng của mình, nhưng chúng ta có thể thiết lập các rào cản ngăn chặn những gì chúng ta coi là "sử dụng sai mục đích". Một số người nhấp đúp vào các nút — một hành vi chủ yếu là thừa trên web, tuy nhiên không phải là hành vi vi phạm có thể bị trừng phạt.

Nhấp đúp vào nút gửi biểu mẫu không nên gửi biểu mẫu hai lần, đặc biệt là đối với phương thức HTTP không phải là phương thức bất biến. Trong quá trình gửi biểu mẫu, hãy ngăn chặn các lần gửi tiếp theo để giảm thiểu hậu quả từ nhiều yêu cầu được thực hiện.



Việc ngăn chặn việc gửi lại biểu mẫu trong JavaScript cùng với việc sử dụng aria-disabled="true" dễ sử dụng và dễ truy cập hơn so với thuộc tính HTML disabled. Sandrina Pereira giải thích Làm cho các nút bị vô hiệu hóa trở nên bao hàm hơn một cách chi tiết.

Phản hồi lỗi​

Không phải tất cả lỗi đều có thể ngăn ngừa được thông qua lập trình phòng thủ. Điều này có nghĩa là phản hồi lỗi vận hành (lỗi xảy ra trong các chương trình được viết đúng) thuộc về chúng ta.

Phản hồi lỗi có thể được mô hình hóa bằng cách sử dụng cây quyết định. Chúng ta có thể khôi phục, quay lại hoặc thừa nhận lỗi:



Khi gặp lỗi, câu hỏi đầu tiên phải là "chúng ta có thể khôi phục không?" Ví dụ, việc thử lại yêu cầu mạng không thành công lần đầu tiên có thành công trong những lần thử tiếp theo không? Các dịch vụ vi mô không liên tục, kết nối internet không ổn định hoặc tính nhất quán cuối cùng đều là lý do để thử lại. Các thư viện truy xuất dữ liệu như SWR cung cấp chức năng này miễn phí.

Mức độ chấp nhận rủi ro và bối cảnh xung quanh ảnh hưởng đến phương thức HTTP mà bạn muốn thử lại. Tại Nutmeg, chúng tôi thử lại các lần đọc không thành công (yêu cầu GET), nhưng không thử lại các lần ghi (POST/PUT/PATCH/DELETE). Nhiều lần thử truy xuất dữ liệu (hiệu suất danh mục đầu tư) an toàn hơn là thay đổi dữ liệu (gửi lại biểu mẫu).

Câu hỏi thứ hai là: Nếu chúng tôi không thể khôi phục, chúng tôi có thể cung cấp phương án dự phòng không? Ví dụ, nếu thanh toán bằng thẻ trực tuyến không thành công, chúng tôi có thể cung cấp phương thức thanh toán thay thế như qua PayPal hoặc Open Banking không.



Các giải pháp dự phòng không phải lúc nào cũng cần phải quá phức tạp, chúng có thể rất tinh tế. Bản sao chứa văn bản phụ thuộc vào dữ liệu từ xa có thể chuyển sang văn bản ít cụ thể hơn khi yêu cầu không thành công:



Câu hỏi thứ ba và cũng là câu hỏi cuối cùng là: Nếu chúng ta không thể khôi phục hoặc dự phòng thì lỗi này quan trọng như thế nào (liên quan đến “Tính bình đẳng của lỗi”). Giao diện người dùng phải xác nhận các lỗi chính bằng cách thông báo cho người dùng biết có điều gì đó không ổn, đồng thời đưa ra lời nhắc có thể thực hiện được như liên hệ với bộ phận hỗ trợ khách hàng hoặc liên kết đến các bài viết hỗ trợ có liên quan.


Khả năng quan sát​

UI thích ứng với sự cố không phải là mục đích cuối cùng. Đồng xu còn có một mặt khác.

Các kỹ sư cần có khả năng nhìn thấy nguyên nhân gốc rễ đằng sau trải nghiệm bị suy giảm. Ngay cả những lỗi không được nêu ra với người dùng cuối (lỗi thứ cấp) cũng phải được truyền đến các kỹ sư. Các dịch vụ giám sát lỗi theo thời gian thực như Sentry hoặc Rollbar là những công cụ vô giá cho phát triển web hiện đại.



Hầu hết các nhà cung cấp dịch vụ giám sát lỗi đều tự động ghi lại mọi ngoại lệ chưa được xử lý. Thiết lập chỉ cần nỗ lực kỹ thuật tối thiểu nhưng nhanh chóng mang lại lợi nhuận cho môi trường sản xuất lành mạnh được cải thiện và MTTA (thời gian trung bình để xác nhận).

Sức mạnh thực sự đến khi chúng tôi tự ghi nhật ký lỗi một cách rõ ràng. Mặc dù điều này đòi hỏi nhiều nỗ lực ban đầu hơn nhưng cho phép chúng tôi làm phong phú thêm các lỗi đã ghi nhật ký với nhiều ý nghĩa và ngữ cảnh hơn — cả hai đều hỗ trợ khắc phục sự cố. Nếu có thể, hãy hướng đến các thông báo lỗi mà các thành viên không chuyên về kỹ thuật trong nhóm có thể hiểu được.



Mở rộng ví dụ Stripe trước đó bằng nhánh else là ứng cử viên hoàn hảo cho việc ghi nhật ký lỗi rõ ràng:
Mã:
if ( typeof stripe === "object" && typeof stripe.handleCardPayment === "function") { stripe.handleCardPayment(/* ... */);} else { logger.capture( "[Thanh toán] Thẻ tính phí — Không thể thực hiện thanh toán bằng thẻ vì stripe.handleCardPayment không khả dụng" );}
Lưu ý: Phong cách phòng thủ này không nhất thiết phải bị ràng buộc với việc gửi biểu mẫu (vào thời điểm xảy ra lỗi), nó có thể xảy ra khi một thành phần lần đầu tiên được gắn kết (trước khi xảy ra lỗi) giúp chúng ta và UI có thêm thời gian để thích ứng.

Khả năng quan sát giúp xác định điểm yếu trong mã và các khu vực có thể được củng cố. Khi điểm yếu xuất hiện, hãy xem xét liệu/làm thế nào để có thể củng cố điểm yếu đó để ngăn chặn sự cố tương tự xảy ra lần nữa. Xem xét các xu hướng và lĩnh vực rủi ro như tích hợp của bên thứ ba để xác định những gì có thể được gói trong cờ tính năng hoạt động (hay còn gọi là công tắc tắt).



Người dùng được cảnh báo trước về việc có thứ gì đó không hoạt động sẽ ít thất vọng hơn những người không được cảnh báo. Việc biết trước về các công trình đường bộ giúp quản lý kỳ vọng, cho phép người lái xe lập kế hoạch cho các tuyến đường thay thế. Khi xử lý sự cố mất điện (hy vọng được phát hiện thông qua quá trình giám sát và không bị người dùng báo cáo), hãy minh bạch.


Retrospectives​

Rất dễ bỏ qua lỗi.

Tuy nhiên, chúng mang lại cơ hội học tập có giá trị cho chúng ta và các đồng nghiệp hiện tại hoặc tương lai. Việc xóa bỏ định kiến về sự không thể tránh khỏi rằng mọi thứ sẽ diễn ra không như mong đợi là rất quan trọng. Trong Suy nghĩ hộp đen, điều này được mô tả như sau:
“Trong các tổ chức cực kỳ phức tạp, thành công chỉ có thể đến khi chúng ta đối mặt với sai lầm của mình, học hỏi từ phiên bản hộp đen của riêng mình và tạo ra một môi trường mà ở đó, thất bại là điều an toàn.”
Phân tích giúp ngăn ngừa hoặc giảm thiểu lỗi tương tự xảy ra lần nữa. Giống như hộp đen trong ngành hàng không ghi lại các sự cố, chúng ta nên ghi lại các lỗi. Ít nhất thì tài liệu từ các sự cố trước đó cũng giúp giảm MTTR (thời gian trung bình để sửa chữa) nếu lỗi tương tự xảy ra lần nữa.

Tài liệu thường ở dạng báo cáo RCA (phân tích nguyên nhân gốc rễ) phải trung thực, có thể phát hiện được và bao gồm: sự cố là gì, tác động của sự cố, thông tin chi tiết về kỹ thuật, cách khắc phục và các hành động cần thực hiện sau sự cố.

Suy nghĩ kết thúc​

Chấp nhận tính mong manh của web là bước cần thiết để xây dựng các hệ thống phục hồi. Trải nghiệm người dùng đáng tin cậy hơn đồng nghĩa với khách hàng hài lòng. Được trang bị cho tình huống xấu nhất (chủ động) tốt hơn là dập tắt đám cháy (phản ứng) từ quan điểm của doanh nghiệp, khách hàng và nhà phát triển (ít lỗi hơn!).

Những điều cần nhớ:
  • Giao diện người dùng (UI) phải thích ứng với chức năng mà chúng có thể cung cấp, đồng thời vẫn mang lại giá trị cho người dùng;
  • Luôn nghĩ đến điều gì có thể sai (không bao giờ đưa ra giả định);
  • Phân loại lỗi dựa trên tác động của chúng (không phải tất cả lỗi đều như nhau);
  • Ngăn ngừa lỗi tốt hơn là phản hồi lỗi (viết mã phòng thủ);
  • Khi gặp lỗi, hãy hỏi xem có khả năng khôi phục hoặc dự phòng không;
  • Thông báo lỗi dành cho người dùng phải cung cấp lời nhắc có thể thực hiện được;
  • Kỹ sư phải có khả năng hiển thị lỗi (sử dụng dịch vụ giám sát lỗi);
  • Thông báo lỗi dành cho kỹ sư/đồng nghiệp phải có ý nghĩa và cung cấp ngữ cảnh;
  • Học hỏi từ lỗi để giúp bản thân tương lai và những người khác.

Đọc thêm​

  • Sự cường điệu xung quanh tín hiệu
  • Tạo biểu mẫu nhiều bước hiệu quả Để có trải nghiệm người dùng tốt hơn
  • Tác động chuyển đổi của AI lên thiết kế web: Tăng cường năng suất trong toàn ngành
  • Cách xây dựng trang web đa ngôn ngữ với Nuxt.js
 
Back
Bên trên