En esta lección se presenta una PWA básica.
Puedes probar el ejemplo en https://pwabasw.web.app/.
Prueba e instala, de preferencia con Chrome, el sitio https://pwabasw.web.app/.
Descarga el archivo /src/pwab.zip y descompáctalo.
Crea una cuenta de email de Google por ejemplo, juanito@google.com
En https://console.firebase.google.com/ selecciona la cuenta de email que acabas de crear, por ejemplo, juanito@google.com
Crea un proyecto de Firebase nuevo. Por ejemplo, pwabase. No uses IA ni Amalytics.
Crea una cuenta de GitHub usando el email anterior y selecciona el nombre de usuario unsando la parte inicial del correo electrónico, por ejemplo juanito.
Crea un repositorio nuevo. En el nombre del repositorio, pon el nombre del proyecto de firebase, por ejemplo pwabase
En Visual Studio Code selecciona el menú Terminal > New Terminal y al abrirse esta, teclea cmd y luego ENTER
En la consola de firebase, en el costado izquierdo, en la opción Hosting y sin servidores selecciona Hosting. Añade hosting a ti sitio (Sin usar IA ni Amalytics) y sigue las instrucciones que se indican para para la consola, que básicamente son
Edita los archivos que desees.
El archivo sw.js
tiene una lista de los archivos que se instalan.
El archivo instruccionesListadoSw.txt
te indica como generarla usando
Visual Studio Code.
Crea los íconos enmascarables con https://maskable.app/ a partir del archivo «icono2048.png».
Coloca el archivo favicon.ico
en la carpeta public
del
proyecto.
Coloca los otros íconos en la carpeta public/img
y asegúrate de que
estén declarados en el archivo site.webmanifest
.
El archivo public/sw.js
tiene una lista de los archivos que se
instalan. El archivo instruccionesListadoSw.txt
te indica como
generarla usando Visual Studio Code.
Para depurar localmente tu aplicación, teclea en la terminal
firebase emulators:start
Lo puedes ver en la dirección http://localhost:5000. Para detener el
servidor local, en la terminal debes presionar Ctrl+C
En el siguiente enlace se muestra como probar la aplicación. La forma de
levantar el servidor es diferente, pero la forma de usar el navegador es la
misma.
Prueba tu PWA.
Cada vez que vayas a publicar cambios, debes modificar el valor
de VERSION en el archivo sw.js
para poder ver los cambios
en el navegador.
Cuando desarrolles, es incómodo modificar la versión cada que realizas cambios; en vez de ello desinstala la app:
Abre las herramientas de depuración haciendo clic derecho en la página y selecciona Inspeccionar (o Inspect si aparece en inglés).
En la Pestaña Aplicación (o Application en inglés) selecciona Almacenamoento (o Storage en inglés). Cliquea Borrar datos del sitio.
Recarga la app, de preferencia haciendo clic derecho en el ícono de
volver a cargar la página
y
seleccionando vaciar caché y volver a cargar de manera forzada (o
algo parecido). Si no aparece un menú emergente, simplemente cliquea
volver a cargar la página
.
Revisa que no aparezca ningún error ni en la pestañas Consola, ni
en Red.
Tanbién puedes usar la combinación de teclas Ctrl+Mayúsculas+r para forzar que se actualice temporalmente el navegador en caso de que no se vean los cambios.
En la Pestaña Aplicación (o Application en inglés) selecciona Archivo de manifiesto (o Manifest file en inglés). Esta herramienta analiza la estructura del archivo de manifiesto y te indica si hay un error.
En la Pestaña Aplicación (o Application en inglés) selecciona Almacenamiento en caché (o Cache storage en inglés). Aquí puedes revisar si el caché de la aplicación se llenó correctamente. En caso de que esté vacío, es que hubo algún error durante la carga y la app se ejecuta más lenta.
El archivo
.htaccess
Solo se utiliza en servidores compatibles con apache, como Ngnx o infinity
free. Sirve para quitar el cache de http y declarar el mime type de los
archivos .manifest
Para desplegar tu aplicación, teclea en la terminal
firebase deploy
Te va a indicar la dirección para ver tu sitio en internet.
Instala y usa tu PWA en Windows. Aunque en este video se recomienda usar Edge, al momento de actualizar el contenido, la opción más recomendada es Chrome para que te muestre las descripciones y las capturas de pantalla.
Instala y usa tu PWA en Android. Al momento de actualizar las notas, tal vez no te aparezca el botón para instalar y tengas que seleccionar la acción de agregar a la pantalla principal que aparece en el menú de extensión de Chrome.
Haz clic en los triángulos para expandir las carpetas
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 | } |
| 1 | { |
| 2 | "projects": { |
| 3 | "default": "pwabasw" |
| 4 | } |
| 5 | } |
| 6 |
| 1 | { |
| 2 | "hosting": { |
| 3 | "public": "public", |
| 4 | "headers": [ |
| 5 | { |
| 6 | "source": "**", |
| 7 | "headers": [ |
| 8 | { |
| 9 | "key": "Cache-Control", |
| 10 | "value": "no-cache, no-store, must-revalidate" |
| 11 | }, |
| 12 | { |
| 13 | "key": "Pragma", |
| 14 | "value": "no-cache" |
| 15 | }, |
| 16 | { |
| 17 | "key": "Expires", |
| 18 | "value": "0" |
| 19 | } |
| 20 | ] |
| 21 | } |
| 22 | ], |
| 23 | "ignore": [ |
| 24 | "firebase.json", |
| 25 | "**/.*", |
| 26 | "**/node_modules/**" |
| 27 | ] |
| 28 | }, |
| 29 | "emulators": { |
| 30 | "hosting": { |
| 31 | "port": 5000 |
| 32 | }, |
| 33 | "ui": { |
| 34 | "enabled": true |
| 35 | }, |
| 36 | "singleProjectMode": true |
| 37 | } |
| 38 | } |
| 39 |
| 1 | Generar el listado de archivos del sw.js desde Visual Studio Code. |
| 2 | 1. Abrir una terminal desde el menú |
| 3 | Terminal > New Terminal |
| 4 | |
| 5 | 2. Desde la terminal introducir la orden: |
| 6 | Get-ChildItem -path public -Recurse | Select Directory,Name | Out-File archivos.txt |
| 7 | |
| 8 | 3. Abrir el archivo generado, que se llama |
| 9 | archivos.txt |
| 10 | y sobre este, realizar los pasos que siguen: |
| 11 | |
| 12 | 4. Quita del archivo archivos.txt: |
| 13 | * el encabezado, |
| 14 | * todas las carpetas, |
| 15 | * todos los archivos dentro de .vscode como: |
| 16 | * el archivo .vscode/settings.json, |
| 17 | * el archivo .vscode/launch.json, |
| 18 | * el archivo .htaccess, |
| 19 | * el archivo archivos.txt, |
| 20 | * este archivo (instruccionesListadoSw.txt), |
| 21 | * el archivo jsconfig.json, |
| 22 | * el archivo sw.js, |
| 23 | * el archivo de la base de datos, que termina en ".db" y |
| 24 | está en la carpeta php, |
| 25 | * todos los archivos de php y |
| 26 | * las líneas en blanco del final |
| 27 | |
| 28 | 5. Cambia los \ por / desde Visual Studio Code con las siguientes |
| 29 | combinaciones de teclas: |
| 30 | |
| 31 | Ctrl+H En el diálogo que aparece introduce lo siguiente: |
| 32 | Find:\ |
| 33 | Replace:/ |
| 34 | |
| 35 | Clic en el icono Reemplaza todo o Replace All y luego teclea ESC |
| 36 | |
| 37 | 6. Coloca las comillas y coma del final de cada línea desde Visual |
| 38 | Studio Code con las siguientes combinaciones de teclas: |
| 39 | |
| 40 | Ctrl+H En el diálogo que aparece, selecciona el botón |
| 41 | ".*" |
| 42 | e introduce lo siguiente: |
| 43 | Find:\s*$ |
| 44 | Replace:", |
| 45 | |
| 46 | Clic en el icono Reemplaza todo o Replace All y luego teclea ESC |
| 47 | |
| 48 | 7. Marca la carpeta inicial, presiona la combinación de teclas: |
| 49 | |
| 50 | Shift+Ctrl+L |
| 51 | |
| 52 | borra la selección, teclea " y luego ESC |
| 53 | |
| 54 | 8. Cambia las secuencias de espacios por / con las siguientes |
| 55 | combinaciones de teclas: |
| 56 | |
| 57 | Ctrl+H En el diálogo que aparece, selecciona el botón |
| 58 | ".*" |
| 59 | e introduce lo siguiente: |
| 60 | Find:\s+ |
| 61 | Replace:/ |
| 62 | |
| 63 | Clic en el icono Reemplaza todo o Replace All y luego teclea ESC |
| 64 | |
| 65 | 9. Cambia las "/ por " con las siguientes combinaciones de teclas: |
| 66 | |
| 67 | Ctrl+H En el diálogo que aparece, quita la selección del botón |
| 68 | ".*" |
| 69 | e introduce lo siguiente: |
| 70 | Find:"/ |
| 71 | Replace:" |
| 72 | |
| 73 | Clic en el icono Reemplaza todo o Replace All y luego teclea ESC |
| 74 | |
| 75 | 10. Copia el texto al archivo |
| 76 | public/sw.js |
| 77 | en el contenido del arreglo llamado ARCHIVOS, pero recuerda |
| 78 | mantener el último elemento, que dice: |
| 79 | "/" |
| 1 | "favicon.ico", |
| 2 | "index.html", |
| 3 | "site.webmanifest", |
| 4 | "css/estilos.css", |
| 5 | "img/icono2048.png", |
| 6 | "img/maskable_icon.png", |
| 7 | "img/maskable_icon_x128.png", |
| 8 | "img/maskable_icon_x192.png", |
| 9 | "img/maskable_icon_x384.png", |
| 10 | "img/maskable_icon_x48.png", |
| 11 | "img/maskable_icon_x512.png", |
| 12 | "img/maskable_icon_x72.png", |
| 13 | "img/maskable_icon_x96.png", |
| 14 | "img/screenshot_horizontal.png", |
| 15 | "img/screenshot_vertical.png", |
| 16 | "js/registraServiceWorker.js", |
| 1 | # Logs |
| 2 | logs |
| 3 | *.log |
| 4 | npm-debug.log* |
| 5 | yarn-debug.log* |
| 6 | yarn-error.log* |
| 7 | firebase-debug.log* |
| 8 | firebase-debug.*.log* |
| 9 | |
| 10 | # Firebase cache |
| 11 | .firebase/ |
| 12 | |
| 13 | # Firebase config |
| 14 | |
| 15 | # Uncomment this if you'd like others to create their own Firebase project. |
| 16 | # For a team working on the same Firebase project(s), it is recommended to leave |
| 17 | # it commented so all members can deploy to the same project(s) in .firebaserc. |
| 18 | # .firebaserc |
| 19 | |
| 20 | # Runtime data |
| 21 | pids |
| 22 | *.pid |
| 23 | *.seed |
| 24 | *.pid.lock |
| 25 | |
| 26 | # Directory for instrumented libs generated by jscoverage/JSCover |
| 27 | lib-cov |
| 28 | |
| 29 | # Coverage directory used by tools like istanbul |
| 30 | coverage |
| 31 | |
| 32 | # nyc test coverage |
| 33 | .nyc_output |
| 34 | |
| 35 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) |
| 36 | .grunt |
| 37 | |
| 38 | # Bower dependency directory (https://bower.io/) |
| 39 | bower_components |
| 40 | |
| 41 | # node-waf configuration |
| 42 | .lock-wscript |
| 43 | |
| 44 | # Compiled binary addons (http://nodejs.org/api/addons.html) |
| 45 | build/Release |
| 46 | |
| 47 | # Dependency directories |
| 48 | node_modules/ |
| 49 | |
| 50 | # Optional npm cache directory |
| 51 | .npm |
| 52 | |
| 53 | # Optional eslint cache |
| 54 | .eslintcache |
| 55 | |
| 56 | # Optional REPL history |
| 57 | .node_repl_history |
| 58 | |
| 59 | # Output of 'npm pack' |
| 60 | *.tgz |
| 61 | |
| 62 | # Yarn Integrity file |
| 63 | .yarn-integrity |
| 64 | |
| 65 | # dotenv environment variables file |
| 66 | .env |
| 67 | |
| 68 | # dataconnect generated files |
| 69 | .dataconnect |
| 70 |
Este archivo ayuda a detectar errores en los archivos del proyecto.
Lo utiliza principalmente Visual Studio Code.
No se explica aquí su estructura, pero puede encontrarse la explicación de todo en la documentación del sitio de Visual Studio Code.
| 1 |
{
|
| 2 |
"compilerOptions": {
|
| 3 |
"checkJs": true, |
| 4 |
"strictNullChecks": true, |
| 5 |
"target": "ES6", |
| 6 |
"module": "Node16", |
| 7 |
"moduleResolution": "Node16", |
| 8 |
"lib": [
|
| 9 |
"ES2017",
|
| 10 |
"WebWorker",
|
| 11 |
"DOM"
|
| 12 |
] |
| 18 |
} |
| 1 | Creative Commons Legal Code |
| 2 | |
| 3 | CC0 1.0 Universal |
| 4 | |
| 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE |
| 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN |
| 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS |
| 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES |
| 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS |
| 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM |
| 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED |
| 12 | HEREUNDER. |
| 13 | |
| 14 | Statement of Purpose |
| 15 | |
| 16 | The laws of most jurisdictions throughout the world automatically confer |
| 17 | exclusive Copyright and Related Rights (defined below) upon the creator |
| 18 | and subsequent owner(s) (each and all, an "owner") of an original work of |
| 19 | authorship and/or a database (each, a "Work"). |
| 20 | |
| 21 | Certain owners wish to permanently relinquish those rights to a Work for |
| 22 | the purpose of contributing to a commons of creative, cultural and |
| 23 | scientific works ("Commons") that the public can reliably and without fear |
| 24 | of later claims of infringement build upon, modify, incorporate in other |
| 25 | works, reuse and redistribute as freely as possible in any form whatsoever |
| 26 | and for any purposes, including without limitation commercial purposes. |
| 27 | These owners may contribute to the Commons to promote the ideal of a free |
| 28 | culture and the further production of creative, cultural and scientific |
| 29 | works, or to gain reputation or greater distribution for their Work in |
| 30 | part through the use and efforts of others. |
| 31 | |
| 32 | For these and/or other purposes and motivations, and without any |
| 33 | expectation of additional consideration or compensation, the person |
| 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she |
| 35 | is an owner of Copyright and Related Rights in the Work, voluntarily |
| 36 | elects to apply CC0 to the Work and publicly distribute the Work under its |
| 37 | terms, with knowledge of his or her Copyright and Related Rights in the |
| 38 | Work and the meaning and intended legal effect of CC0 on those rights. |
| 39 | |
| 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be |
| 41 | protected by copyright and related or neighboring rights ("Copyright and |
| 42 | Related Rights"). Copyright and Related Rights include, but are not |
| 43 | limited to, the following: |
| 44 | |
| 45 | i. the right to reproduce, adapt, distribute, perform, display, |
| 46 | communicate, and translate a Work; |
| 47 | ii. moral rights retained by the original author(s) and/or performer(s); |
| 48 | iii. publicity and privacy rights pertaining to a person's image or |
| 49 | likeness depicted in a Work; |
| 50 | iv. rights protecting against unfair competition in regards to a Work, |
| 51 | subject to the limitations in paragraph 4(a), below; |
| 52 | v. rights protecting the extraction, dissemination, use and reuse of data |
| 53 | in a Work; |
| 54 | vi. database rights (such as those arising under Directive 96/9/EC of the |
| 55 | European Parliament and of the Council of 11 March 1996 on the legal |
| 56 | protection of databases, and under any national implementation |
| 57 | thereof, including any amended or successor version of such |
| 58 | directive); and |
| 59 | vii. other similar, equivalent or corresponding rights throughout the |
| 60 | world based on applicable law or treaty, and any national |
| 61 | implementations thereof. |
| 62 | |
| 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention |
| 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, |
| 65 | irrevocably and unconditionally waives, abandons, and surrenders all of |
| 66 | Affirmer's Copyright and Related Rights and associated claims and causes |
| 67 | of action, whether now known or unknown (including existing as well as |
| 68 | future claims and causes of action), in the Work (i) in all territories |
| 69 | worldwide, (ii) for the maximum duration provided by applicable law or |
| 70 | treaty (including future time extensions), (iii) in any current or future |
| 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, |
| 72 | including without limitation commercial, advertising or promotional |
| 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each |
| 74 | member of the public at large and to the detriment of Affirmer's heirs and |
| 75 | successors, fully intending that such Waiver shall not be subject to |
| 76 | revocation, rescission, cancellation, termination, or any other legal or |
| 77 | equitable action to disrupt the quiet enjoyment of the Work by the public |
| 78 | as contemplated by Affirmer's express Statement of Purpose. |
| 79 | |
| 80 | 3. Public License Fallback. Should any part of the Waiver for any reason |
| 81 | be judged legally invalid or ineffective under applicable law, then the |
| 82 | Waiver shall be preserved to the maximum extent permitted taking into |
| 83 | account Affirmer's express Statement of Purpose. In addition, to the |
| 84 | extent the Waiver is so judged Affirmer hereby grants to each affected |
| 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, |
| 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and |
| 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the |
| 88 | maximum duration provided by applicable law or treaty (including future |
| 89 | time extensions), (iii) in any current or future medium and for any number |
| 90 | of copies, and (iv) for any purpose whatsoever, including without |
| 91 | limitation commercial, advertising or promotional purposes (the |
| 92 | "License"). The License shall be deemed effective as of the date CC0 was |
| 93 | applied by Affirmer to the Work. Should any part of the License for any |
| 94 | reason be judged legally invalid or ineffective under applicable law, such |
| 95 | partial invalidity or ineffectiveness shall not invalidate the remainder |
| 96 | of the License, and in such case Affirmer hereby affirms that he or she |
| 97 | will not (i) exercise any of his or her remaining Copyright and Related |
| 98 | Rights in the Work or (ii) assert any associated claims and causes of |
| 99 | action with respect to the Work, in either case contrary to Affirmer's |
| 100 | express Statement of Purpose. |
| 101 | |
| 102 | 4. Limitations and Disclaimers. |
| 103 | |
| 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, |
| 105 | surrendered, licensed or otherwise affected by this document. |
| 106 | b. Affirmer offers the Work as-is and makes no representations or |
| 107 | warranties of any kind concerning the Work, express, implied, |
| 108 | statutory or otherwise, including without limitation warranties of |
| 109 | title, merchantability, fitness for a particular purpose, non |
| 110 | infringement, or the absence of latent or other defects, accuracy, or |
| 111 | the present or absence of errors, whether or not discoverable, all to |
| 112 | the greatest extent permissible under applicable law. |
| 113 | c. Affirmer disclaims responsibility for clearing rights of other persons |
| 114 | that may apply to the Work or any use thereof, including without |
| 115 | limitation any person's Copyright and Related Rights in the Work. |
| 116 | Further, Affirmer disclaims responsibility for obtaining any necessary |
| 117 | consents, permissions or other rights required for any use of the |
| 118 | Work. |
| 119 | d. Affirmer understands and acknowledges that Creative Commons is not a |
| 120 | party to this document and has no duty or obligation with respect to |
| 121 | this CC0 or use of the Work. |
| 122 |
| 1 | # pwabasw |
| 2 | Ejwmplo de pwa |
| 3 |
En esta lección se presentó la estructura básica de una PWA.