Este archivo configura las respuestas del servidor..
Lo utilizan principalmente servidores como Apache o Nginx.
Configura el mime type para el archivo de manifiesto y sedhabilita el uso de la cache general de http..
| 1 | AddType application/manifest+json .webmanifest |
| 2 | |
| 3 | ExpiresActive On |
| 4 | |
| 5 | Header set Cache-Control "max-age=1, must-revalidate" |
| 6 |
| 1 | <!DOCTYPE html> |
| 2 | <html> |
| 3 | <head> |
| 4 | <meta charset="utf-8"> |
| 5 | <meta name="viewport" content="width=device-width, initial-scale=1"> |
| 6 | <title>Page Not Found</title> |
| 7 | |
| 8 | <style media="screen"> |
| 9 | body { background: #ECEFF1; color: rgba(0,0,0,0.87); font-family: Roboto, Helvetica, Arial, sans-serif; margin: 0; padding: 0; } |
| 10 | #message { background: white; max-width: 360px; margin: 100px auto 16px; padding: 32px 24px 16px; border-radius: 3px; } |
| 11 | #message h3 { color: #888; font-weight: normal; font-size: 16px; margin: 16px 0 12px; } |
| 12 | #message h2 { color: #ffa100; font-weight: bold; font-size: 16px; margin: 0 0 8px; } |
| 13 | #message h1 { font-size: 22px; font-weight: 300; color: rgba(0,0,0,0.6); margin: 0 0 16px;} |
| 14 | #message p { line-height: 140%; margin: 16px 0 24px; font-size: 14px; } |
| 15 | #message a { display: block; text-align: center; background: #039be5; text-transform: uppercase; text-decoration: none; color: white; padding: 16px; border-radius: 4px; } |
| 16 | #message, #message a { box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); } |
| 17 | #load { color: rgba(0,0,0,0.4); text-align: center; font-size: 13px; } |
| 18 | @media (max-width: 600px) { |
| 19 | body, #message { margin-top: 0; background: white; box-shadow: none; } |
| 20 | body { border-top: 16px solid #ffa100; } |
| 21 | } |
| 22 | </style> |
| 23 | </head> |
| 24 | <body> |
| 25 | <div id="message"> |
| 26 | <h2>404</h2> |
| 27 | <h1>Page Not Found</h1> |
| 28 | <p>The specified file was not found on this website. Please check the URL for mistakes and try again.</p> |
| 29 | <h3>Why am I seeing this?</h3> |
| 30 | <p>This page was generated by the Firebase Command-Line Interface. To modify it, edit the <code>404.html</code> file in your project's configured <code>public</code> directory.</p> |
| 31 | </div> |
| 32 | </body> |
| 33 | </html> |
| 1 | <!DOCTYPE html> |
| 2 | <html lang="es"> |
| 3 | |
| 4 | <head> |
| 5 | |
| 6 | <meta charset="UTF-8"> |
| 7 | |
| 8 | <title>PWA Básica</title> |
| 9 | |
| 10 | <!-- Resumen para los motores de búsqueda. --> |
| 11 | <meta name="description" content="Ejemplo de PWA"> |
| 12 | |
| 13 | <script type="module" src="js/registraServiceWorker.js"></script> |
| 14 | |
| 15 | <meta name="viewport" content="width=device-width"> |
| 16 | |
| 17 | <!-- Color de la barra de navegación de Chrome en dispositivos móviles. --> |
| 18 | <meta name="theme-color" content="#cbc693"> |
| 19 | |
| 20 | <!-- Ícono para la página web, que normalmente se pone en la raíz del sitio. |
| 21 | Puede ser diferente para cada página. --> |
| 22 | <link rel="icon" sizes="32x32" href="favicon.ico"> |
| 23 | |
| 24 | <!-- Configuración de la PWA para Chrome, Edge y Safari. |
| 25 | Debe ponerse en todas las páginas. --> |
| 26 | <link rel="manifest" href="site.webmanifest"> |
| 27 | |
| 28 | <link rel="stylesheet" href="css/estilos.css"> |
| 29 | |
| 30 | </head> |
| 31 | |
| 32 | <body> |
| 33 | |
| 34 | <h1>PWA Básica</h1> |
| 35 | |
| 36 | <p>Hola mundo.</p> |
| 37 | |
| 38 | </body> |
| 39 | |
| 40 | </html> |
Este archivo sirve para configurar los instaladores de la aplicación.
short_nameNombre corto. Normalmente se despliega en dispositivos móviles. Máximo 20 caracteres.
nameNombre largo. Normalmente se despliega en computadoras de escritorio. Máximo 30 caracteres.
idIdentificador del archivo de instalación. Normalmente es la ruta del archivo inicial de la app.
start_urlRuta del archivo inicial de la app.
display
Forma de mostrar la app. El término standalone significa que
no
se muestra la barra de navegación del navegador web.
theme_colorColor de la barra de estado (en dispositivos móviles) o de título (en computadoras de escritorio) de la app.
background_colorColor de fondo de la pantalla desplah en dispositivos móviles.
descriptionDescribe el propósito de la aplicación. Aparece en el cuadro de diálogo que muestra el navegador al instalar la app.
screenshots
Listado de máximo 8 capturas de pantalla. Aparecen en el cuadro de diálogo
que muestra el navegador al instalar la app. Debes incluir al menos una con
"form_factor": "wide"
y otra con
"form_factor": "narrow".
iconsListado de íconos en distintas resoluciones para los instaladores de la app. Se selecciona el que se vea mejor según las característocas del dispositivo.
srcUrl de la imagen dentro de la app.
sizesDimensiones en pixeles de la imagen, anchoxalto.
typeTipo mime de la imagen.
purposeForma en que se usa la imagen.
maskableLa imagen puede recortarse de forma segura para tomar distintas formas, como círculos, gotas, cuadrados con esquinas redondeadas, etc. Normalmente se usa para dispositivos móviles.
anyNo se puede asegurar nada sobre la imagen. Normalmente se usa para dispositivos de escritorio.
Normalmente debe proporcionarse un juego de íconos con purpose
any y otro juego de íconos con purpose
maskable.
form_factorOrientación de una screenshot.
wideLa screenshot tiene una orientación horizontal. Normalmente la creenshot se usa para dispositivos de escritorio.
narrowLa screenshot tiene una orientación vertical. Normalmente la creenshot se usa para dispositivos móviles.
Debes incluir al menos una screenshot con
"form_factor": "wide"
y otra con
"form_factor": "narrow".
labelDescripción de una screenshot. Aparece en el cuadro de diálogo que muestra el navegador al instalar la app.
| 1 | { |
| 2 | "short_name": "PWA", |
| 3 | "name": "Ejemplo de PWA", |
| 4 | "id": "/index.html", |
| 5 | "start_url": "/index.html", |
| 6 | "display": "standalone", |
| 7 | "theme_color": "#cbc693", |
| 8 | "background_color": "#ffffff", |
| 9 | "description": "Ejemplos básico de PWA.", |
| 10 | "screenshots": [ |
| 11 | { |
| 12 | "src": "/img/screenshot_horizontal.png", |
| 13 | "sizes": "1507x777", |
| 14 | "type": "image/png", |
| 15 | "form_factor": "wide", |
| 16 | "label": "PWA Básica" |
| 17 | }, |
| 18 | { |
| 19 | "src": "/img/screenshot_vertical.png", |
| 20 | "sizes": "591x980", |
| 21 | "type": "image/png", |
| 22 | "form_factor": "narrow", |
| 23 | "label": "PWA Básica (2)" |
| 24 | } |
| 25 | ], |
| 26 | "icons": [ |
| 27 | { |
| 28 | "src": "/img/maskable_icon_x48.png", |
| 29 | "sizes": "48x48", |
| 30 | "type": "image/png", |
| 31 | "purpose": "any" |
| 32 | }, |
| 33 | { |
| 34 | "src": "/img/maskable_icon_x72.png", |
| 35 | "sizes": "72x72", |
| 36 | "type": "image/png", |
| 37 | "purpose": "any" |
| 38 | }, |
| 39 | { |
| 40 | "src": "/img/maskable_icon_x96.png", |
| 41 | "sizes": "96x96", |
| 42 | "type": "image/png", |
| 43 | "purpose": "any" |
| 44 | }, |
| 45 | { |
| 46 | "src": "/img/maskable_icon_x128.png", |
| 47 | "sizes": "128x128", |
| 48 | "type": "image/png", |
| 49 | "purpose": "any" |
| 50 | }, |
| 51 | { |
| 52 | "src": "/img/maskable_icon_x192.png", |
| 53 | "sizes": "192x192", |
| 54 | "type": "image/png", |
| 55 | "purpose": "any" |
| 56 | }, |
| 57 | { |
| 58 | "src": "/img/maskable_icon_x384.png", |
| 59 | "sizes": "384x384", |
| 60 | "type": "image/png", |
| 61 | "purpose": "any" |
| 62 | }, |
| 63 | { |
| 64 | "src": "/img/maskable_icon_x512.png", |
| 65 | "sizes": "512x512", |
| 66 | "type": "image/png", |
| 67 | "purpose": "any" |
| 68 | }, |
| 69 | { |
| 70 | "src": "/img/maskable_icon.png", |
| 71 | "sizes": "2730x2730", |
| 72 | "type": "image/png", |
| 73 | "purpose": "any" |
| 74 | }, |
| 75 | { |
| 76 | "src": "/img/icono2048.png", |
| 77 | "sizes": "2048x2048", |
| 78 | "type": "image/png", |
| 79 | "purpose": "any" |
| 80 | }, |
| 81 | { |
| 82 | "src": "/img/maskable_icon_x48.png", |
| 83 | "sizes": "48x48", |
| 84 | "type": "image/png", |
| 85 | "purpose": "maskable" |
| 86 | }, |
| 87 | { |
| 88 | "src": "/img/maskable_icon_x72.png", |
| 89 | "sizes": "72x72", |
| 90 | "type": "image/png", |
| 91 | "purpose": "maskable" |
| 92 | }, |
| 93 | { |
| 94 | "src": "/img/maskable_icon_x96.png", |
| 95 | "sizes": "96x96", |
| 96 | "type": "image/png", |
| 97 | "purpose": "maskable" |
| 98 | }, |
| 99 | { |
| 100 | "src": "/img/maskable_icon_x128.png", |
| 101 | "sizes": "128x128", |
| 102 | "type": "image/png", |
| 103 | "purpose": "maskable" |
| 104 | }, |
| 105 | { |
| 106 | "src": "/img/maskable_icon_x192.png", |
| 107 | "sizes": "192x192", |
| 108 | "type": "image/png", |
| 109 | "purpose": "maskable" |
| 110 | }, |
| 111 | { |
| 112 | "src": "/img/maskable_icon_x384.png", |
| 113 | "sizes": "384x384", |
| 114 | "type": "image/png", |
| 115 | "purpose": "maskable" |
| 116 | }, |
| 117 | { |
| 118 | "src": "/img/maskable_icon_x512.png", |
| 119 | "sizes": "512x512", |
| 120 | "type": "image/png", |
| 121 | "purpose": "maskable" |
| 122 | }, |
| 123 | { |
| 124 | "src": "/img/maskable_icon.png", |
| 125 | "sizes": "2730x2730", |
| 126 | "type": "image/png", |
| 127 | "purpose": "maskable" |
| 128 | } |
| 129 | ] |
| 130 | } |
| 1 | /* Este archivo debe estar colocado en la carpeta raíz del sitio. |
| 2 | * |
| 3 | * Cualquier cambio en el contenido de este archivo hace que el service |
| 4 | * worker se reinstale. */ |
| 5 | |
| 6 | /** |
| 7 | * Cambia el número de la versión cuando cambia el contenido de los |
| 8 | * archivos. |
| 9 | * |
| 10 | * El número a la izquierda del punto (.), en este caso <q>1</q>, se |
| 11 | * conoce como número mayor y se cambia cuando se realizan |
| 12 | * modificaciones grandes o importantes. |
| 13 | * |
| 14 | * El número a la derecha del punto (.), en este caso <q>00</q>, se |
| 15 | * conoce como número menor y se cambia cuando se realizan |
| 16 | * modificaciones menores. |
| 17 | */ |
| 18 | const VERSION = "1.00" |
| 19 | |
| 20 | /** Nombre del archivo de cache. */ |
| 21 | const CACHE = "ejemploPWA" |
| 22 | |
| 23 | /** |
| 24 | * Archivos requeridos para que la aplicación funcione fuera de |
| 25 | * línea. |
| 26 | */ |
| 27 | const ARCHIVOS = [ |
| 28 | "favicon.ico", |
| 29 | "index.html", |
| 30 | "site.webmanifest", |
| 31 | "css/estilos.css", |
| 32 | "img/icono2048.png", |
| 33 | "img/maskable_icon.png", |
| 34 | "img/maskable_icon_x128.png", |
| 35 | "img/maskable_icon_x192.png", |
| 36 | "img/maskable_icon_x384.png", |
| 37 | "img/maskable_icon_x48.png", |
| 38 | "img/maskable_icon_x512.png", |
| 39 | "img/maskable_icon_x72.png", |
| 40 | "img/maskable_icon_x96.png", |
| 41 | "img/screenshot_horizontal.png", |
| 42 | "img/screenshot_vertical.png", |
| 43 | "js/registraServiceWorker.js", |
| 44 | "/" |
| 45 | ] |
| 46 | |
| 47 | // Verifica si el código corre dentro de un service worker. |
| 48 | if (self instanceof ServiceWorkerGlobalScope) { |
| 49 | // Evento al empezar a instalar el servide worker, |
| 50 | self.addEventListener("install", |
| 51 | (/** @type {ExtendableEvent} */ evt) => { |
| 52 | console.log("El service worker se está instalando.") |
| 53 | evt.waitUntil(llenaElCache()) |
| 54 | }) |
| 55 | |
| 56 | // Evento al solicitar información a la red. |
| 57 | self.addEventListener("fetch", (/** @type {FetchEvent} */ evt) => { |
| 58 | if (evt.request.method === "GET") { |
| 59 | evt.respondWith(buscaLaRespuestaEnElCache(evt)) |
| 60 | } |
| 61 | }) |
| 62 | |
| 63 | // Evento cuando el service worker se vuelve activo. |
| 64 | self.addEventListener("activate", |
| 65 | () => console.log("El service worker está activo.")) |
| 66 | } |
| 67 | |
| 68 | async function llenaElCache() { |
| 69 | console.log("Intentando cargar caché:", CACHE) |
| 70 | // Borra todos los cachés. |
| 71 | const keys = await caches.keys() |
| 72 | for (const key of keys) { |
| 73 | await caches.delete(key) |
| 74 | } |
| 75 | // Abre el caché de este service worker. |
| 76 | const cache = await caches.open(CACHE) |
| 77 | // Carga el listado de ARCHIVOS. |
| 78 | await cache.addAll(ARCHIVOS) |
| 79 | console.log("Cache cargado:", CACHE) |
| 80 | console.log("Versión:", VERSION) |
| 81 | } |
| 82 | |
| 83 | /** @param {FetchEvent} evt */ |
| 84 | async function buscaLaRespuestaEnElCache(evt) { |
| 85 | // Abre el caché. |
| 86 | const cache = await caches.open(CACHE) |
| 87 | const request = evt.request |
| 88 | /* Busca la respuesta a la solicitud en el contenido del caché, sin |
| 89 | * tomar en cuenta la parte después del símbolo "?" en la URL. */ |
| 90 | const response = await cache.match(request, { ignoreSearch: true }) |
| 91 | if (response === undefined) { |
| 92 | /* Si no la encuentra, empieza a descargar de la red y devuelve |
| 93 | * la promesa. */ |
| 94 | return fetch(request) |
| 95 | } else { |
| 96 | // Si la encuentra, devuelve la respuesta encontrada en el caché. |
| 97 | return response |
| 98 | } |
| 99 | } |
| 1 | html { |
| 2 | color-scheme: light dark; |
| 3 | font-family: sans-serif; |
| 4 | } |
| 1 | const nombreDeServiceWorker = "sw.js" |
| 2 | |
| 3 | try { |
| 4 | navigator.serviceWorker.register(nombreDeServiceWorker) |
| 5 | .then(registro => { |
| 6 | console.log(nombreDeServiceWorker, "registrado.") |
| 7 | console.log(registro) |
| 8 | }) |
| 9 | .catch(error => console.log(error)) |
| 10 | } catch (error) { |
| 11 | console.log(error) |
| 12 | } |