《Web 推送通知》系列翻譯 | 第七篇:推送事件 && 第八篇:顯示一個通知

閱文前端團隊發表於2019-02-28

原文地址:handling messages

譯文地址:推送事件

譯者:張卓

校對者:楊芯芯劉文濤

到此已經覆蓋了訂閱使用者並給其推送傳送訊息。下一步是在使用者的裝置上接收此推送訊息並顯示通知(以及我們可能要做的任何其他工作)。

第七篇:推送事件

當接受到一條訊息時,一個推送事件將被 dispatch 到你的 service worker 中。

監聽推送事件的程式碼和在 JavaScript 中監聽其它事件的程式碼十分類似:

self.addEventListener('push', function(event) {
  if (event.data) {
    console.log('This push event has data: ', event.data.text());
  } else {
    console.log('This push event has no data.');
  }
});
複製程式碼

對於不熟悉 service workers 的開發者來說,這段程式碼最奇怪的部分應該是變數 selfself 常用在 Web Workers 中, service workers 就是這樣的。self 是指全域性作用域,類似於瀏覽器環境中的 window。但對於 web workers 和 service workers,self 指的的 worker 本身。

在上面的例子中,可以將 self.addEventListener() 視為向 service workers 本身新增事件監聽。

在推送事件示例中,我們檢查是否有資料,並列印一些日誌到終端。

除此之外,還有其他方法可以解析推送事件中的資料:

// 返回 string
event.data.text()

// Parses data as JSON string and returns an Object
event.data.json()

// 返回 blob
event.data.blob()

// 返回 arrayBuffer
event.data.arrayBuffer()
複製程式碼

大多數人使用 json() 或者 text()(取決於他們希望從應用程式那得到什麼)。

此示例演示如何新增推送事件監聽器以及如何訪問資料,但是它 缺少兩個非常重要的功能:它沒有顯示通知,並且沒有使用 event.waitUntil()

Wait Until

必須要了解的一點是,你幾乎無法控制 service workers 的程式碼何時執行,瀏覽器決定何時將其喚醒以及何時終止它。唯一的方法是,你告訴瀏覽器:“嘿——我非常忙,要做重要的事情了”,將一個 promise 物件傳遞給 event.waitUntil() 方法,這樣,瀏覽器就會 保持 service workers 執行,直到傳入的 promise 被 resolve。

對於推送事件,還有一個必要條件是必須在 promise 被 resolve 之前顯示通知。

以下是顯示通知的基本示例:

self.addEventListener('push', function(event) {
  const promiseChain = self.registration.showNotification('Hello, World.');

  event.waitUntil(promiseChain);
});
複製程式碼

執行 self.registration.showNotification() 方法會向使用者顯示一個通知並且返回一個 promise 物件,這個 promise 物件會在通知顯示之後被 resolve。

為了讓這個例子儘可能清楚,我已經將這個 promise 物件賦值給了一個 叫做 promiseChain 的變數,然後將其傳遞給 event.waitUntil()。 我知道這裡 非常冗長,但我已經看到了許多由此引發的問題,例如, 誤解了應該傳遞給 waitUntil() 的內容,或者是一個錯誤的 promise 鏈。

一個包括網路請求資料和分析追蹤推送事件的例子如下:

self.addEventListener('push', function(event) {
  const analyticsPromise = pushReceivedTracking();
  const pushInfoPromise = fetch('/api/get-more-data')
    .then(function(response) {
      return response.json();
    })
    .then(function(response) {
      const title = response.data.userName + ' says...';
      const message = response.data.message;

      return self.registration.showNotification(title, {
        body: message
      });
    });

  const promiseChain = Promise.all([
    analyticsPromise,
    pushInfoPromise
  ]);

  event.waitUntil(promiseChain);
});
複製程式碼

這裡為了示例,我們呼叫一個返回 promise 物件的函式 pushReceivedTracking(), ,假裝將發出網路請求到我們的分析提供商。同時,我們也會傳送網路請求、獲取響應,並使用響應的資料來顯示通知的標題和內容。

我們使用 Promise.all() 將這兩個 promise 物件合併,來確保 service worker 在這兩個任務完成之前存活。合併後的 promise 被傳遞進 event.waitUntil() ,這意味著瀏覽器將等到兩個 promise 都完成後,才會檢查通知已顯示,最後終止 service worker。

注意:如果你對 promise 鏈式呼叫有些疑惑,將功能分解為函式會有助於降低複雜性,同時也推薦這篇Philip Walton 的博文來理解 promise 的鏈式呼叫。重點是你應該嘗試如何來寫 promise 以及它的鏈式呼叫,最終找到適合自己的風格。

我們應該關注 waitUntil() 以及如何使用它,因為開發人員常常會面臨的一個問題是,當 promise 鏈使用的不正確時,Chrome 會 顯示此“預設”通知:

