javascript - navegadores - requirejs config
Prevenir RequireJS de almacenar en caché los scripts requeridos (12)
RequireJS parece hacer algo internamente que almacena en caché los archivos javascript necesarios. Si realizo un cambio en uno de los archivos requeridos, tengo que cambiar el nombre del archivo para que se apliquen los cambios.
El truco común de agregar un número de versión como un parámetro de cadena de consulta al final del nombre de archivo no funciona con requirejs <script src="jsfile.js?v2"></script>
Lo que busco es una forma de evitar este almacenamiento en caché interno de los scripts requeridos por RequireJS sin tener que cambiar el nombre de mis archivos de script cada vez que se actualicen.
Solución multiplataforma:
Ahora estoy usando urlArgs: "bust=" + (new Date()).getTime()
para la urlArgs: "bust=v2"
automática de caché durante el desarrollo y urlArgs: "bust=v2"
para la producción donde urlArgs: "bust=v2"
número de versión codificada después del despliegue una secuencia de comandos necesaria actualizada.
Nota:
@Dustin Getz mencionó en una respuesta reciente que Chrome Developer Tools eliminará los puntos de interrupción durante la depuración cuando los archivos de Javascript se actualicen continuamente de esta manera. Una solución es escribir el debugger;
en el código para desencadenar un punto de interrupción en la mayoría de los depuradores de Javascript.
Soluciones específicas del servidor:
Para soluciones específicas que pueden funcionar mejor para el entorno de su servidor, como Node o Apache, consulte algunas de las respuestas a continuación.
No use urlArgs para esto!
Requerir cargas de scripts respeta los encabezados de almacenamiento en caché http (Los scripts se cargan con un <script>
insertado dinámicamente, lo que significa que la solicitud se parece a la carga de cualquier activo anterior).
Sirva sus activos de javascript con los encabezados HTTP adecuados para deshabilitar el almacenamiento en caché durante el desarrollo.
El uso de urlArgs de require significa que los puntos de interrupción que establezca no se conservarán en las actualizaciones; usted termina necesitando poner declaraciones del debugger
en todas partes de su código. Malo. Utilizo urlArgs
para los activos que urlArgs
caché durante las actualizaciones de producción con el git sha; luego puedo configurar mis recursos para que se almacenen en caché para siempre y se me garantiza que nunca tendrán activos obsoletos.
En el desarrollo, mockjax todas las solicitudes de ajax con una compleja configuración de mockjax , luego puedo servir mi aplicación en modo solo de javascript con un servidor http de 10 líneas de python con todo el almacenamiento en caché desactivado . Esto se ha extendido para mí a una aplicación "empresarial" bastante grande con cientos de puntos finales de servicio web tranquilos. Incluso tenemos un diseñador contratado que puede trabajar con nuestro código base de producción real sin darle acceso a nuestro código de fondo.
En producción
urlArgs
puede causar problemas!
El autor principal de requirejs prefiere no usar urlArgs
:
Para los activos implementados, prefiero poner la versión o el hash para toda la compilación como un directorio de compilación, luego simplemente modificar la configuración
baseUrl
utilizada para que el proyecto use ese directorio versionado comobaseUrl
. Entonces, ningún otro archivo cambia, y ayuda a evitar algunos problemas de proxy en los que no pueden almacenar en caché una URL con una cadena de consulta.
[Estilo mío.]
Sigo este consejo.
En desarrollo
Prefiero usar un servidor que almacene de manera inteligente los archivos que pueden cambiar con frecuencia: un servidor que emita Last-Modified
y responda a If-Modified-Since
con 304 cuando sea apropiado. Incluso un servidor basado en el conjunto express de Node para servir archivos estáticos hace esto directamente. No requiere hacer nada en mi navegador y no desordena los puntos de interrupción.
Así es como lo hago en Django / Flask (se puede adaptar fácilmente a otros idiomas / sistemas VCS):
En tu config.py
(uso esto en python3, así que es posible que necesites modificar la codificación en python2)
import subprocess
GIT_HASH = subprocess.check_output([''git'', ''rev-parse'', ''HEAD'']).strip().decode(''utf-8'')
Luego en tu plantilla:
{% if config.DEBUG %}
require.config({urlArgs: "bust=" + (new Date().getTime())});
{% else %}
require.config({urlArgs: "bust=" + {{ config.GIT_HASH|tojson }}});
{% endif %}
- No requiere proceso de construcción manual
- Solo ejecuta
git rev-parse HEAD
una vez cuando se inicia la aplicación, y la almacena en el objeto deconfig
En mi caso, quería cargar el mismo formulario cada vez que hago clic, no quería que los cambios que he realizado en el archivo permanezcan. Puede que no sea relevante para esta publicación exactamente, pero esto podría ser una solución potencial en el lado del cliente sin necesidad de configurar la configuración. En lugar de enviar el contenido directamente, puede hacer una copia del archivo requerido y mantener el archivo real intacto.
LoadFile(filePath){
const file = require(filePath);
const result = angular.copy(file);
return result;
}
Esto es además de la respuesta aceptada de @phil mccull.
Utilizo su método pero también automatizo el proceso creando una plantilla T4 para ejecutarse antes de la compilación.
Comandos precompilados:
set textTemplatingPath="%CommonProgramFiles(x86)%/Microsoft Shared/TextTemplating/$(VisualStudioVersion)/texttransform.exe"
if %textTemplatingPath%=="/Microsoft Shared/TextTemplating/$(VisualStudioVersion)/texttransform.exe" set textTemplatingPath="%CommonProgramFiles%/Microsoft Shared/TextTemplating/$(VisualStudioVersion)/texttransform.exe"
%textTemplatingPath% "$(ProjectDir)CacheBuster.tt"
Plantilla T4:
Almacenar en la variable antes de que se cargue require.config.js:
Referencia en require.config.js:
Inspirados en el cache de Expire en require.js data-main , actualizamos nuestro script de implementación con la siguiente tarea ant:
<target name="deployWebsite">
<untar src="${temp.dir}/website.tar.gz" dest="${website.dir}" compression="gzip" />
<!-- fetch latest buildNumber from build agent -->
<replace file="${website.dir}/js/main.js" token="@Revision@" value="${buildNumber}" />
</target>
Donde el inicio de main.js se ve como:
require.config({
baseUrl: ''/js'',
urlArgs: ''bust=@Revision@'',
...
});
La solución urlArgs tiene problemas. Desafortunadamente, no puede controlar todos los servidores proxy que pueden estar entre usted y el navegador web de su usuario. Lamentablemente, algunos de estos servidores proxy pueden configurarse para ignorar los parámetros de URL al almacenar en caché los archivos. Si esto sucede, la versión incorrecta de su archivo JS será entregada a su usuario.
Finalmente renuncié e implementé mi propia solución directamente en require.js. Si está dispuesto a modificar su versión de la biblioteca requirejs, esta solución podría funcionar para usted.
Puedes ver el parche aquí:
https://github.com/jbcpollak/requirejs/commit/589ee0cdfe6f719cd761eee631ce68eee09a5a67
Una vez agregado, puedes hacer algo como esto en tu configuración requerida:
var require = {
baseUrl: "/scripts/",
cacheSuffix: ".buildNumber"
}
Utilice su sistema de compilación o entorno de servidor para reemplazar buildNumber
con un ID de revisión / versión de software / color favorito.
Usando requiere de esta manera:
require(["myModule"], function() {
// no-op;
});
Será necesario solicitar este archivo:
http://yourserver.com/scripts/myModule.buildNumber.js
En nuestro entorno de servidor, usamos las reglas de reescritura de URL para eliminar el número de compilación y entregar el archivo JS correcto. De esta manera, no debemos preocuparnos por cambiar el nombre de todos nuestros archivos JS.
El parche ignorará cualquier secuencia de comandos que especifique un protocolo, y no afectará a ningún archivo que no sea JS.
Esto funciona bien para mi entorno, pero me doy cuenta de que algunos usuarios preferirían un prefijo en lugar de un sufijo, debería ser fácil modificar mi compromiso para satisfacer sus necesidades.
Actualizar:
En la discusión de solicitud de extracción, el autor de requirejs sugiere que esto podría funcionar como una solución para prefijar el número de revisión:
var require = {
baseUrl: "/scripts/buildNumber."
};
No he intentado esto, pero la implicación es que solicitaría la siguiente URL:
http://yourserver.com/scripts/buildNumber.myModule.js
Lo que podría funcionar muy bien para muchas personas que pueden usar un prefijo.
Aquí hay algunas posibles preguntas duplicadas:
require.js - ¿Cómo puedo configurar una versión en los módulos requeridos como parte de la URL?
No recomiendo usar '' urlArgs '' para el almacenamiento en caché con RequireJS. Como esto no resuelve el problema completamente. La actualización de una versión no dará lugar a la descarga de todos los recursos, aunque acaba de cambiar un solo recurso.
Para manejar este problema, recomiendo usar módulos Grunt como ''filerev'' para crear la revisión no. Además de esto, he escrito una tarea personalizada en Gruntfile para actualizar la revisión donde no sea necesario.
Si es necesario, puedo compartir el fragmento de código para esta tarea.
RequireJS se puede configurar para agregar un valor a cada una de las direcciones URL de script para el almacenamiento en caché.
De la documentación de RequireJS ( http://requirejs.org/docs/api.html#config ):
urlArgs : argumentos de cadena de consulta adicionales anexados a las URL que RequireJS usa para obtener recursos. Más útil para almacenar en la memoria caché cuando el navegador o el servidor no están configurados correctamente.
Ejemplo, agregando "v2" a todos los scripts:
require.config({
urlArgs: "bust=v2"
});
Para fines de desarrollo, puede forzar a RequireJS a omitir el caché agregando una marca de tiempo:
require.config({
urlArgs: "bust=" + (new Date()).getTime()
});
Tomé este fragmento de AskApache y lo puse en un archivo .conf separado de mi servidor web Apache local (en mi caso /etc/apache2/others/preventcaching.conf):
<FilesMatch "/.(html|htm|js|css)$">
FileETag None
<ifModule mod_headers.c>
Header unset ETag
Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires "Wed, 11 Jan 1984 05:00:00 GMT"
</ifModule>
</FilesMatch>
Para el desarrollo, esto funciona bien sin necesidad de cambiar el código. En cuanto a la producción, podría usar el enfoque de @ dvtoever.
Solución dinámica (sin urlArgs)
Hay una solución simple para este problema, de modo que puede cargar un número de revisión único para cada módulo.
Puede guardar la función requirejs.load original, sobrescribirla con su propia función y analizar su url modificado a la requirejs.load original nuevamente:
var load = requirejs.load;
requirejs.load = function (context, moduleId, url) {
url += "?v=" + oRevision[moduleId];
load(context, moduleId, url);
};
En nuestro proceso de construcción usé "gulp-rev" para construir un archivo de manifiesto con todas las revisiones de todos los módulos que se están utilizando. Versión simplificada de mi tarea trilla:
gulp.task(''gulp-revision'', function() {
var sManifestFileName = ''revision.js'';
return gulp.src(aGulpPaths)
.pipe(rev())
.pipe(rev.manifest(sManifestFileName, {
transformer: {
stringify: function(a) {
var oAssetHashes = {};
for(var k in a) {
var key = (k.substr(0, k.length - 3));
var sHash = a[k].substr(a[k].indexOf(".") - 10, 10);
oAssetHashes[key] = sHash;
}
return "define([], function() { return " + JSON.stringify(oAssetHashes) + "; });"
}
}
}))
.pipe(gulp.dest(''./''));
});
esto generará un módulo AMD con números de revisión a nombre de módulo, que se incluye como ''oRevision'' en main.js, donde sobrescribe la función requirejs.load como se muestra anteriormente.
Solución rápida para el desarrollo
Para el desarrollo, simplemente puede deshabilitar el caché en Chrome Dev Tools ( Desactivar el caché de Chrome para el desarrollo de sitios web ). La desactivación de la memoria caché se produce solo si el cuadro de diálogo de las herramientas de desarrollo está abierto, por lo que no debe preocuparse por alternar esta opción cada vez que realice una navegación regular.
Nota: el uso de '' urlArgs '' es la solución adecuada en producción para que los usuarios obtengan el código más reciente. Pero hace que la depuración sea difícil porque Chrome invalida los puntos de interrupción con cada actualización (porque cada vez se sirve un archivo ''nuevo'').