Compilación de widgets controlados por PWA

Varios sistemas operativos tienen paneles de widgets que permiten a los usuarios leer contenido y realizar tareas. Algunos ejemplos de esto son widgets de la pantalla principal de Android, widgets del panel de macOS y del panel de hoy, apple touch bar, tarjetas diarias samsung, widgets de miniaplicaciones y complementos de aplicaciones watch inteligentes.

En Windows 11, los widgets aparecen en el Panel de widgets, que se abre desde el lado izquierdo de la barra de tareas:

El panel de widgets de Windows 11

En Windows 11, la Web Apps progresiva (PWA) puede definir widgets, actualizarlos y controlar las interacciones del usuario dentro de ellos.

Requiere la creación de un widget personalizado para la PWA.

Una PWA existente no se puede colocar simplemente en el panel del widget tal como está, como puede hacer con la barra lateral de Microsoft Edge. En su lugar, debe crear una experiencia de widget personalizada que sea adecuada para el host del widget, que actualmente es el panel de widgets de Windows 11. (Puede haber otros hosts de widget en el futuro). El panel de widgets de Windows 11 requiere que los widgets se compilen mediante plantillas de tarjeta adaptable en lugar de HTML y JavaScript, por lo que el widget debe diseñarse por separado del resto de la interfaz de usuario de la aplicación.

Vea también:

Para compilar un widget controlado por PWA y entregarlo a través de Microsoft Store, no se requiere código de C++/C#. Una vez que haya generado el widget y pueda instalar y ejecutar correctamente el widget desde un punto de conexión público, puede empaquetar la aplicación mediante PWABuilder.com y enviar la aplicación a Microsoft Store sin necesidad de código adicional. La copia de seguridad de PWA del widget debe ser instalable desde un punto de conexión público, ya que PWABuilder.com no admite el empaquetado de aplicaciones desde localhost.

Vea también:

Instalar WinAppSDK y habilitar el modo de desarrollador

Para habilitar el desarrollo y la prueba de widgets en el equipo local:

  • Instale WinAppSDK 1.2.

  • Habilite el modo de desarrollador en Windows 11:

    1. Abra Configuración.

    2. En el cuadro de texto Buscar una configuración , escriba developery, a continuación, haga clic en Usar características para desarrolladores.

    3. Habilitar modo de desarrollador:

      Configuración del desarrollador de Windows 11

Definición de widgets

Los widgets se definen en el archivo de manifiesto PWA mediante el miembro del widgets manifiesto. Este miembro de manifiesto es una matriz que puede contener varias definiciones de widget.

{
  "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 */
  ]
}

Cada entrada de la widgets matriz contiene varios campos, como se muestra a continuación:

{
  ...
  "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
    }
  ]
}

En el ejemplo anterior, una aplicación de reproductor de música define un widget de mini reproductor. Una definición de widget en el manifiesto de aplicación web tiene los siguientes campos obligatorios y opcionales:

Campo Descripción Obligatorio
name Título del widget, presentado a los usuarios. Yes
short_name Una versión abreviada alternativa del nombre. No
description Descripción de lo que hace el widget. Yes
icons Matriz de iconos que se usarán para el widget. Si falta, se usa el miembro del icons manifiesto en su lugar. Los iconos mayores que 1024x1024 se omiten. No
screenshots Matriz de capturas de pantalla que muestran el aspecto del widget. Análogo al miembro del screenshot manifiesto. El platform campo de un elemento de captura de pantalla admite los Windows valores y any . Las imágenes de más de 1024 x 1024 píxeles se omiten. Para ver los requisitos de captura de pantalla específicos del panel de widgets de Windows 11, vea Requisitos de imagen de captura de pantalla en Integrar con el selector de widgets. Yes
tag Cadena que se usa para hacer referencia al widget en el trabajo del servicio PWA. Yes
template Plantilla que se va a usar para mostrar el widget en el panel de widgets del sistema operativo. Nota: Esta propiedad es actualmente solo informativa y no se usa. Vea ms_ac_template a continuación. No
ms_ac_template Dirección URL de la plantilla de tarjetas adaptables personalizada que se va a usar para mostrar el widget en el panel de widgets del sistema operativo. Consulte Definición de una plantilla de widget a continuación. Yes
data Dirección URL con la que se pueden encontrar los datos con los que se va a rellenar la plantilla. Si está presente, esta dirección URL es necesaria para devolver json válido. No
type Tipo MIME para los datos del widget. No
auth Boolean que indica si el widget requiere autenticación. No
update Frecuencia, en segundos, en la que se actualizará el widget. El código del trabajo de servicio debe realizar la actualización; el widget no se actualiza automáticamente. Consulte Acceso a instancias de widget en tiempo de ejecución. No
multiple Un valor booleano que indica si se van a permitir varias instancias del widget. Valores predeterminados de true. No