An Image of the default notification in Chrome

當接收到一個推送訊息,但在 service worker 中的推送事件當傳遞給 event.waitUntil() 的 promise 結束之後也沒有顯示任何訊息,Chrome 就只會顯示 "This site has been updated in the background."

導致這個問題主要原因是開發者的程式碼中經常在呼叫 self.registration.showNotification() 之後在 promise 中 沒有返回任何東西,這會導致顯示預設通知。舉個例子,我們可以刪除上面示例中的 self.registration.showNotification() 的返回值,就會有看到“預設”通知的風險。

self.addEventListener('push', function(event) {
  const analyticsPromise = pushReceivedTracking();
  const pushInfoPromise = fetch('/api/get-more-data')
    .then(function(response) {
      return response.json();
    })
    .then(function(response) {
      const title = response.data.userName + ' says...';
      const message = response.data.message;

      self.registration.showNotification(title, {
        body: message
      });
    });

  const promiseChain = Promise.all([
    analyticsPromise,
    pushInfoPromise
  ]);

  event.waitUntil(promiseChain);
});
複製程式碼

你可以看到它是如何容易遺漏的。

請記住 - 如果看到該通知,請檢查 promise 鏈和 event.waitUntil()

在下一節中,我們將看看我們可以做什麼來設定通知的樣式以及可以展示什麼內容。

第八篇:顯示一個通知

原文地址:display a notification

譯文地址:顯示一個通知

譯者:劉文濤

校對者:楊芯芯 任家樂

我將通知引數分為兩部分,一部分處理視覺(本節),另一部分解釋通知的行為。

這麼做的原因是每個開發人員都需要擔心視覺方面,而行為方面則取決於你使用推送的方式。

下面所有的例子的原始碼,都來自我的一個 demo 頁面。 如果你想自己測試它們,請點選下面的按鈕。

Notification Demos

視覺顯示相關引數

顯示一個通知的 API 很簡單,如下:

<ServiceWorkerRegistration>.showNotification(<title>, <options>);
複製程式碼

title 是一個字串型別,options 的引數如下:

{
  "//": "Visual Options",
  "body": "<String>",
  "icon": "<URL String>",
  "image": "<URL String>",
  "badge": "<URL String>",
  "vibrate": "<Array of Integers>",
  "sound": "<URL String>",
  "dir": "<String of 'auto' | 'ltr' | 'rtl'>",

  "//": "Behavioural Options",
  "tag": "<String>",
  "data": "<Anything>",
  "requireInteraction": "<boolean>",
  "renotify": "<Boolean>",
  "silent": "<Boolean>",

  "//": "Both Visual & Behavioural Options",
  "actions": "<Array of Strings>",

  "//": "Information Option. No visual affect.",
  "timestamp": "<Long>"
}
複製程式碼

首先讓我們看看視覺相關的引數,如下圖:

Dissection of the UI of a Notification

title 和 body 引數

title 和 body 引數,顧名思義,即通知上顯示的兩塊不同區域的文字(標題和文字)

如果我們執行以下程式碼:

    const title = 'Simple Title';
    const options = {
      body: 'Simple piece of body text.\nSecond line of body text :)'
    };
    registration.showNotification(title, options);
複製程式碼

我們會在 chrome 中收到如下通知:

Notification with title and body text on Chrome on Linux.

在 Linux 的 Firefox 上,它看起來是這樣的:

Notification with title and body text on Firefox on Linux.

我很好奇,如果我新增了大量文字會發生什麼,其結果是:

Notification with long title and body text on Chrome on Linux.

有趣的是,Linux 上的 Firefox 截斷了正文的部分,直到滑鼠 hover 到通知上面時,會展開顯示全部

Notification with long title and body text on Firefox on Linux.
Notification with long title and body text on Firefox on Linux while hovering over the notification with the mouse cursor.

我文中加入這些例子的原因有2個。首先瀏覽器之間會有顯示上的差異。單單隻看文字,Firefox 和 Chrome 在顯示和行為上有所不同。 其次是跨平臺存在差異。 Chrome 為所有平臺提供自定義使用者介面,而 Linux 機器上的 Firefox 則使用系統通知。 相同的通知在 windows 上的 Firefox 顯示如下:

Notification with title and body text on Firefox on Windows.
Notification with long title and body text on Firefox on Windows.

Icon

引數 icon 其實就是在標題和正文旁邊展示的一張小圖。

在你的程式碼中,你只需要提供一個你想載入的圖片 URL。

    const title = 'Icon Notification';
    const options = {
      icon: '/images/demos/icon-512x512.png'
    };
    registration.showNotification(title, options);
複製程式碼

Linux 的 Chrome 上,我們收到的通知如下:

