共用方式為


在 Windows Widgets 板上顯示 PWA 小工具

各種作業系統都有小工具、儀表板,讓使用者能閱讀內容並執行任務。 例如Android主畫面小工具、macOS儀表板與今日面板小工具、Apple Touch Bar、三星每日卡片、Mini App小工具,以及智慧手錶應用程式的夥伴。

在 Windows 11 中,小工具會出現在小工具板,你可以從工作列左側開啟:

Windows 11 中的小工具板

在Windows 11中,Progressive Web Apps (PWA) 能定義小工具、更新元件,並處理使用者互動。

需要為 PWA 建立自訂小工具

現有的 PWA 不能像 Microsoft Edge 側邊欄那樣直接放進小工具儀表板。 相反地,你需要打造一個適合該小工具主機的自訂小工具體驗,目前的主機是 Windows 11 小工具板。 (未來可能會有其他小工具主機。) Windows 11 小工具板要求小工具必須使用自適應卡片範本而非 HTML 和 JavaScript,因此小工具必須與應用程式其他介面分開設計。

另請參閱:

若要建立一個以 PWA 驅動的小工具並透過 Microsoft Store 交付,則不需要 C++/C# 程式碼。 一旦你製作好小工具,並能成功從公開端點安裝並執行小工具,就可以用 PWABuilder.com 打包應用程式,並將應用程式發佈到 Microsoft 商店,無需額外程式碼。 支撐小工具的 PWA 必須能從公開端點安裝,因為 PWABuilder 不支援從 localhost.

另請參閱:

安裝 WinAppSDK 並啟用開發者模式

為了在您的本地機器上啟用小工具的開發與測試:

  • 安裝 WinAppSDK 1.2

  • 在 Windows 11 啟用開發者模式:

    1. 開啟 設定

    2. 「尋找設定 」文字框中輸入 developer,然後點選 「使用開發者功能」。

    3. 啟用 開發者模式

      Windows 11 的開發者設定

定義元件

小工具是在你的 PWA 清單檔案中,透過 manifest widgets 成員定義的。 此 manifest 成員是一個陣列,可包含多個元件定義。

{
  "name": "PWAmp",
  "description": "A music player app",
  "icons": [
    { "src": "img/icon-96.png", "sizes": "96x96" },
    { "src": "img/icon-128.png", "sizes": "128x128" },
    { "src": "img/icon-256.png", "sizes": "256x256" },
    { "src": "img/icon-512.png", "sizes": "512x512" }
  ],
  "widgets": [
    /* widget definitions go here */
  ]
}

陣列中的 widgets 每個項目包含多個欄位,如下所示:

{
  ...
  "widgets": [
    {
      "name": "PWAmp mini player",
      "description": "widget to control the PWAmp music player",
      "tag": "pwamp",
      "template": "pwamp-template",
      "ms_ac_template": "widgets/mini-player-template.json",
      "data": "widgets/mini-player-data.json",
      "type": "application/json",
      "screenshots": [
        {
          "src": "./screenshot-widget.png",
          "sizes": "600x400",
          "label": "The PWAmp mini-player widget"
        }
      ],
      "icons": [
        {
          "src": "./favicon-16.png",
          "sizes": "16x16"
        }
      ],
      "auth": false,
      "update": 86400
    }
  ]
}

在上述範例中,音樂播放器應用程式定義了一個迷你播放器小工具。 網頁應用程式清單中的元件定義包含以下必填與可選欄位:

