Xây dựng cho các trình duyệt hiện đại và tăng cường dần như phiên bản 2003
Tháng 3 năm 2003, Nick Finck và Steve Cheeon đã làm cho thế giới thiết kế web sửng sốt khi đề cập đến khái niệm nâng cao tiến bộ, một chiến lược thiết kế web tập trung vào việc tải nội dung trang web cốt lõi trước, sau đó dần dần thêm các lớp trình bày và tính năng phức tạp hơn vào nội dung. Vào năm 2003, cải tiến dần dần xoay quanh việc sử dụng các tính năng CSS hiện đại, JavaScript không phô trương và thậm chí chỉ là Đồ hoạ vectơ có thể mở rộng. Trong năm 2020 và trong tương lai, chúng tôi sẽ nâng cao dần bằng cách sử dụng các tính năng hiện đại của trình duyệt.
JavaScript hiện đại
Nói về JavaScript, khả năng hỗ trợ của trình duyệt cho các tính năng JavaScript chính mới nhất của ES 2015 là rất tốt.
Tiêu chuẩn mới bao gồm các lời hứa, mô-đun, lớp, giá trị cố định của mẫu, hàm mũi tên, let
và const
, tham số mặc định, trình tạo, chỉ định giải cấu trúc, nghỉ và trải rộng, Map
/Set
, WeakMap
/WeakSet
và nhiều nội dung khác.
Tất cả đều được hỗ trợ.
Các hàm không đồng bộ, một tính năng ES 2017 và là một trong những tính năng cá nhân tôi yêu thích,
có thể dùng
trong tất cả các trình duyệt chính.
Các từ khoá async
và await
cho phép hành vi không đồng bộ, dựa trên lời hứa được viết theo kiểu rõ ràng hơn, tránh nhu cầu định cấu hình rõ ràng chuỗi lời hứa.
Ngoài ra, thậm chí các ngôn ngữ bổ sung mới nhất cho ngôn ngữ ES 2020 như chuỗi tuỳ chọn và hợp nhất rỗng cũng đã được hỗ trợ rất nhanh chóng. Bạn có thể xem mã mẫu bên dưới. Khi nói đến các tính năng cốt lõi của JavaScript, bãi cỏ không thể xanh hơn nhiều như hiện nay.
const adventurer = {
name: 'Alice',
cat: {
name: 'Dinah',
},
};
console.log(adventurer.dog?.name);
// Expected output: undefined
console.log(0 ?? 42);
// Expected output: 0
Ứng dụng mẫu: Fugu Greetings
Trong bài viết này, tôi sẽ làm việc với một PWA đơn giản có tên là Fugu Greetings (GitHub). Tên của ứng dụng này là một mẹo hay của Project Fugu 🐡, một nỗ lực mang lại cho web tất cả sức mạnh của các ứng dụng dành cho Android/iOS/máy tính để bàn. Bạn có thể đọc thêm về dự án trên trang đích của dự án.
Fugu Greetings là một ứng dụng vẽ cho phép bạn tạo thiệp chúc mừng ảo và gửi chúng cho những người thân yêu. Tệp này minh hoạ các khái niệm chính của PWA. Tính năng này đáng tin cậy và được bật hoàn toàn ngoại tuyến, vì vậy, ngay cả khi không có mạng, bạn vẫn có thể sử dụng ứng dụng này. Ứng dụng này cũng Có thể cài đặt vào màn hình chính của thiết bị và tích hợp liền mạch với hệ điều hành dưới dạng một ứng dụng độc lập.
Cải tiến tăng dần
Sau đây, đã đến lúc thảo luận về tính năng nâng cao dần dần. Bảng chú giải thuật ngữ về Tài liệu web của MDN xác định khái niệm như sau:
Tính năng nâng cao tiến bộ là một triết lý thiết kế cung cấp đường cơ sở gồm các chức năng và nội dung thiết yếu cho nhiều người dùng nhất có thể, trong khi chỉ mang lại trải nghiệm tốt nhất có thể cho những người dùng những trình duyệt hiện đại nhất có thể chạy mọi mã bắt buộc.
Phát hiện tính năng thường dùng để xác định xem trình duyệt có thể xử lý chức năng hiện đại hơn hay không, trong khi polyfill thường được dùng để thêm các tính năng còn thiếu bằng JavaScript.
[…]
Nâng cao tiến bộ là một kỹ thuật hữu ích cho phép các nhà phát triển web tập trung vào việc phát triển các trang web tốt nhất có thể trong khi vẫn khiến những trang web đó hoạt động trên nhiều tác nhân người dùng không xác định. Tình trạng xuống cấp nhẹ có liên quan, nhưng không giống nhau và thường được coi là đi theo hướng ngược lại với tính năng nâng cao dần. Trong thực tế, cả hai phương pháp đều hợp lệ và thường có thể bổ sung cho nhau.
Người đóng góp cho MMDN
Việc tạo từng thiệp chúc mừng từ đầu có thể thực sự rườm rà.
Vậy tại sao không có một tính năng cho phép người dùng nhập hình ảnh và bắt đầu từ đó?
Với phương pháp truyền thống, bạn nên sử dụng phần tử <input type=file>
để thực hiện việc này.
Trước tiên, bạn sẽ tạo phần tử, đặt type
của phần tử đó thành 'file'
rồi thêm loại MIME vào thuộc tính accept
, sau đó lập trình "nhấp" vào phần tử đó và lắng nghe các thay đổi.
Khi bạn chọn, hình ảnh sẽ được nhập thẳng vào canvas.
const importImage = async () => {
return new Promise((resolve) => {
const input = document.createElement('input');
input.type = 'file';
input.accept = 'image/*';
input.addEventListener('change', () => {
resolve(input.files[0]);
});
input.click();
});
};
Khi có tính năng import, bạn nên dùng tính năng import để người dùng có thể lưu thiệp chúc mừng trên máy.
Cách truyền thống để lưu tệp là tạo một đường liên kết neo với thuộc tính download
và với URL của blob là href
.
Bạn cũng sẽ "nhấp" vào nút này theo phương thức lập trình để kích hoạt quá trình tải xuống, và để tránh rò rỉ bộ nhớ, hy vọng là đừng quên thu hồi URL của đối tượng blob.
const exportImage = async (blob) => {
const a = document.createElement('a');
a.download = 'fugu-greeting.png';
a.href = URL.createObjectURL(blob);
a.addEventListener('click', (e) => {
setTimeout(() => URL.revokeObjectURL(a.href), 30 * 1000);
});
a.click();
};
Nhưng chờ một chút. Về mặt trí tuệ, bạn chưa "tải xuống" thiệp chúc mừng thì nghĩa là bạn đã "lưu" thiệp. Thay vì cho bạn thấy hộp thoại "lưu" cho phép bạn chọn nơi đặt tệp, trình duyệt đã tải trực tiếp thiệp chúc mừng xuống mà không cần người dùng tương tác và chuyển thẳng thiệp đó vào thư mục Downloads (Tệp đã tải xuống). Không ổn lắm.
Nếu có cách tốt hơn thì sao? Điều gì sẽ xảy ra nếu bạn có thể chỉ mở một tệp cục bộ, chỉnh sửa rồi lưu các nội dung sửa đổi vào tệp mới hoặc quay lại tệp gốc mà bạn đã mở ban đầu? Hoá ra là có. API Truy cập hệ thống tệp cho phép bạn mở và tạo tệp và thư mục, cũng như sửa đổi và lưu chúng .
Vậy làm cách nào để phát hiện API?
API Truy cập hệ thống tệp cho thấy một phương thức mới window.chooseFileSystemEntries()
.
Do đó, tôi cần tải có điều kiện các mô-đun nhập và xuất khác nhau tuỳ thuộc vào việc có phương pháp này hay không. Tôi đã hướng dẫn cách thực hiện việc này ở bên dưới.
const loadImportAndExport = () => {
if ('chooseFileSystemEntries' in window) {
Promise.all([
import('./import_image.mjs'),
import('./export_image.mjs'),
]);
} else {
Promise.all([
import('./import_image_legacy.mjs'),
import('./export_image_legacy.mjs'),
]);
}
};
Tuy nhiên, trước khi tìm hiểu chi tiết về API Truy cập hệ thống tệp, vui lòng cho phép tôi làm nổi bật nhanh mẫu tính năng nâng cao tăng dần tại đây. Trên các trình duyệt hiện không hỗ trợ API Truy cập hệ thống tệp, tôi tải các tập lệnh cũ. Bạn có thể thấy các thẻ mạng của Firefox và Safari bên dưới.
Tuy nhiên, trên Chrome (trình duyệt hỗ trợ API), chỉ các tập lệnh mới được tải.
Điều này trở nên tinh tế nhờ có import()
động mà tất cả các trình duyệt hiện đại đều hỗ trợ.
Như tôi đã nói, những ngày này cỏ khá xanh.
API Truy cập hệ thống tệp
Giờ đây, sau khi giải quyết vấn đề này, đã đến lúc xem xét cách triển khai thực tế dựa trên API Truy cập hệ thống tệp.
Để nhập hình ảnh, tôi gọi window.chooseFileSystemEntries()
và chuyển thuộc tính đó vào thuộc tính accepts
, trong đó tôi nói tôi muốn các tệp hình ảnh.
Cả hai đuôi tệp và loại MIME đều được hỗ trợ.
Kết quả là một trình xử lý tệp, từ đó tôi có thể lấy tệp thực tế bằng cách gọi getFile()
.
const importImage = async () => {
try {
const handle = await window.chooseFileSystemEntries({
accepts: [
{
description: 'Image files',
mimeTypes: ['image/*'],
extensions: ['jpg', 'jpeg', 'png', 'webp', 'svg'],
},
],
});
return handle.getFile();
} catch (err) {
console.error(err.name, err.message);
}
};
Việc xuất hình ảnh gần giống nhau, nhưng lần này, tôi cần truyền một tham số loại của 'save-file'
sang phương thức chooseFileSystemEntries()
.
Từ đó, tôi nhận được một hộp thoại lưu tệp.
Khi tệp mở, bạn không cần làm điều này vì 'open-file'
là chế độ mặc định.
Tôi đặt tham số accepts
tương tự như trước, nhưng lần này chỉ giới hạn ở hình ảnh PNG.
Một lần nữa, tôi sẽ quay lại trình xử lý tệp, nhưng thay vì nhận tệp, lần này tôi tạo một luồng có thể ghi bằng cách gọi createWritable()
.
Tiếp theo, tôi viết blob, là hình ảnh thiệp chúc mừng, vào tệp.
Cuối cùng, tôi đóng luồng có thể ghi.
Mọi thứ đều có thể luôn không thành công: Ổ đĩa có thể hết dung lượng, có thể xảy ra lỗi ghi hoặc đọc hoặc có thể đơn giản là người dùng huỷ hộp thoại tệp.
Đây là lý do tại sao tôi luôn gói các lệnh gọi trong một câu lệnh try...catch
.
const exportImage = async (blob) => {
try {
const handle = await window.chooseFileSystemEntries({
type: 'save-file',
accepts: [
{
description: 'Image file',
extensions: ['png'],
mimeTypes: ['image/png'],
},
],
});
const writable = await handle.createWritable();
await writable.write(blob);
await writable.close();
} catch (err) {
console.error(err.name, err.message);
}
};
Khi sử dụng tính năng nâng cao tăng dần với API Truy cập hệ thống tệp, tôi có thể mở một tệp như trước. Tệp đã nhập được vẽ ngay trên canvas. Tôi có thể thực hiện các chỉnh sửa và cuối cùng lưu chúng bằng một hộp thoại lưu thực tế, nơi tôi có thể chọn tên và vị trí lưu trữ của tệp. Bây giờ, tệp này đã sẵn sàng để lưu giữ vĩnh viễn.
Web Share Target API (API Mục tiêu chia sẻ web) và Web Share Target API
Ngoài việc lưu trữ để lưu giữ vĩnh viễn, có lẽ tôi thực sự muốn chia sẻ thiệp chúc mừng của mình. Đây là điều mà API Chia sẻ web và API Mục tiêu chia sẻ web cho phép. Các hệ điều hành dành cho thiết bị di động và gần đây là máy tính đã có cơ chế chia sẻ tích hợp sẵn. Ví dụ: dưới đây là trang chia sẻ của Safari dành cho máy tính trên macOS được kích hoạt từ một bài viết trên blog của tôi. Khi nhấp vào nút Share Article (Chia sẻ bài viết), bạn có thể chia sẻ đường liên kết đến bài viết đó với một người bạn, chẳng hạn như qua ứng dụng Tin nhắn của macOS.
Mã để thực hiện việc này khá đơn giản. Tôi gọi navigator.share()
và truyền vào đó một title
, text
và url
không bắt buộc trong một đối tượng.
Nhưng nếu tôi muốn đính kèm một hình ảnh thì sao? Cấp 1 của API Chia sẻ web chưa hỗ trợ tính năng này.
Tin vui là tính năng Chia sẻ web cấp 2 đã bổ sung khả năng chia sẻ tệp.
try {
await navigator.share({
title: 'Check out this article:',
text: `"${document.title}" by @tomayac:`,
url: document.querySelector('link[rel=canonical]').href,
});
} catch (err) {
console.warn(err.name, err.message);
}
Hãy để tôi chỉ cho bạn cách làm cho ứng dụng này hoạt động với ứng dụng Thiệp chúc mừng Fugu.
Trước tiên, tôi cần chuẩn bị một đối tượng data
có một mảng files
bao gồm một blob, sau đó là title
và text
. Tiếp theo, như một phương pháp hay nhất, tôi sử dụng phương thức navigator.canShare()
mới đúng như tên gọi của nó: Phương thức này cho tôi biết liệu trình duyệt có thể chia sẻ đối tượng data
mà tôi đang cố chia sẻ về mặt kỹ thuật hay không.
Nếu navigator.canShare()
cho tôi biết dữ liệu có thể được chia sẻ, tôi đã sẵn sàng gọi navigator.share()
như trước.
Vì mọi thứ đều có thể không thành công nên tôi sẽ sử dụng lại khối try...catch
.
const share = async (title, text, blob) => {
const data = {
files: [
new File([blob], 'fugu-greeting.png', {
type: blob.type,
}),
],
title: title,
text: text,
};
try {
if (!(navigator.canShare(data))) {
throw new Error("Can't share data.", data);
}
await navigator.share(data);
} catch (err) {
console.error(err.name, err.message);
}
};
Như trước đây, tôi sử dụng tính năng nâng cao tăng dần.
Nếu cả 'share'
và 'canShare'
tồn tại trên đối tượng navigator
, thì chỉ khi đó tôi sẽ tiếp tục và tải share.mjs
thông qua import()
động.
Trên các trình duyệt như Safari trên thiết bị di động chỉ đáp ứng một trong hai điều kiện này, tôi sẽ không tải chức năng.
const loadShare = () => {
if ('share' in navigator && 'canShare' in navigator) {
import('./share.mjs');
}
};
Trong Lời chào Fugu, nếu tôi nhấn vào nút Share (Chia sẻ) trên một trình duyệt hỗ trợ như Chrome trên Android, thì trang tính chia sẻ tích hợp sẵn sẽ mở ra. Ví dụ: tôi có thể chọn Gmail và tiện ích trình soạn email bật lên với hình ảnh đính kèm.
API Bộ chọn danh bạ
Tiếp theo, tôi muốn nói về danh bạ, nghĩa là sổ địa chỉ trên thiết bị hoặc ứng dụng trình quản lý danh bạ. Khi viết thiệp chúc mừng, không phải lúc nào bạn cũng dễ dàng viết chính xác tên của ai đó. Ví dụ: Tôi có một người bạn Sergey thích tên của mình được viết bằng chữ cái Kirin. Tôi đang dùng bàn phím QWERTZ bằng tiếng Đức và không biết cách nhập tên. Đây là vấn đề mà API Bộ chọn liên hệ có thể giải quyết. Do bạn tôi đã lưu trữ bạn bè trong ứng dụng danh bạ trên điện thoại của mình nên thông qua API Bộ chọn danh bạ, tôi có thể nhấn vào danh bạ của mình trên web.
Trước tiên, tôi cần chỉ định danh sách các thuộc tính tôi muốn truy cập.
Trong trường hợp này, tôi chỉ cần tên, nhưng đối với các trường hợp sử dụng khác, tôi có thể quan tâm đến số điện thoại, email, biểu tượng hình đại diện hoặc địa chỉ thực tế.
Tiếp theo, tôi sẽ định cấu hình đối tượng options
và đặt multiple
thành true
để có thể chọn nhiều mục nhập.
Cuối cùng, tôi có thể gọi navigator.contacts.select()
để trả về các thuộc tính mong muốn cho các địa chỉ liên hệ do người dùng chọn.
const getContacts = async () => {
const properties = ['name'];
const options = { multiple: true };
try {
return await navigator.contacts.select(properties, options);
} catch (err) {
console.error(err.name, err.message);
}
};
Đến nay, có lẽ bạn đã học được mẫu: Tôi chỉ tải tệp khi API thực sự được hỗ trợ.
if ('contacts' in navigator) {
import('./contacts.mjs');
}
Trong Lời chào Fugu, khi tôi nhấn vào nút Danh bạ và chọn hai người bạn thân nhất của mình, tiếng Anh Sau đó, tên của họ sẽ được vẽ trên thiệp chúc mừng của tôi.
API Bảng nhớ tạm không đồng bộ
Tiếp theo là sao chép và dán. Một trong những thao tác mà chúng tôi yêu thích với vai trò là nhà phát triển phần mềm là sao chép và dán. Là tác giả thiệp chúc mừng, đôi khi, tôi có thể cũng muốn làm như vậy. Tôi muốn dán hình ảnh vào thiệp chúc mừng tôi đang làm hoặc sao chép thiệp chúc mừng để có thể tiếp tục chỉnh sửa từ nơi khác. API Bảng nhớ tạm không đồng bộ, hỗ trợ cả văn bản và hình ảnh. Tôi sẽ hướng dẫn bạn cách thêm tính năng hỗ trợ sao chép và dán vào ứng dụng Fugu Greetings.
Để sao chép nội dung nào đó vào bảng nhớ tạm của hệ thống, tôi cần ghi vào đó.
Phương thức navigator.clipboard.write()
lấy một mảng các mục trong bảng nhớ tạm làm thông số.
Về cơ bản, mỗi mục trong bảng nhớ tạm là một đối tượng với một blob là giá trị và loại của blob là khoá.
const copy = async (blob) => {
try {
await navigator.clipboard.write([
new ClipboardItem({
[blob.type]: blob,
}),
]);
} catch (err) {
console.error(err.name, err.message);
}
};
Để dán, tôi cần lặp lại các mục trong bảng nhớ tạm mà tôi nhận được bằng cách gọi
navigator.clipboard.read()
.
Lý do là nhiều mục trong bảng nhớ tạm có thể nằm trên bảng nhớ tạm theo các cách biểu diễn khác nhau.
Mỗi mục trong bảng nhớ tạm có một trường types
cho tôi biết loại MIME của các tài nguyên hiện có.
Tôi gọi phương thức getType()
của mục trong bảng nhớ tạm, truyền loại MIME mà tôi đã nhận được trước đó.
const paste = async () => {
try {
const clipboardItems = await navigator.clipboard.read();
for (const clipboardItem of clipboardItems) {
try {
for (const type of clipboardItem.types) {
const blob = await clipboardItem.getType(type);
return blob;
}
} catch (err) {
console.error(err.name, err.message);
}
}
} catch (err) {
console.error(err.name, err.message);
}
};
Đến nay, bạn gần như không cần phải nói nữa. Tôi chỉ làm việc này trên các trình duyệt hỗ trợ.
if ('clipboard' in navigator && 'write' in navigator.clipboard) {
import('./clipboard.mjs');
}
Vậy quy trình này hoạt động như thế nào trong thực tế? Tôi có một hình ảnh đang mở trong ứng dụng macOS Preview và sao chép hình ảnh đó vào bảng nhớ tạm. Khi tôi nhấp vào Paste (Dán), ứng dụng Fugu Greetings sau đó hỏi tôi xem tôi có muốn cho phép ứng dụng xem văn bản và hình ảnh trên bảng nhớ tạm hay không.
Cuối cùng, sau khi chấp nhận quyền, hình ảnh sẽ được dán vào ứng dụng. Còn một cách khác nữa. Để tôi sao chép thiệp chúc mừng vào bảng nhớ tạm. Sau đó, khi tôi mở Preview (Xem trước) và nhấp vào File (Tệp), sau đó nhấp vào New from clipboard (Mới từ bảng nhớ tạm), thiệp chúc mừng sẽ được dán vào một hình ảnh mới không có tiêu đề.
API Huy hiệu
Một API hữu ích khác là API Huy hiệu.
Tất nhiên là là một PWA có thể cài đặt, Fugu Greetings tất nhiên có một biểu tượng ứng dụng mà người dùng có thể đặt trên thanh Dock ứng dụng hoặc màn hình chính.
Một cách thú vị và dễ dàng để chứng minh API là (ab) sử dụng API trong Lời chào Fugu dưới dạng bộ đếm nét bút.
Tôi đã thêm một trình nghe sự kiện để tăng bộ đếm nét bút bất cứ khi nào sự kiện pointerdown
xảy ra và sau đó đặt huy hiệu biểu tượng đã cập nhật.
Bất cứ khi nào canvas bị xoá, bộ đếm sẽ đặt lại và huy hiệu sẽ bị xoá.
let strokes = 0;
canvas.addEventListener('pointerdown', () => {
navigator.setAppBadge(++strokes);
});
clearButton.addEventListener('click', () => {
strokes = 0;
navigator.setAppBadge(strokes);
});
Tính năng này là một tính năng nâng cao theo mức độ tăng dần, vì vậy, logic tải như thường lệ.
if ('setAppBadge' in navigator) {
import('./badge.mjs');
}
Trong ví dụ này, tôi đã vẽ các số từ 1 đến 7, sử dụng một nét bút cho mỗi số. Bộ đếm huy hiệu trên biểu tượng hiện ở mức 7.
API Đồng bộ hoá định kỳ ở chế độ nền
Bạn muốn bắt đầu ngày mới thật mới mẻ? Một tính năng thú vị của ứng dụng Fugu Greetings là ứng dụng này có thể truyền cảm hứng cho bạn mỗi sáng bằng một hình nền mới để bắt đầu thiệp chúc mừng. Ứng dụng dùng API Đồng bộ hoá định kỳ ở chế độ nền để đạt được điều này.
Bước đầu tiên là register một sự kiện đồng bộ hoá định kỳ trong quá trình đăng ký trình chạy dịch vụ.
Phương thức này sẽ theo dõi thẻ đồng bộ hoá có tên là 'image-of-the-day'
và có khoảng thời gian tối thiểu là 1 ngày để người dùng có thể nhận được hình nền mới 24 giờ một lần.
const registerPeriodicBackgroundSync = async () => {
const registration = await navigator.serviceWorker.ready;
try {
registration.periodicSync.register('image-of-the-day-sync', {
// An interval of one day.
minInterval: 24 * 60 * 60 * 1000,
});
} catch (err) {
console.error(err.name, err.message);
}
};
Bước thứ hai là nghe sự kiện periodicsync
trong trình chạy dịch vụ.
Nếu thẻ sự kiện là 'image-of-the-day'
, tức là thẻ đã được đăng ký trước đó, hình ảnh của ngày sẽ được truy xuất thông qua hàm getImageOfTheDay()
và kết quả được truyền đến tất cả các ứng dụng, nhờ đó, họ có thể cập nhật canvas và bộ nhớ đệm của mình.
self.addEventListener('periodicsync', (syncEvent) => {
if (syncEvent.tag === 'image-of-the-day-sync') {
syncEvent.waitUntil(
(async () => {
const blob = await getImageOfTheDay();
const clients = await self.clients.matchAll();
clients.forEach((client) => {
client.postMessage({
image: blob,
});
});
})()
);
}
});
Xin nhắc lại, đây thực sự là một tính năng nâng cao tăng dần, vì vậy, mã chỉ được tải khi trình duyệt hỗ trợ API.
Điều này áp dụng cho cả mã ứng dụng khách và mã trình chạy dịch vụ.
Trên các trình duyệt không hỗ trợ, cả hai trình duyệt này đều không được tải.
Hãy lưu ý cách trong trình chạy dịch vụ, thay vì import()
động (chưa được hỗ trợ trong ngữ cảnh trình chạy dịch vụ), tôi sử dụng importScripts()
cũ.
// In the client:
const registration = await navigator.serviceWorker.ready;
if (registration && 'periodicSync' in registration) {
import('./periodic_background_sync.mjs');
}
// In the service worker:
if ('periodicSync' in self.registration) {
importScripts('./image_of_the_day.mjs');
}
Trong Lời chào Fugu, việc nhấn nút Hình nền sẽ hiển thị hình ảnh thiệp chúc mừng của ngày hôm đó được cập nhật hằng ngày thông qua API đồng bộ hoá định kỳ ở chế độ nền.
API Kích hoạt thông báo
Đôi khi, ngay cả khi có rất nhiều cảm hứng, bạn cần một lời nhắc để hoàn thành thiệp chúc mừng đã bắt đầu. Đây là tính năng do API Kích hoạt thông báo bật. Là người dùng, tôi có thể nhập thời gian mà tôi muốn được nhắc hoàn thành thiệp chúc mừng. Khi đến thời điểm đó, tôi sẽ nhận được thông báo rằng thiệp chúc mừng của tôi đang chờ.
Sau khi nhắc về thời gian mục tiêu, ứng dụng sẽ lên lịch gửi thông báo bằng một showTrigger
.
Đây có thể là TimestampTrigger
với ngày đích đã chọn trước đó.
Thông báo nhắc nhở sẽ được kích hoạt cục bộ mà không cần phía mạng hoặc máy chủ.
const targetDate = promptTargetDate();
if (targetDate) {
const registration = await navigator.serviceWorker.ready;
registration.showNotification('Reminder', {
tag: 'reminder',
body: "It's time to finish your greeting card!",
showTrigger: new TimestampTrigger(targetDate),
});
}
Giống như mọi nội dung khác mà tôi đã trình bày cho đến nay, đây là một tính năng nâng cao tăng dần, vì vậy, mã chỉ được tải theo điều kiện.
if ('Notification' in window && 'showTrigger' in Notification.prototype) {
import('./notification_triggers.mjs');
}
Khi tôi đánh dấu vào hộp Lời nhắc trong Lời chào Fugu, một lời nhắc sẽ hỏi tôi khi nào tôi muốn được nhắc hoàn thành thiệp chúc mừng.
Khi một thông báo đã lên lịch kích hoạt trong Lời chào Fugu, thông báo đó sẽ xuất hiện giống như mọi thông báo khác, nhưng như tôi đã viết trước đây, thông báo này không yêu cầu kết nối mạng.
API Khoá chế độ thức
Tôi cũng muốn thêm API Khoá chế độ thức. Đôi khi, bạn chỉ cần nhìn chăm chú vào màn hình đủ lâu cho đến khi nguồn cảm hứng đột ngột xuất hiện. Điều tồi tệ nhất có thể xảy ra khi đó là màn hình tắt. API Khóa chế độ thức có thể ngăn điều này xảy ra.
Bước đầu tiên là lấy khoá chế độ thức bằng navigator.wakelock.request method()
.
Tôi truyền cho nó chuỗi 'screen'
để lấy khoá chế độ thức màn hình.
Sau đó, tôi thêm trình nghe sự kiện để nhận thông báo khi khoá chế độ thức được nhả ra.
Điều này có thể xảy ra, chẳng hạn như khi chế độ hiển thị thẻ thay đổi.
Nếu điều này xảy ra, tôi có thể mở lại khoá chế độ thức khi thẻ hiển thị trở lại.
let wakeLock = null;
const requestWakeLock = async () => {
wakeLock = await navigator.wakeLock.request('screen');
wakeLock.addEventListener('release', () => {
console.log('Wake Lock was released');
});
console.log('Wake Lock is active');
};
const handleVisibilityChange = () => {
if (wakeLock !== null && document.visibilityState === 'visible') {
requestWakeLock();
}
};
document.addEventListener('visibilitychange', handleVisibilityChange);
document.addEventListener('fullscreenchange', handleVisibilityChange);
Có, đây là một tính năng nâng cao tăng dần, vì vậy, tôi chỉ cần tải bản nâng cao này khi trình duyệt hỗ trợ API.
if ('wakeLock' in navigator && 'request' in navigator.wakeLock) {
import('./wake_lock.mjs');
}
Trong Fugu Greetings có một hộp đánh dấu Insomnia (Chứng mất ngủ) giúp giữ cho màn hình luôn bật.
API Phát hiện trạng thái rảnh
Đôi khi, ngay cả khi bạn chăm chú vào màn hình hàng giờ, điều đó chỉ vô ích và bạn không thể nghĩ ra sơ lược việc cần làm với thiệp chúc mừng. API Phát hiện trạng thái rảnh cho phép ứng dụng phát hiện thời gian không hoạt động của người dùng. Nếu người dùng không hoạt động quá lâu, ứng dụng sẽ đặt lại về trạng thái ban đầu và xoá canvas. API này hiện được kiểm soát sau quyền gửi thông báo, vì nhiều trường hợp sử dụng tính năng phát hiện trạng thái rảnh trong phiên bản chính thức liên quan đến thông báo, chẳng hạn như để chỉ gửi thông báo đến một thiết bị mà người dùng hiện đang sử dụng.
Sau khi chắc chắn rằng quyền gửi thông báo đã được cấp, tôi sẽ tạo thực thể cho trình phát hiện trạng thái rảnh. Tôi đăng ký một trình nghe sự kiện để theo dõi các thay đổi ở trạng thái rảnh, bao gồm cả người dùng và trạng thái màn hình. Người dùng có thể đang hoạt động hoặc ở trạng thái rảnh, và màn hình có thể được mở khoá hoặc khoá. Nếu người dùng ở trạng thái rảnh, canvas sẽ bị xoá. Tôi đặt ngưỡng là 60 giây cho trình phát hiện trạng thái rảnh.
const idleDetector = new IdleDetector();
idleDetector.addEventListener('change', () => {
const userState = idleDetector.userState;
const screenState = idleDetector.screenState;
console.log(`Idle change: ${userState}, ${screenState}.`);
if (userState === 'idle') {
clearCanvas();
}
});
await idleDetector.start({
threshold: 60000,
signal,
});
Và như mọi khi, tôi chỉ tải mã này khi trình duyệt hỗ trợ mã.
if ('IdleDetector' in window) {
import('./idle_detection.mjs');
}
Trong ứng dụng Fugu Greetings, canvas sẽ xoá khi hộp đánh dấu Ephemeral được đánh dấu và người dùng không hoạt động quá lâu.
Closing (Đang đóng)
Chà, thật là một chuyến đi. Có rất nhiều API chỉ trong một ứng dụng mẫu. Và hãy nhớ rằng tôi không bao giờ bắt người dùng phải trả chi phí tải xuống cho một tính năng mà trình duyệt của họ không hỗ trợ. Bằng cách sử dụng tính năng nâng cao tăng dần, tôi đảm bảo chỉ có mã có liên quan được tải. Và vì với HTTP/2, các yêu cầu khá rẻ nên mẫu này sẽ hoạt động tốt cho nhiều ứng dụng, mặc dù bạn có thể muốn xem xét một trình đóng gói cho các ứng dụng thực sự lớn.
Ứng dụng có thể trông hơi khác trên mỗi trình duyệt vì không phải tất cả nền tảng đều hỗ trợ tất cả các tính năng, nhưng chức năng cốt lõi thì luôn có sẵn — được cải tiến dần theo khả năng của trình duyệt cụ thể. Xin lưu ý rằng các chức năng này có thể thay đổi ngay cả trong một và cùng một trình duyệt, tuỳ thuộc vào việc ứng dụng đang chạy dưới dạng ứng dụng đã cài đặt hay trong thẻ trình duyệt.
Nếu bạn quan tâm đến ứng dụng Fugu Greetings, hãy tìm và phát triển ứng dụng này trên GitHub.
Nhóm Chromium đang nỗ lực làm cho cỏ xanh hơn khi nói đến API Fugu nâng cao. Bằng cách áp dụng tính năng nâng cao tăng dần trong quá trình phát triển ứng dụng, tôi đảm bảo rằng mọi người đều có được trải nghiệm cơ sở tốt và vững chắc, nhưng những người sử dụng trình duyệt hỗ trợ nhiều API nền tảng web hơn nữa sẽ có trải nghiệm tốt hơn nữa. Tôi rất mong được thấy những việc bạn làm được với tính năng nâng cao dần dần trong ứng dụng của bạn.
Xác nhận
Tôi rất biết ơn Christian Liebel và
Hemanth HM vì cả hai đã đóng góp cho lời chúc Fugu.
Bài viết này đã được Joe Medley và Kayce Basques đánh giá.
Jake Archibald đã giúp tôi tìm hiểu tình huống với import()
động trong ngữ cảnh trình chạy dịch vụ.