Definición de una plantilla de widget

Para que los widgets sean fáciles de crear y adaptar a varios paneles de widgets del sistema operativo, se muestran mediante plantillas. Existen dos tipos de plantillas:

  • Plantillas genéricas, definidas por sus nombres mediante el template campo .
  • Plantillas personalizadas, definidas por sus direcciones URL mediante un campo de plantilla personalizado.

Por el momento, solo se admiten plantillas de tarjetas adaptables personalizadas. Tarjetas adaptables es un formato de intercambio de tarjetas abiertas que se puede usar para intercambiar contenido de la interfaz de usuario de una manera común y coherente. Consulte Introducción a las tarjetas adaptables.

Para definir una plantilla personalizada de tarjetas adaptables en Windows 11, use el ms_ac_template campo de la definición del widget que se encuentra en el manifiesto de la aplicación web. Aunque template no se usa actualmente, es un campo obligatorio.

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

El valor del ms_ac_template campo debe ser una dirección URL válida de un archivo de plantilla.

Este es un ejemplo de una plantilla de tarjetas adaptables:

{
  "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"
}

Para más información, consulte Plantillas de tarjetas adaptables.

A continuación, debe enlazar datos a la plantilla.

Enlace de datos a la plantilla

La plantilla declara la interfaz de usuario de un widget. A continuación, los datos rellenan esta interfaz de usuario.

Para enlazar datos a la plantilla, use el campo en la data definición del widget. Este campo debe establecerse en una dirección URL que devuelva datos JSON válidos.

La plantilla definida en la sección anterior contiene dos variables: song y artist, que se incluyen en la sintaxis de la expresión de enlace: ${}. Los datos devueltos por la dirección URL de la data definición del widget deben contener valores para estas variables.

Este es un ejemplo de lo que podría devolver la data dirección URL:

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

Definir acciones de widget

Si desea que el widget permita a los usuarios realizar tareas, defina una plantilla que admita acciones.

Este es un ejemplo de una acción definida en una plantilla personalizada de tarjetas adaptables:

{
  "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"
}

Anote el verb campo de la plantilla JSON anterior. Se usará al controlar las acciones del widget en el código de trabajo del servicio. Consulte Controlar acciones de widget.

Acceso a instancias de widget en tiempo de ejecución

Puede acceder a widgets y actualizarlos desde el código de trabajo del servicio PWA. El acceso a widgets en tiempo de ejecución es útil en casos como:

Los trabajadores de servicio tienen acceso al self.widgets objeto y a varios eventos de widget que, juntos, constituyen una API que se usa para reaccionar a los cambios y acceder a los widgets en tiempo de ejecución.

En las secciones siguientes se proporcionan ejemplos de código. Para obtener una referencia de la API, consulte la referencia de api de trabajo de servicio.

Representación de widgets en la instalación

Cuando se instala un PWA, los widgets que define la aplicación en su manifiesto se agregan al panel de widgets, pero aún no están instalados. Un widget solo se instala cuando el usuario elige agregar el widget desde el panel.

Cuando se instala un widget, no se representa automáticamente mediante los ms_ac_template campos y data de la definición del widget.

Para representar el widget, escuche el evento en el widgetinstall trabajo de servicio y actualice el widget mediante la widgets.updateByTag función :

// 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});
}

Actualización de widgets en las actualizaciones de service worker

Cuando el código de trabajo del servicio cambia en un PWA, el explorador detecta ese cambio, instala el nuevo trabajo de servicio y, a continuación, lo activa.