欄位 描述 必要項目
name 小工具的標題,呈現給使用者。
short_name 這是名稱的另一個簡短版本。
description 說明這個小工具的功能。
icons 一組用於小工具的圖示陣列。 若缺少 icons ,則改用清單成員。 超過1024x1024的圖示會被忽略。
screenshots 一組截圖顯示小工具的樣貌。 screenshot類似於顯現成員platform截圖項目的欄位支援 Windowsany 值。 超過1024x1024像素的影像會被忽略。 關於 Windows 11 小工具板的截圖需求,請參閱與小工具選擇器整合中的截圖圖片需求
tag 一個用於 PWA 服務工作者中參考元件的字串。
template 用來在作業系統小工具儀表板中顯示該小工具的範本。 注意:此物業目前僅供參考,未使用。 詳見 ms_ac_template 下方。
ms_ac_template 自訂 Adaptive Cards 範本的網址,用於在作業系統 Widgets 儀表板中顯示該元件。 請參見下方 定義小工具範本
data 可以找到要填滿範本資料的網址。 若存在,該 URL 必須回傳有效的 JSON。
type widget 資料的 MIME 類型。
auth 一個布林值,表示該小工具是否需要認證。
update 小工具更新的頻率(以秒計)。 你的服務人員必須執行更新;小工具不會自動更新。 請參閱 執行時的存取元件實例
multiple 一個布林值,表示是否允許多個元件實例。 預設為 true

定義一個小工具範本

為了讓小工具易於建立並適應各種作業系統小工具儀表板,它們以範本顯示。 範本有兩種類型:

  • 通用範本,並以欄位 template 名稱定義。
  • 自訂範本,透過其網址和自訂範本欄位定義。

目前僅支援自訂的自適應卡片範本。 Adaptive Cards 是一種開放式卡片交換格式,可以用來以共通且一致的方式交換 UI 內容。 詳見 自適應卡概覽

要在 Windows 11 上定義自訂的 Adaptive Cards 範本,請使用ms_ac_template你網頁應用程式清單中widget定義中的欄位。 雖然 template 目前沒有使用,但這是必填欄位。

{
  ...
  "template": "pwamp-template",
  "ms_ac_template": "widgets/mini-player.json",
  ...
}

ms_ac_template欄位值應該是範本檔案的有效 URL。

這裡有一個自適應卡片範本的範例:

{
  "type": "AdaptiveCard",
  "body": [
    {
      "type": "TextBlock",
      "size": "Medium",
      "text": "Now playing...",
      "horizontalAlignment": "Center"
    },
    {
      "type": "TextBlock",
      "spacing": "Large",
      "weight": "Bolder",
      "horizontalAlignment": "Center",
      "text": "${song}, by ${artist}",
    }
  ],
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "version": "1.5"
}

欲了解更多,請參閱 Adaptive Cards Templateation

接著,你需要將資料綁定到你的範本。

將資料綁定到你的範本

範本宣告了小工具的使用者介面。 資料接著會填充這個使用者介面。

要將資料綁定到範本,請使用元件定義中的欄位 data 。 這個欄位應該設定為能回傳有效 JSON 資料的 URL。

前一節定義的範本包含兩個變數:songartist,這些變數被包含在綁定式語法中:${}。 你小工具定義中 URL 回傳 data 的資料應該包含這些變數的值。

以下是網址 data 可能回傳的範例:

{
  "song": "I Will Always Love You",
  "artist": "Whitney Houston"
}

定義元件動作

如果你希望你的小工具讓使用者執行任務,請定義一個支援動作的範本。

以下是自訂自適應卡片範本中定義的動作範例:

{
  "type": "AdaptiveCard",
  "body": [
    {
      "type": "TextBlock",
      "size": "Medium",
      "text": "Now playing...",
      "horizontalAlignment": "Center"
    },
    {
      "type": "TextBlock",
      "spacing": "Large",
      "weight": "Bolder",
      "horizontalAlignment": "Center",
      "text": "${song}, by ${artist}",
    }
  ],
  "actions": [
    {
      "type": "Action.Execute",
      "title": "Previous",
      "verb": "previous-song"
    },
    {
      "type": "Action.Execute",
      "title": "Next",
      "verb": "next-song"
    }
  ],
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "version": "1.5"
}

