API đẩy cho phép ứng dụng web nhận thông báo do máy chủ đẩy, ngay cả khi ứng dụng web hiện không mở trong trình duyệt hoặc không chạy trên thiết bị.
- Nó có được hỗ trợ tốt không?
- Làm thế nào nó hoạt động
- Nhận được sự cho phép của người dùng
- Cách hoạt động của phía Máy chủ
- Nhận sự kiện Đẩy
API đẩy cho phép ứng dụng web nhận thông báo do máy chủ đẩy, ngay cả khi ứng dụng web hiện không mở trong trình duyệt hoặc không chạy trên thiết bị
Sử dụng API Đẩy, bạn có thể gửi tin nhắn cho người dùng của mình, đẩy họ từ máy chủ đến máy khách, ngay cả khi người dùng không duyệt trang web.
Điều này cho phép bạn gửi thông báo và cập nhật nội dung, mang lại cho bạn khả năng có được nhiều khán giả gắn bó hơn.
Điều này là rất lớn vì một trong những trụ cột còn thiếu của web di động, so với các ứng dụng gốc, là khả năng nhận thông báo cùng với hỗ trợ ngoại tuyến.
Nó có được hỗ trợ tốt không?
Push API là một bổ sung gần đây cho các API của trình duyệt và nó hiện được hỗ trợ bởi Chrome (Máy tính để bàn và Di động), Firefox và Opera kể từ năm 2016, Edge kể từ phiên bản 17 (đầu năm 2018). Xem thêm về trạng thái hiện tại của hỗ trợ trình duyệt tạihttps://caniuse.com/#feat=push-api
IE không hỗ trợ nó vàSafari có cách triển khai riêng.
Vì Chrome và Firefox hỗ trợ nó, khoảng 60% người dùng duyệt trên máy tính để bàn có quyền truy cập vào nó, vì vậy nó kháan toànđể sử dụng.
Làm thế nào nó hoạt động
Tổng quat
Khi người dùng truy cập ứng dụng web của bạn, bạn có thể kích hoạt một bảng yêu cầu quyền gửi các bản cập nhật. ANhân viên phục vụđược cài đặt và hoạt động trong nền sẽ lắng ngheSự kiện đẩy.
Đẩy và Thông báo là một khái niệm và API riêng biệt, đôi khi bị trộn lẫn vìthông báo đẩythuật ngữ được sử dụng trong iOS. Về cơ bản, API thông báo được gọi khi nhận được sự kiện đẩy bằng API đẩy.
Của bạnngười phục vụgửi thông báo cho khách hàng và Nhân viên dịch vụ, nếu được cho phép, sẽ nhận đượcsự kiện đẩy. Nhân viên Dịch vụ phản ứng với sự kiện này bằng cáchkích hoạt một thông báo.
Nhận được sự cho phép của người dùng
Bước đầu tiên khi làm việc với API Push là người dùng có quyền nhận dữ liệu từ bạn.
Nhiều trang web triển khai bảng điều khiển này không tốt, hiển thị bảng điều khiển này khi tải trang đầu tiên. Người dùng vẫn chưa tin rằng nội dung của bạn là tốt và họ sẽ từ chối cấp phép. Làm điều đó một cách khôn ngoan.
Có 6 bước:
- Kiểm tra xem Nhân viên dịch vụ có được hỗ trợ không
- Kiểm tra xem API Push có được hỗ trợ không
- Đăng ký Nhân viên Dịch vụ
- Yêu cầu quyền từ người dùng
- Đăng ký người dùng và nhận đối tượng PushSubscription
- Gửi đối tượng PushSubscription đến máy chủ của bạn
Kiểm tra xem Nhân viên dịch vụ có được hỗ trợ không
if (!('serviceWorker' in navigator)) {
// Service Workers are not supported. Return
return
}
Kiểm tra xem API Push có được hỗ trợ không
if (!('PushManager' in window)) {
// The Push API is not supported. Return
return
}
Đăng ký Nhân viên Dịch vụ
Mã này đăng ký Service Worker nằm trongworker.js
tệp được đặt trong tên miền gốc:
window.addEventListener('load', () => {
navigator.serviceWorker.register('/worker.js')
.then((registration) => {
console.log('Service Worker registration completed with scope: ',
registration.scope)
}, (err) => {
console.log('Service Worker registration failed', err)
})
})
Để biết thêm về cách làm việc chi tiết của Nhân viên dịch vụ, hãy xemHướng dẫn Nhân viên Dịch vụ.
Yêu cầu quyền từ người dùng
Bây giờ Service worker đã được đăng ký, bạn có thể yêu cầu quyền.
API để thực hiện việc này đã thay đổi theo thời gian và nó đã chuyển từ việc chấp nhận một hàm gọi lại làm tham số để trả về mộtLời hứa, phá vỡ khả năng tương thích ngược và chuyển tiếp, và chúng ta cần làmcả haivì chúng tôi không biết phương pháp tiếp cận nào được trình duyệt của người dùng triển khai.
Mã như sau, đang gọiNotification.requestPermission()
.
const askPermission = () => {
return new Promise((resolve, reject) => {
const permissionResult = Notification.requestPermission((result) => {
resolve(result)
})
if (permissionResult) {
permissionResult.then(resolve, reject)
}
})
.then((permissionResult) => {
if (permissionResult !== 'granted') {
throw new Error('Permission denied')
}
})
}
CácpermissionResult
value là một chuỗi, có thể có giá trị là: -granted
-default
-denied
Mã này khiến trình duyệt hiển thị hộp thoại quyền:
Nếu người dùng nhấp vào Chặn, bạn sẽ không thể yêu cầu quyền của người dùng nữa, trừ khi họ truy cập và bỏ chặn trang web theo cách thủ công trong bảng cài đặt nâng cao trong trình duyệt (rất khó xảy ra).
Đăng ký người dùng và nhận đối tượng PushSubscription
Nếu người dùng đã cho phép chúng tôi, chúng tôi có thể đăng ký và bằng cách gọiregistration.pushManager.subscribe()
.
const APP_SERVER_KEY = 'XXX'
window.addEventListener(‘load’, () => {
navigator.serviceWorker.register(’/worker.js’)
.then((registration) => {
askPermission().then(() => {
const options = {
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(APP_SERVER_KEY)
}
return registration.pushManager.subscribe(options)
}).then((pushSubscription) => {
// we got the pushSubscription object
}
}, (err) => {
console.log(‘Service Worker registration failed’, err)
})
})
APP_SERVER_KEY
là một chuỗi - được gọi làKhóa máy chủ ứng dụnghoặc làKhóa VAPID- xác định khóa công khai của ứng dụng, một phần của cặp khóa công khai / riêng tư.
Nó sẽ được sử dụng như một phần của xác thực vì lý do bảo mật xảy ra để đảm bảo rằng bạn (và chỉ bạn chứ không phải ai khác) có thể gửi thông báo đẩy trở lại người dùng.
Gửi đối tượng PushSubscription đến máy chủ của bạn
Trong đoạn mã trước, chúng tôi cópushSubscription
đối tượng, chứa tất cả những gì chúng ta cần để gửi thông điệp đẩy tới người dùng. Chúng tôi cần gửi thông tin này đến máy chủ của mình để có thể gửi thông báo sau này.
Trước tiên, chúng tôi tạo một biểu diễn JSON của đối tượng
const subscription = JSON.stringify(pushSubscription)
và chúng tôi có thể đăng nó lên máy chủ của chúng tôi bằng cách sử dụngAPI tìm nạp:
const sendToServer = (subscription) => {
return fetch('/api/subscription', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(subscription)
})
.then((res) => {
if (!res.ok) {
throw new Error('An error occurred')
}
return res.json()
})
.then((resData) => {
if (!(resData.data && resData.data.success)) {
throw new Error('An error occurred')
}
})
}
sendToServer(subscription)
Phía máy chủ,/api/subscription
endpoint nhận được yêu cầu POST và có thể lưu trữ thông tin đăng ký vào bộ nhớ của nó.
Cách hoạt động của phía Máy chủ
Cho đến nay, chúng tôi chỉ nói về phần phía máy khách: nhận được sự cho phép của người dùng để được thông báo trong tương lai.
Còn máy chủ thì sao? Nó phải làm gì, và nó nên tương tác với khách hàng như thế nào?
Các ví dụ phía máy chủ này sử dụng Express.js (http://expressjs.com/) dưới dạng khung HTTP cơ sở, nhưng bạn có thể viết trình xử lý API Đẩy phía máy chủ bằng bất kỳ ngôn ngữ hoặc khung công tác nào
Đăng ký gói đăng ký khách hàng mới
Khi khách hàng gửi đăng ký mới, hãy nhớ rằng chúng tôi đã sử dụng/api/subscription
Điểm cuối HTTP POST, gửi chi tiết đối tượng PushSubscription ở định dạng JSON, trong nội dung.
Chúng tôi khởi tạo Express.js:
const express = require('express')
const app = express()
Hàm tiện ích này đảm bảo yêu cầu hợp lệ, có phần thân và thuộc tính điểm cuối, nếu không, nó trả về lỗi cho máy khách:
const isValidSaveRequest = (req, res) => {
if (!req.body || !req.body.endpoint) {
res.status(400)
res.setHeader('Content-Type', 'application/json')
res.send(JSON.stringify({
error: {
id: 'no-endpoint',
message: 'Subscription must have an endpoint'
}
}))
return false
}
return true
}
Chức năng tiện ích tiếp theo lưu đăng ký vào cơ sở dữ liệu, trả về một lời hứa được giải quyết khi việc chèn hoàn tất (hoặc không thành công). CácinsertToDatabase
hàm là một trình giữ chỗ, chúng tôi sẽ không đi sâu vào các chi tiết đó ở đây:
const saveSubscriptionToDatabase = (subscription) => {
return new Promise((resolve, reject) => {
insertToDatabase(subscription, (err, id) => {
if (err) {
reject(err)
return
}
<span style="color:#a6e22e">resolve</span>(<span style="color:#a6e22e">id</span>)
})
})
}
Chúng tôi sử dụng các chức năng đó trong trình xử lý yêu cầu POST bên dưới. Chúng tôi kiểm tra xem yêu cầu có hợp lệ không, sau đó chúng tôi lưu yêu cầu và sau đó chúng tôi trả vềdata.success: true
phản hồi lại máy khách hoặc lỗi:
app.post('/api/subscription', (req, res) => {
if (!isValidSaveRequest(req, res)) {
return
}
saveSubscriptionToDatabase(req, res.body)
.then((subscriptionId) => {
res.setHeader(‘Content-Type’, ‘application/json’)
res.send(JSON.stringify({ data: { success: true } }))
})
.catch((err) => {
res.status(500)
res.setHeader(‘Content-Type’, ‘application/json’)
res.send(JSON.stringify({
error: {
id: ‘unable-to-save-subscription’,
message: ‘Subscription received but failed to save it’
}
}))
})
})
app.listen(3000, () => {
console.log(‘App listening on port 3000’)
})
Gửi tin nhắn đẩy
Bây giờ máy chủ đã đăng ký ứng dụng khách trong danh sách của nó, chúng ta có thể gửi cho nó các thông điệp Đẩy. Hãy xem cách đó hoạt động như thế nào bằng cách tạo một đoạn mã ví dụ tìm nạp tất cả các đăng ký và gửi Thông báo đẩy cho tất cả chúng cùng một lúc.
Chúng tôi sử dụng một thư viện vìGiao thức đẩy webLàphức tạpvà lib cho phép chúng ta trừu tượng hóa rất nhiều mã cấp thấp để đảm bảo rằng chúng ta có thể làm việc an toàn và xử lý chính xác mọi trường hợp cạnh.
Ví dụ này sử dụng
web-push
Node.jsthư viện (https://github.com/web-push-libs/web-push) để xử lý việc gửi tin nhắn Đẩy
Đầu tiên chúng tôi khởi tạoweb-push
lib và chúng tôi tạo một loạt các khóa riêng tư và công khai, đồng thời đặt chúng làm chi tiết VAPID:
const webpush = require('web-push')
const vapidKeys = webpush.generateVAPIDKeys()
const PUBLIC_KEY = ‘XXX’
const PRIVATE_KEY = ‘YYY’
const vapidKeys = {
publicKey: PUBLIC_KEY,
privateKey: PRIVATE_KEY
}
webpush.setVapidDetails(
‘mailto:[email protected]’,
vapidKeys.publicKey,
vapidKeys.privateKey
)
Sau đó, chúng tôi thiết lập mộttriggerPush()
, chịu trách nhiệm gửi sự kiện đẩy tới máy khách. Nó chỉ gọiwebpush.sendNotification()
và bắt bất kỳ lỗi nào. Nếu lỗi trả về mã trạng thái HTTP là410, nghĩa làKhông còn, chúng tôi xóa người đăng ký đó khỏi cơ sở dữ liệu.
const triggerPush = (subscription, dataToSend) => {
return webpush.sendNotification(subscription, dataToSend)
.catch((err) => {
if (err.statusCode === 410) {
return deleteSubscriptionFromDatabase(subscription._id)
} else {
console.log('Subscription is no longer valid: ', err)
}
})
}
Chúng tôi không triển khai việc lấy các đăng ký từ cơ sở dữ liệu, nhưng chúng tôi để nó ở dạng sơ khai:
const getSubscriptionsFromDatabase = () => {
//stub
}
Phần lớn của mã là lệnh gọi lại của yêu cầu ĐĂNG tới/api/push
điểm cuối:
app.post('/api/push', (req, res) => {
return getSubscriptionsFromDatabase()
.then((subscriptions) => {
let promiseChain = Promise.resolve()
for (let i = 0; i < subscriptions.length; i++) {
const subscription = subscriptions[i]
promiseChain = promiseChain.then(() => {
return triggerPush(subscription, dataToSend)
})
}
return promiseChain
})
.then(() => {
res.setHeader('Content-Type', 'application/json')
res.send(JSON.stringify({ data: { success: true } }))
})
.catch((err) => {
res.status(500)
res.setHeader('Content-Type', 'application/json')
res.send(JSON.stringify({
error: {
id: 'unable-to-send-messages',
message: `Failed to send the push ${err.message}`
}
}))
})
})
Những gì đoạn mã trên làm là: nó lấy tất cả các đăng ký từ cơ sở dữ liệu, sau đó nó lặp lại trên chúng và nó gọitriggerPush()
chức năng chúng tôi đã giải thích trước đây.
Sau khi đăng ký xong, chúng tôi trả về phản hồi JSON thành công, trừ khi xảy ra lỗi và chúng tôi trả về lỗi 500.
Trong thế giới thực…
Không chắc rằng bạn sẽ thiết lập máy chủ Đẩy của riêng mình trừ khi bạn có một trường hợp sử dụng rất đặc biệt, hoặc bạn chỉ muốn tìm hiểu công nghệ hoặc bạn thích tự làm. Thay vào đó, bạn thường muốn sử dụng các nền tảng như OneSignal (https://onesignal.com) xử lý minh bạch các sự kiện Đẩy tới tất cả các loại nền tảng, bao gồm Safari và iOS, miễn phí.
Nhận sự kiện Đẩy
Khi một sự kiện Đẩy được gửi từ máy chủ, làm thế nào để máy khách nhận được nó?
Đó là một bình thườngJavaScriptngười nghe sự kiện, trênpush
sự kiện chạy bên trong Service Worker:
self.addEventListener('push', (event) => {
// data is available in event.data
})
event.data
chứaPushMessageData
đối tượng hiển thị các phương thức để truy xuất dữ liệu đẩy được gửi bởi máy chủ, ở định dạng bạn muốn:
- arrayBuffer (): như một
ArrayBuffer
vật - bãi(): như mộtBãivật
- json (): phân tích cú pháp làJSON
- bản văn(): văn bản thô
Bạn sẽ thường sử dụngevent.data.json()
.
Hiển thị thông báo
Ở đây chúng tôi giao nhau một chút vớiAPI thông báo, nhưng vì một lý do chính đáng, vì một trong những trường hợp sử dụng chính của API Đẩy là hiển thị thông báo.
Bên trong của chúng tôipush
trình xử lý sự kiện trong Service Worker, chúng tôi cần hiển thị thông báo cho người dùng và yêu cầu sự kiện đợi cho đến khi trình duyệt hiển thị nó trước khi chức năng có thể kết thúc. Chúng tôi kéo dài thời gian tồn tại của sự kiện cho đến khi trình duyệt hoàn tất việc hiển thị thông báo (cho đến khi lời hứa đã được giải quyết), nếu không, Service Worker có thể bị dừng giữa chừng khi bạn đang xử lý:
self.addEventListener('push', (event) => {
const promiseChain = self.registration.showNotification('Hey!')
event.waitUntil(promiseChain)
})
Thêm thông báo trongHướng dẫn API thông báo.
Tải xuống miễn phí của tôiSổ tay dành cho Người mới bắt đầu JavaScript
Các hướng dẫn khác về trình duyệt:
- Một số thủ thuật hữu ích có sẵn trong HTML5
- Cách tôi làm cho một trang web dựa trên CMS hoạt động ngoại tuyến
- Hướng dẫn hoàn chỉnh về ứng dụng web tiến bộ
- API tìm nạp
- Hướng dẫn API đẩy
- API nhắn tin kênh
- Hướng dẫn Nhân viên Dịch vụ
- Hướng dẫn API bộ nhớ cache
- Hướng dẫn API thông báo
- Đi sâu vào IndexedDB
- API Bộ chọn: querySelector và querySelectorAll
- Tải JavaScript hiệu quả với trì hoãn và không đồng bộ
- Mô hình đối tượng tài liệu (DOM)
- API lưu trữ web: lưu trữ cục bộ và lưu trữ phiên
- Tìm hiểu cách hoạt động của HTTP Cookies
- API lịch sử
- Định dạng hình ảnh WebP
- XMLHttpRequest (XHR)
- Hướng dẫn chuyên sâu về SVG
- URL dữ liệu là gì
- Lộ trình tìm hiểu Nền tảng Web
- CORS, Chia sẻ tài nguyên đa nguồn gốc
- Nhân viên web
- Hướng dẫn requestAnimationFrame ()
- Doctype là gì
- Làm việc với Bảng điều khiển DevTools và API Bảng điều khiển
- API tổng hợp giọng nói
- Cách chờ sự kiện DOM sẵn sàng bằng JavaScript thuần túy
- Cách thêm lớp vào phần tử DOM
- Cách lặp qua các phần tử DOM từ querySelectorAll
- Cách xóa một lớp khỏi phần tử DOM
- Cách kiểm tra xem một phần tử DOM có một lớp hay không
- Cách thay đổi giá trị nút DOM
- Cách thêm sự kiện nhấp chuột vào danh sách các phần tử DOM được trả về từ querySelectorAll
- WebRTC, API web thời gian thực
- Cách lấy vị trí cuộn của một phần tử trong JavaScript
- Cách thay thế một phần tử DOM
- Cách chỉ chấp nhận hình ảnh trong trường tệp đầu vào
- Tại sao sử dụng phiên bản xem trước của trình duyệt?
- Đối tượng Blob
- Đối tượng tệp
- Đối tượng FileReader
- Đối tượng FileList
- ArrayBuffer
- ArrayBufferView
- Đối tượng URL
- Mảng đã nhập
- Đối tượng DataView
- API BroadcastChannel
- API luồng
- Đối tượng FormData
- Đối tượng Điều hướng
- Cách sử dụng API vị trí địa lý
- Cách sử dụng getUserMedia ()
- Cách sử dụng API Kéo và Thả
- Cách làm việc với tính năng cuộn trên các Trang Web
- Xử lý các biểu mẫu trong JavaScript
- Sự kiện bàn phím
- Sự kiện chuột
- Chạm vào sự kiện
- Cách xóa tất cả phần tử con khỏi phần tử DOM
- Cách tạo thuộc tính HTML bằng vanilla Javascript
- Làm cách nào để kiểm tra xem một hộp kiểm đã được chọn bằng JavaScript hay chưa?
- Cách sao chép vào khay nhớ tạm bằng JavaScript
- Cách tắt một nút bằng JavaScript
- Cách làm cho một trang có thể chỉnh sửa trong trình duyệt
- Cách lấy giá trị chuỗi truy vấn trong JavaScript với URLSearchParams
- Cách xóa tất cả CSS khỏi một trang cùng một lúc
- Cách sử dụng insertAdjacentHTML
- Safari, cảnh báo trước khi thoát
- Cách thêm hình ảnh vào DOM bằng JavaScript
- Cách đặt lại biểu mẫu
- Cách sử dụng Google Fonts