Cuando esto sucede, es importante actualizar las instancias de widget que ya se estén ejecutando. Es posible que se hayan instalado widgets antes de que se emita el evento de trabajo activate del servicio. Para evitar mostrar widgets vacíos, actualice los widgets cuando se produzca el activate evento.

// 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});
}

Controlar acciones de widget

Si la plantilla de widget contiene acciones, los usuarios pueden ejecutar esas acciones haciendo clic en los botones del widget representado. Para obtener información sobre cómo definir acciones en una plantilla, vea Definir acciones de widget.

Cuando un usuario ejecuta una acción de widget, se desencadena un widgetclick evento en el trabajo del servicio PWA. Para controlar la acción del usuario, escuche el evento:

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;
  }
});

Para mayor brevedad, el código de aplicación real no se muestra en el fragmento de código anterior. Cuando se reciben las previous-song acciones o next-song , es probable que deba enviarse un mensaje a la aplicación mediante Client.postMessage para que la aplicación sepa que debe empezar a reproducir las canciones anteriores o siguientes.

Tenga en cuenta que la action propiedad del widgetEvent objeto pasado al agente de escucha de eventos anterior coincide con la cadena definida en el action.verb campo de la plantilla de widget.

Para obtener más información sobre el widgetclick evento y la información a la que puede acceder desde él, consulte la referencia de la API de Service Worker a continuación.

Actualización de widgets en los cambios de la aplicación

En las secciones anteriores, ha aprendido a actualizar widgets cuando se han producido eventos de widget específicos, acciones de widget y actualizaciones de service worker. También puede ser útil actualizar widgets cuando ocurre algo en la aplicación, o cuando se produce una notificación push, o periódicamente.

En esta sección, aprenderá a usar la API de sincronización en segundo plano periódica para actualizar los widgets periódicamente. Para obtener más información sobre la API de sincronización en segundo plano periódico, consulte Uso de la API de sincronización en segundo plano periódica para obtener contenido nuevo periódicamente.

En el siguiente fragmento de código, se usa un agente de escucha de eventos para reaccionar a varios eventos de ciclo de vida del widget de aplicación. Cuando se detecta una instalación de widget, se registra una sincronización periódica y, cuando se detecta una eliminación de widget, se anula el registro de la sincronización periódica.

Cuando se producen eventos de sincronización periódicos, las instancias de widget se actualizan mediante la widgets.updateByTag función .

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});
}

Aplicación de demostración

PWAmp es una aplicación de demostración de PWA de reproductor de música que define un widget. El widget PWAmp permite a los usuarios visualizar la canción actual y reproducir las canciones anteriores o siguientes.

  1. Si aún no lo ha hecho, instale WinAppSDK 1.2 y habilite el modo de desarrollador en Windows 11.

  2. Vaya a PWAmp e instale la aplicación en Windows 11.

  3. Abra el panel de widgets Windows 11 presionando la tecla del logotipo de Windows + W.

  4. Haga clic en Agregar widgets para abrir la pantalla de configuración de widgets , desplácese hasta el widget de mini reproductor PWAmp y agréguelo.

  5. Cierre la pantalla de configuración de widgets . El mini-reproductor PWAmp ahora se muestra en el Panel de widgets.

El widget PWAmp muestra la canción actual y los botones para reproducir la canción anterior o siguiente.

Panel de widgets de Windows, junto a la aplicación de demostración PWAmp. El panel de widgets contiene el widget de mini reproductor pwamp, que muestra la canción actual que se reproduce en la aplicación PWAmp.

Referencia de la API de Service Worker

El objeto global de trabajo de servicio (o ServiceWorkerGlobalScope) contiene un widgets atributo que expone los siguientes métodos basados en Promise:

Método Descripción Parameters Valor devuelto
getByTag(tag) Obtener un widget por etiqueta La etiqueta de widget Promesa que se resuelve en el objeto de widget que coincide con la etiqueta o undefined.
getByInstanceId(id) Obtención de un widget por identificador de instancia Identificador de instancia del widget Promesa que se resuelve en el objeto de widget correspondiente, o undefined.
getByHostId(id) Obtención de widgets por identificador de host El identificador de host Matriz de objetos de widget que se encuentran en ese host.
matchAll(options) Obtención de widgets mediante opciones de coincidencia Un objeto widgetOptions Promesa que se resuelve en una matriz de objetos de widget que coinciden con los options criterios.
updateByInstanceId(id, payload) Actualización de un widget por identificador de instancia El identificador de instancia y un objeto widgetPayload Promesa que se resuelve en undefined o Error.
updateByTag(tag, payload) Actualización de un widget por etiqueta La etiqueta de widget y un objeto widgetPayload Promesa que se resuelve en undefined o Error.