請注意 verb 上述 JSON 範本中的欄位。 它會在處理服務工作者程式碼中元件動作時使用。 請參見 處理小工具動作

執行時存取小工具實例

你可以從 PWA 服務工作者的程式碼存取並更新小工具。 執行時存取元件在以下情況下非常有用:

服務工作者可以存取 self.widgets 物件及多個元件事件,這些事件共同構成一個 API,用來在執行時回應變更並存取元件。

以下章節提供程式碼範例。 關於 API 的參考,請參見服務工作者 API 參考。

安裝時的渲染元件

當 PWA 安裝時,應用程式在清單中定義的元件會被加入元件的儀表板,但尚未安裝。 只有當使用者選擇從儀表板新增該元件時,元件才會被安裝。

安裝小工具時,不會自動使用 ms_ac_template 小工具定義中的 和 data 欄位來渲染。

要渲染小工具,請在你的服務工作者中監聽事件, widgetinstall 然後透過以下 widgets.updateByTag 函式更新小工具:

// Listen to the widgetinstall event.
self.addEventListener("widgetinstall", event => {
  // The widget just got installed, render it using renderWidget.
  // Pass the event.widget object to the function.
  event.waitUntil(renderWidget(event.widget));
});

async function renderWidget(widget) {
  // Get the template and data URLs from the widget definition.
  const templateUrl = widget.definition.msAcTemplate;
  const dataUrl = widget.definition.data;

  // Fetch the template text and data.
  const template = await (await fetch(templateUrl)).text();
  const data = await (await fetch(dataUrl)).text();

  // Render the widget with the template and data.
  await self.widgets.updateByTag(widget.definition.tag, {template, data});
}

服務工作者更新元件

當 PWA 中的服務工作者程式碼變更時,瀏覽器會偵測到該變更,安裝新的服務工作者,然後再啟用該服務工作者。

當這種情況發生時,更新可能已經在執行的小工具實例非常重要。 元件可能在服務工作者 activate 事件發出前就已安裝。 為避免顯示空的元件,事件發生時 activate 請更新元件

// Update the widgets to their initial states
// when the service worker is activated.
self.addEventListener("activate", event => {
  event.waitUntil(updateWidgets());
});

async function updateWidgets() {
  // Get the widget that match the tag defined in the web app manifest.
  const widget = await self.widgets.getByTag("pwamp");
  if (!widget) {
    return;
  }

  // Using the widget definition, get the template and data.
  const template = await (await fetch(widget.definition.msAcTemplate)).text();
  const data = await (await fetch(widget.definition.data)).text();

  // Render the widget with the template and data.
  await self.widgets.updateByTag(widget.definition.tag, {template, data});
}

處理小工具動作

如果元件範本包含動作,使用者可以透過點擊渲染元件中的按鈕來執行這些動作。 關於如何在範本中定義動作的資訊,請參見 定義元件動作

當使用者執行元件動作時,PWA 服務工作者會觸發事件 widgetclick 。 要處理使用者動作,請聆聽事件:

self.addEventListener('widgetclick', (event) => {
  switch (event.action) {
    case 'previous-song':
      // Application logic to play the previous song...
      break;
    case 'next-song':
      // Application logic to play the next song...
      break;
  }
});

為了簡潔起見,上述程式碼片段中並未顯示實際的應用程式代碼。 當 previous-song 收到 or next-song 動作時,可能需要透過 Client.postMessage 發送訊息給應用程式,告知應用程式應該開始播放之前或接下來的歌曲。

請注意, action 傳遞給事件監聽器的物件屬性 widgetEvent 與小工具範本欄位中定義 action.verb 的字串相符。

關於本次 widgetclick 活動的更多資訊及可取得的資訊,請參閱下方 的服務工作者 API 參考資料

應用程式變更時更新元件

在之前的章節中,你學會了當特定元件事件、元件動作和服務工作者更新時,如何更新元件。 當應用程式發生問題、推播通知或定期更新元件時,也非常有用。