Notification with icon on Chrome on Linux.

Firefox:

Notification with icon on Firefox on Linux.

悲傷的是,圖示大小並沒有固定標準。

Android似乎想要一個64dp的影象(這是裝置畫素比例的64倍)。

如果我們假設裝置的最高畫素比例為3,那麼192畫素及以上大小的圖片是安全的。

注意:某些瀏覽器可能需要HTTPS協議頭的影象。 如果你打算使用第三方影象,請注意這一點。

Badge

badge 是一個小的單色圖示,用於向使用者展示更多資訊,告知使用者訊息是從哪裡來的。

    const title = 'Badge Notification';
    const options = {
      badge: '/images/demos/badge-128x128.png'
    };
    registration.showNotification(title, options);
複製程式碼

在寫本文時,badge 僅適用於 Android 版 Chrome。

Notification with badge on Chrome for Android.

在其他瀏覽器(或沒有指定 badge 的 Chrome)上,你會看到瀏覽器的圖示。

Notification with badge on Firefox for Android.

與 icon 引數一樣,這裡沒有關於使用什麼尺寸的 實際標準。

通過參考 Android guidelines,建議的大小是24px乘以裝置畫素比例。

也就是說使用大於72px大小的圖片應該是合適的(假設裝置的最大畫素比率為3)。

Image

image 引數可用於向使用者展示較大的圖片。尤其適合展示預覽圖。

    const title = 'Image Notification';
    const options = {
      image: '/images/demos/unsplash-farzad-nazifi-1600x1100.jpg'
    };
    registration.showNotification(title, options);
複製程式碼

在系統桌面上,通知顯示樣式如下:

Notification with image on Chrome on Linux.

在Android上,圖片展示的 裁剪方式和顯示的比例是不同的,如下圖:

Notification with image on Chrome for Android.

鑑於桌面和移動裝置之間的圖片顯示比例差異,給一個標準是極其困難的。

如上圖所示,桌面版 Chrome 中,並未完全填充,空間比例為4:3,也許最好的方法是以此比例設定圖片,並允許 Android 裁剪圖片。 話雖然是這樣說,image 引數 仍是一個新的屬性,顯示的形式是可能會改變的。

在 Android 上,我能找到的唯一的標準寬度是450dp。

基於這個標準,寬度為1350px或更高的影象將是一個不錯的選擇(假設裝置的最大畫素比率為3)。

Actions

你可以定義 actions ,來顯示帶按鈕的通知。

    const title = 'Actions Notification';
    const options = {
      actions: [
        {
          action: 'coffee-action',
          title: 'Coffee',
          icon: '/images/demos/action-1-128x128.png'
        },
        {
          action: 'doughnut-action',
          title: 'Doughnut',
          icon: '/images/demos/action-2-128x128.png'
        },
        {
          action: 'gramophone-action',
          title: 'gramophone',
          icon: '/images/demos/action-3-128x128.png'
        },
        {
          action: 'atom-action',
          title: 'Atom',
          icon: '/images/demos/action-4-128x128.png'
        }
      ]
    };

    const maxVisibleActions = Notification.maxActions;
    if (maxVisibleActions < 4) {
      options.body = `This notification will only display ` +
        `${maxVisibleActions} actions.`;
    } else {
      options.body = `This notification can display up to ` +
        `${maxVisibleActions} actions.`;
    }

    registration.showNotification(title, options);
複製程式碼

目前只有 Chrome 和 Android 中的 Opera 支援 actions 引數。

Notification with actions on Chrome on Linux.

對於每個 action,你可以定義一個 title,一個“action”(即一個 ID)和一個圖示。標題和圖示是你可以在通知中看到的內容。ID 是用來檢測操作按鈕是否已經被點選過(我們將在下一節中更詳細地介紹這一點)。

在上面的示例中,我定義了 4 個 actions,來證明你可以定義比顯示的 actions 更多的 actions。 如果你想知道瀏覽器可以顯示多少個 action 按鈕,你可以檢視演示正文中使用的 Notification.maxActions

在桌面端上,操作按鈕圖示會顯示本身的顏色(請參閱上面的粉色甜甜圈 icon)。

在 Android 6.0 Marshmallow 上,圖示被改變顏色以匹配系統配色方案:

Notification with actions on Chrome for Android.

Chrome 將有望改變在桌面端的行為 ,與 Android 相匹配(即應用適當的配色方案使圖示與系統配色相匹配)。同時,你可以手動修改圖示顏色為"#333333",來匹配 Chrome 的文字顏色。

在 Android 7.0 Nougat 上,action 圖示是根本不顯示的。

值得一提的是,這些圖示在 Android 上看起來很清晰,但在桌面上看起來那麼清晰。

在桌面版 Chrome 上使用的最佳尺寸是24px x 24px。 可惜這個在 Android 上看起來不合適。