El objeto global del trabajo de servicio también define los siguientes eventos:

  • widgetinstall: se desencadena cuando el host del widget instala un widget.
  • widgetuninstall: se desencadena cuando el host del widget desinstala un widget.
  • widgetresume: se desencadena cuando el host del widget reanuda la representación de widgets instalados, lo que puede ocurrir después de que el host suspendiera la representación de widgets para conservar los recursos.
  • widgetclick: se desencadena cuando el usuario ejecuta una de las acciones del widget.

Para obtener más información sobre los objetos que se proporcionan con estos eventos, vea las definiciones de objeto widgetEvent y widgetClickEvent a continuación.

widget (objeto)

Cada widget se representa como un widget objeto, que contiene las siguientes propiedades:

widgetOptions (objeto)

Cuando se usa matchAll(options) para obtener varios widgets, es necesario un widgetOptions objeto para filtrar qué widgets devolver. El widgetOptions objeto contiene las siguientes propiedades, todas las cuales son opcionales:

  • installable: un valor booleano que indica si los widgets devueltos deben ser instalables.
  • installed: un valor booleano que indica si los widgets devueltos están instalados en el host del widget.
  • tag: cadena que se usa para filtrar los widgets devueltos por etiqueta.
  • instanceId: cadena que se usa para filtrar los widgets devueltos por identificador de instancia.
  • hostId: cadena que se usa para filtrar los widgets devueltos por el identificador de host del widget.

widgetPayload (objeto)

Al crear o actualizar una instancia de widget, el trabajador del servicio debe enviar la plantilla y los datos necesarios para rellenar el widget. La plantilla y los datos se denominan carga útil. El widgetPayload objeto contiene las siguientes propiedades:

  • template: la plantilla, como una cadena, que se va a usar para representar el widget. Este será el JSON con cadena de una plantilla de tarjeta adaptable.
  • data: los datos, como una cadena, que se van a usar con la plantilla de widget. Estos datos pueden ser datos JSON con cadena.

widgetInstance (objeto)

Este objeto representa una instancia determinada de un widget en un host de widget y contiene las siguientes propiedades:

  • id: cadena GUID interna que se usa para hacer referencia a la instancia.
  • host: puntero interno al host de widget que ha instalado esta instancia.
  • updatedDate: objeto que representa la última vez que se enviaron datos a la instancia.
  • payload: un objeto widgetPayload que representa la última carga que se envió a esta instancia.

widgetDefinition (objeto)

Este objeto representa la definición original del widget, que se encuentra en el archivo de manifiesto PWA. Las propiedades de este objeto coinciden con las propiedades que aparecen en Definir widgets, anteriormente.

widgetEvent (objeto)

Este objeto se pasa como argumento a los agentes de escucha de eventos de widget de trabajo de servicio de tipo widgetinstall, widgetuninstally widgetresume.

Para los widgetinstalltipos de eventos , widgetuninstally widgetresume , el widgetEvent objeto tiene las siguientes propiedades:

Propiedad Descripción Tipo
widget Instancia del widget que desencadenó el evento. Widget
instanceId Identificador de instancia del widget. String
hostId Identificador de host del widget. String

widgetClickEvent (objeto)

Este objeto se pasa como argumento a los agentes de escucha de eventos de widget de trabajo de servicio de tipo widgetclick. Puede abrir la ventana de la aplicación en respuesta al widgetclick evento mediante clients.openWindow().

El widgetClickEvent objeto tiene las siguientes propiedades:

Propiedad Descripción Tipo
action Acción que desencadenó el evento, tal como se define en los actions.verb campos de la plantilla de widget. Consulte Definir acciones de widget. String
widget Instancia del widget que desencadenó el evento. widgetInstance
hostId Identificador de host del widget. String
instanceId Identificador de instancia del widget. String