在本節中,您將學習如何使用週期性背景同步 API 來定期更新小工具。 欲了解更多關於週期性背景同步 API 的資訊,請參見 「使用週期性背景同步 API 以定期獲取新鮮內容」。

在以下程式碼片段中,事件監聽器被用來回應應用程式小工具的各種生命週期事件。 當偵測到元件安裝時,會註冊週期性同步;當偵測到元件移除時,週期性同步會被取消註冊。

當週期性同步事件發生時,widget 實例會透過該 widgets.updateByTag 函式進行更新。

self.addEventListener("widgetinstall", event => {
  event.waitUntil(onWidgetInstall(event.widget));
});

self.addEventListener("widgetuninstall", event => {
  event.waitUntil(onWidgetUninstall(event.widget));
});

async function onWidgetInstall(widget) {
  // Register a periodic sync, if this wasn't done already.
  // We use the same tag for the sync registration and the widget to
  // avoid registering several periodic syncs for the same widget.
  const tags = await self.registration.periodicSync.getTags();
  if (!tags.includes(widget.definition.tag)) {
    await self.registration.periodicSync.register(widget.definition.tag, {
      minInterval: widget.definition.update
    });
  }

  // And also update the instance.
  await updateWidget(widget);
}

async function onWidgetUninstall(widget) {
  // On uninstall, unregister the periodic sync.
  // If this was the last widget instance, then unregister the periodic sync.
  if (widget.instances.length === 1 && "update" in widget.definition) {
    await self.registration.periodicSync.unregister(widget.definition.tag);
  }
}

// Listen to periodicsync events to update all widget instances
// periodically.
self.addEventListener("periodicsync", async event => {
  const widget = await self.widgets.getByTag(event.tag);

  if (widget && "update" in widget.definition) {
    event.waitUntil(updateWidget(widget));
  }
});

async function updateWidget(widget) {
  // Get the template and data URLs from the widget definition.
  const templateUrl = widget.definition.msAcTemplate;
  const dataUrl = widget.definition.data;

  // Fetch the template text and data.
  const template = await (await fetch(templateUrl)).text();
  const data = await (await fetch(dataUrl)).text();

  // Render the widget with the template and data.
  await self.widgets.updateByTag(widget.definition.tag, {template, data});
}

示範應用程式

PWAmp 是一款音樂播放器 PWA 示範應用程式,定義了一個小工具。 PWAmp 小工具讓使用者能視覺化目前的歌曲,並播放之前或下一首歌。

  1. 如果還沒完成,請安裝 WinAppSDK 1.2,並在 Windows 11 啟用開發者模式。

  2. PWAmp 在 Windows 11 上安裝這個應用程式。

  3. 按下 Windows 標誌鍵 + W 開啟 Windows 11 小工具板。

  4. 點選 新增小工具 以開啟 小工具設定 畫面,捲動到 PWAmp 迷你播放器 小工具並新增。

  5. 關閉 小工具的設定 畫面。 PWAmp 迷你播放器現在顯示在小工具板中。

PWAmp 小工具會顯示目前的歌曲,並按按鈕播放前一首或下一首歌。

Windows Widgets 看板,位於 PWAmp 示範應用程式旁邊。小工具板包含 PWAmp 迷你播放器小工具,顯示 PWAmp 應用程式中正在播放的歌曲

Service Worker API 參考

服務工作者全域物件 (或 ServiceWorkerGlobalScope) 包含 widgets 一個屬性,可揭露以下基於承諾的方法:

方法 描述 參數 傳回值
getByTag(tag) 會依標籤獲得一個小工具。 widget 標籤 一個 Promise 會解析到與標籤相符的 widget 物件 ,或 undefined
getByInstanceId(id) 會依實例 ID 取得一個小工具。 widget 實例 ID 一個 Promise 可解析為對應 的元件物件,或 undefined
getByHostId(id) 依照主機 ID 取得小工具。 主機識別碼 一組在該主機中發現的 widget 物件 陣列。
matchAll(options) 透過匹配選項獲得小工具。 一個 widgetOptions 物件 一個承諾,解析為符合options條件的元件物件陣列。
updateByInstanceId(id, payload) 透過實例 ID 匯報小工具。 實例 ID 以及 一個 widgetPayload 物件 一個承諾,其解析為 undefinedError
updateByTag(tag, payload) 依標籤匯報小工具。 widget 標籤,以及 widgetPayload 物件 一個承諾,其解析為 undefinedError

服務工作者全域物件也定義了以下事件:

  • widgetinstall:當小工具主機安裝小工具時會觸發。
  • widgetuninstall:當小工具主機正在卸載小工具時會觸發。
  • widgetresume:當小工具主機恢復渲染已安裝的小工具時會觸發,這通常是在主機為了節省資源而暫停小工具渲染後發生的。
  • widgetclick:當使用者執行其中一個小工具動作時會觸發。

欲了解更多包含這些事件的物件,請參閱下方的 widgetEvent 物件widgetClickEvent 物件

widget 物件

每個小工具都被表示為一個 widget 物件,包含以下屬性:

widgetOptions 物件

當使用 matchAll(options) 多個元件時,必須有一個 widgetOptions 物件來篩選要回傳哪些元件。 該 widgetOptions 物件包含以下屬性,且皆為可選:

  • installable: 一個布林值,指示回傳的小工具是否應該可安裝。
  • installed: 一個布林值,表示返回的小工具是否安裝在小工具主機中。
  • tag: 一個用來依標籤過濾回傳元件的字串。
  • instanceId: 一個用來依實例 ID 過濾回傳元件的字串。
  • hostId: 一個用來依據 widget 主機 ID 過濾回傳元件的字串。

widgetPayload 物件

在建立或更新元件實例時,服務工作者必須傳送範本及填入元件所需的資料。 範本與資料稱為 有效載荷。 該 widgetPayload 物件包含以下屬性:

  • template: 範本,作為字串,用來渲染小工具。 這將是 Adaptive Card 範本的串聯 JSON 檔案。
  • data:資料,作為字串,用於與小工具範本一起使用。 這些資料可以是串聯的 JSON 資料。

widgetInstance 物件

此物件代表元件主機中某個元件的給定實例,並包含以下屬性:

  • id:用於參考實例的內部 GUID 字串。
  • host:一個內部指標,指向安裝此實例的元件主機。
  • updated:一個 Date 代表上次資料傳送到實例的時間的物件。
  • payload: 一個 widgetPayload 物件 ,代表最後一次傳送到此實例的有效載荷。

widgetDefinition 物件

此物件代表元件的原始定義,該定義位於 PWA 清單檔案中。 此物件的屬性與上述 定義元件中列出的屬性相符。

widgetEvent 物件

此物件作為參數傳遞給 類型為 widgetinstallwidgetuninstallwidgetresume的服務工作者小工具事件的監聽者。

對於 widgetinstall、 和 事件widgetresume類型,該widgetEvent物件widgetuninstall具有以下屬性:

屬性 描述 類型
widget 觸發事件的 widget 實例。 小工具
instanceId widget 實例 ID。 String
hostId 小工具主機 ID。 String

widgetClickEvent 物件

此物件作為參數傳遞給 型別 widgetclick為 的服務工作者小工具事件的監聽者。 你可以根據事件開啟應用程式視窗 widgetclick ,方法是使用 clients.openWindow()

widgetClickEvent 物件具有以下特性:

屬性 描述 類型
action 觸發事件的動作,依據小工具範本欄位定義 actions.verb 。 參見 定義小工具動作 String
widget 觸發事件的 widget 實例。 widgetInstance
hostId 小工具主機 ID。 String
instanceId widget 實例 ID。 String