所以,我們可以從這些差異中得出最佳實踐:

  • 給圖示選擇一致的配色方案,讓圖示可以在各端顯示保持一致。
  • 請使用單色圖示,因為有些平臺可能會以這種方式顯示它們。
  • 去測試圖示大小,看看對你來說適合的尺寸是什麼。128px * 128px對我來說在 Android 上是合適的,但在桌面上顯示,影象質量比較差。
  • 要有 action 圖示不顯示的心理預期。

Notification 規範正在探索一種可以定義多種尺寸圖示的方式,但似乎到最終達成共識之前還是需要一些時間。

Direction

“dir”引數允許你定義文字顯示的方向:從右到左或從左到右。

在測試中,顯示的方向似乎很大程度上取決於文字,而不是這個引數。根據規範,這個引數用於建議瀏覽器如何顯示文字(如同 actions 中的引數),但是並沒有什麼用。

如果需要定義文字方向的話,最好定義一下,否則瀏覽器可能會根據提供的文字按照預設的方式顯示。

該引數應可以設定為:autoltrrtl

RTL(從右向左)語言在 Linux 的 Chrome 上顯示如下:

Notification with right-to-left language on Chrome on Linux.

在 Firefox 上(當滑鼠懸停在上面時),你會得到的顯示如下:

Notification with right-to-left language on Firefox on Linux.

Vibrate

假設使用者裝置當前設定允許振動(即裝置不處於靜音模式),vibrate 引數可以讓你顯示一條通知的時候,使用振動模式。

vibrate 引數的格式是一組數字,用於描述裝置應該振動的毫秒數,後面跟著裝置不應該振動的毫秒數。

    const title = 'Vibrate Notification';
    const options = {
      // Star Wars shamelessly taken from the awesome Peter Beverloo
      // https://tests.peter.sh/notification-generator/
      vibrate: [500,110,500,110,450,110,200,110,170,40,450,110,200,110,170,40,500]
    };
    registration.showNotification(title, options);
複製程式碼

該引數只對支援振動的裝置有作用。

Sound

sound 引數允許你定義一個音訊,在收到通知時可以播放。

在寫本文時,沒有瀏覽器支援這個引數。

    const title = 'Sound Notification';
    const options = {
      sound: '/demos/notification-examples/audio/notification-sound.mp3'
    };
    registration.showNotification(title, options);
複製程式碼

Timestamp

Timestamp 引數用於告訴平臺觸發推送訊息事件的時間。

timestamp 引數 是從00:00:00,即1970年1月1日(即 unix 時間)開始的毫秒數。

    const title = 'Timestamp Notification';
    const options = {
      body: 'Timestamp is set to "01 Jan 2000 00:00:00".',
      timestamp: Date.parse('01 Jan 2000 00:00:00')
    };
    registration.showNotification(title, options);
複製程式碼

使用者體驗最佳實踐

在通知中,資訊的顯示缺乏獨特性是我所見最失敗的使用者體驗。

首先你應該考慮為什麼要推送這個通知,並且確保所有的通知引數都能幫助使用者理解為什麼他們需要閱讀這個通知。

看例子很容易,你會覺得“我永遠不會犯這個錯”,但是掉入這個陷阱比你想象的要更容易。

下面是一些我們需要避免的常見陷阱

  • 不要把你的網站地址放在標題或正文中。 瀏覽器在通知的時候會包含你的域名,所以不要重複顯示
  • 使用你可用的所有資訊。 如果你傳送推送訊息是要表達有人向使用者傳送了訊息,不應該使用標題為“新訊息”,正文內容為“點選此處閱讀該訊息”的方式。而是應該使用標題為“約翰剛剛傳送了一條新訊息”,正文為部分訊息的方式去呈現。

瀏覽器支援度

在寫本文時,Chrome 和 Firefox 在通知功能支援方面存在很大差異。

幸運的是,你可以通過檢視 Notification 原型來檢測瀏覽器對通知功能的支援。

如果我們想知道通知是否支援操作按鈕,我們會執行以下操作:

if ('actions' in Notification.prototype) {
  // Action buttons are supported.
} else {
  // Action buttons are NOT supported.
}
複製程式碼

有了這個,我們可以更改我們向使用者展示的通知。

使用其他引數,只需執行與上面相同的方法,將 “actions” 替換為所需的引數名稱。

更多分享,請關注YFE:

《Web 推送通知》系列翻譯 | 第七篇:推送事件 && 第八篇:顯示一個通知

上一篇:《Web 推送通知》系列翻譯 | 第五篇:使用 Web 推送庫傳送訊息 && 第六篇:Web 推送協議

下一篇:《Web 推送通知》系列翻譯 | 第九篇:通知行為 && 第十篇:常用的通知模式

相關文章