Manejo de la memoria caché del navegador en aplicaciones de una sola página

27

Estoy tratando de descubrir cómo manejar correctamente el caché del navegador web para aplicaciones de una sola página.

Tengo un diseño bastante típico: varios archivos HTML, JS y CSS que implementan el SPA, y un montón de datos JSON que consume el SPA. Los problemas surgen cuando quiero enviar una actualización: actualizo la parte estática del sitio y el código que genera el JSON al mismo tiempo, pero los navegadores de los clientes a menudo tienen la parte estática en caché, por lo que el código antiguo intenta procesar los nuevos datos y puede (dependiendo de los cambios realizados) tener problemas. (En particular, IE parece más agresivo que Chrome o Firefox sobre el uso de JS en caché sin revalidar).

¿Cuál es la mejor manera de manejar esto?

  1. Asegúrese de que mis cambios de JSON sean compatibles con versiones anteriores y suponga que las memorias caché del navegador caducarán en un plazo razonable.
  2. Incruste algún tipo de número de versión tanto en el JS estático como en el JSON, luego ejecútelo window.location.reload(true);si no coinciden.
  3. Calcule la combinación apropiada de encabezados ( must-revalidateo no-cachelo que sea; las fuentes varían en cómo hacer esto) para garantizar que los navegadores siempre revaliden todos los recursos en cada carga, incluso si esto implica algunos viajes de ida y vuelta adicionales para cargar el sitio.
  4. Microgestione mi control de caché y caduca los encabezados para que el contenido estático caduque cuando quiera enviar una actualización.
  5. ¿Algo más?
Josh Kelley
fuente
1
He escuchado que los n. ° 3 y n. ° 4 fallan inesperadamente en los controles integrados del navegador web si su entorno es malo ( tos iOS) de los compañeros de trabajo. # 1 y # 2 pueden ser una opción de nivel de aplicación, pero aún pueden (?) Causar problemas de caché para otros recursos o para cargas parciales de recursos. Lo único que he visto funcionar de manera confiable en el código listo para producción es buscar yoururl.html? <SomeTimeStamp> ya que esto falsificará la mayoría de los mecanismos de almacenamiento en caché. Bonificación: enrolle toda su aplicación web en un archivo para que las cargas tengan éxito atómico o fallen. Desventaja: funciona mejor en un enlace local. Su experiencia puede ser diferente. ¡Buena suerte!
J Trana
2
+1 a usar un número de versión o una marca de tiempo como parámetro de URL para los recursos.
9000

Respuestas:

14

Necesita una solución de eliminación de caché . La función del busting de caché es:

  1. Cambie el nombre de los recursos a un nombre único según su contenido.
  2. Actualice todas las referencias a esos recursos.

En un proyecto basado en Grunt es común usar grunt-rev para garantizar que todos los archivos que necesitan actualizarse reciban nombres únicos, en función de su contenido.

Si se asegura de que sus archivos JSON obtengan nombres de archivos de almacenamiento en caché junto con las referencias a ellos en su Javascript, los clientes siempre cargarán los archivos JSON que el Javascript espera.

La ventaja de los nombres de archivos basados ​​en hash es que los archivos que no han cambiado obtendrán los mismos nombres de archivo después de la eliminación de la memoria caché, por lo que los navegadores pueden continuar usando el contenido en caché de forma segura cuando no haya cambiado.

Obviamente, este es el tipo de cosas que desea automatizar como parte de la compilación de producción de su proyecto para que no tenga que realizar un seguimiento de los cambios de nombres y referencias de archivos manualmente.

Ted Percival
fuente
2
+1 para el bit en "cursiva de caché" en cursiva, que abre la puerta para realmente googlear estas cosas productivamente.
Zak Kus
@Ted Percival: Yeoman Framework hace esto, lo cual yo uso, pero veo un problema. cuando lanzo una nueva compilación, el navegador puede tener el index.html almacenado en caché con referencias a los archivos antiguos ... y el navegador recibe un error. ¿Cómo debo solucionar esto? (A.) enlace simbólico todos los nombres de archivos antiguos a los nuevos (esto funciona) (B.) agregue el encabezado sin caché a index.html (pero siempre se respeta) (C.) agregue .htaccess para reconocer un archivo acelerado y busque la base (es decir, 12345.main.js -> main.js)
timh
5

Puede usar if-modified-since + last-modifiedo if-none-match + etagencabezados junto con el cache-controlencabezado adecuado . (Puede haber errores del navegador , pero nada que no pueda administrar en los navegadores recientes).

Si los archivos son estáticos, le sugiero que los use if-modified-since, ya que se puede hacer automáticamente con un servidor HTTP bien configurado. Debería devolver 304 si el archivo no se modifica desde la última descarga.

No creo que tu # 1 y # 2 funcionen a largo plazo. El # 3 o # 4 puede funcionar. El # 3 es más simple, pero debe aprender a lidiar con este problema solo una vez. Así que probaría el # 4 si fuera usted, pero la solución podría depender de qué navegadores usen sus clientes ... Por ejemplo, IE8 tiene problemas al actualizar el caché ajax, etc.

inf3rno
fuente
2

Si puede incluir Java Servlet Filter en su SPA, aquí hay una solución de trabajo: CorrectBrowserCacheHandlerFilter.java

Básicamente, cuando su navegador solicita los archivos estáticos, el servidor redirigirá todas las solicitudes a la misma pero con un parámetro de consulta hash ( ?v=azErTpor ejemplo) que depende del contenido del archivo estático de destino.

Al hacer esto, el navegador nunca almacenará en caché los archivos estáticos declarados en su index.htmlpor ejemplo (porque siempre recibió un 302 Moved Temporarily), pero solo almacenará en caché los que tengan la versión hash (el servidor responderá 200por ellos). Por lo tanto, el caché del navegador se usará de manera eficiente para aquellos archivos estáticos con versión hash.

Descargo de responsabilidad: soy el autor de CorrectBrowserCacheHandlerFilter.java.

Anthony O.
fuente