RequireJS, похоже, делает что-то внутренне, чтобы кэшировать файлы javascript. Если я вношу изменения в один из необходимых файлов, я должен переименовать файл, чтобы изменения были применены.
Общий трюк добавления номера версии в качестве параметра querystring в конец имени файла не работает с requirejs <script src="jsfile.js?v2"></script>
Я ищу способ предотвратить это внутреннее кэширование требуемых скриптов RequireJS без необходимости переименовывать мои файлы script каждый раз, когда они обновляются.
Кросс-платформенное решение:
Теперь я использую urlArgs: "bust=" + (new Date()).getTime()
для автоматического обхода кеша во время разработки и urlArgs: "bust=v2"
для производства, где я увеличиваю число жестко закодированных версий после развертывания обновленного требуемого script.
Примечание:
@Dustin Getz упомянул в недавнем ответе, что инструменты для разработчиков Chrome будут отбрасывать точки останова во время отладки, когда файлы Javascript будут постоянно обновляться. Один из способов - написать debugger;
в коде, чтобы вызвать точку останова в большинстве отладчиков Javascript.
Решения для сервера:
Для конкретных решений, которые могут работать лучше для вашей серверной среды, таких как Node или Apache, см. некоторые ответы ниже.
RequireJS может быть настроен для добавления значения к каждому из URL-адресов script для перебора кеша.
Из документации RequireJS (http://requirejs.org/docs/api.html#config):
urlArgs: дополнительные аргументы строки запроса, добавленные к URL-адресам, которые RequireJS использует для извлечения ресурсов. Наиболее полезно кэшировать бюст, когда браузер или сервер настроен неправильно.
Пример, добавив "v2" ко всем скриптам:
require.config({
urlArgs: "bust=v2"
});
В целях развития вы можете заставить RequireJS обходить кеш, добавив временную метку:
require.config({
urlArgs: "bust=" + (new Date()).getTime()
});
urlArgs: "bust=" + (new Date()).getTime()
для автоматической urlArgs: "bust=" + (new Date()).getTime()
кэша во время разработки и urlArgs: "bust=v2"
для производства, где я увеличиваю жестко закодированную версию num после развертывания обновлен необходимый скрипт.
Требовать script загружает почтовые заголовки http-кеширования. (Сценарии загружаются динамически вставленным <script>
, что означает, что запрос выглядит так же, как и любой старый объект, загружаемый.)
Предоставляйте свои javascript-ресурсы надлежащим HTTP-заголовкам, чтобы отключить кеширование во время разработки.
Использование require urlArgs означает, что любые точки останова, которые вы установили, не будут сохранены при обновлении; вам нужно вставлять операторы debugger
везде в свой код. Плохо. Я использую urlArgs
для ресурсов, связанных с кэшем, во время модернизации продукта с помощью git sha; то я могу установить, что мои активы будут кэшироваться навсегда и гарантированно никогда не иметь устаревших активов.
В разработке я макет всех аякс-запросов с сложной конфигурацией mockjax, затем я могу обслуживать свое приложение только в режиме javascript с помощью 10-страничный HTTP-сервер python с отключенным кэшированием. Это расширилось для меня до довольно большого приложения "enterpriseisey" с сотнями прекрасных конечных точек webservice. У нас даже есть контрактный дизайнер, который может работать с нашей реальной производственной кодовой базой, не предоставляя ему доступ к нашему внутреннему коду.
Решение urlArgs имеет проблемы. К сожалению, вы не можете контролировать все прокси-серверы, которые могут находиться между вами и вашим веб-браузером. К сожалению, некоторые из этих прокси-серверов, к сожалению, настроены на игнорирование параметров URL при кешировании файлов. Если это произойдет, неправильная версия вашего JS файла будет доставлена вашему пользователю.
Я, наконец, сдался и внедрил собственное исправление непосредственно в require.js. Если вы хотите изменить свою версию библиотеки requirejs, это решение может сработать для вас.
Здесь вы можете увидеть патч:
https://github.com/jbcpollak/requirejs/commit/589ee0cdfe6f719cd761eee631ce68eee09a5a67
После добавления вы можете сделать что-то подобное в своей конфигурации:
var require = {
baseUrl: "/scripts/",
cacheSuffix: ".buildNumber"
}
Используйте свою систему сборки или серверную среду для замены buildNumber
версией id/версией программного обеспечения/любимым цветом.
Используйте так:
require(["myModule"], function() {
// no-op;
});
Потребуется запросить этот файл:
http://yourserver.com/scripts/myModule.buildNumber.js
В нашей серверной среде мы используем правила перезаписи URL, чтобы вырезать номер сборки и обслуживать правильный JS файл. Таким образом, нам не нужно беспокоиться о переименовании всех наших JS файлов.
Патч будет игнорировать любой script, который указывает протокол, и не будет влиять на файлы, не относящиеся к JS.
Это хорошо работает для моей среды, но я понимаю, что некоторые пользователи предпочтут префикс, а не суффикс, поэтому его легко скопировать в соответствии с вашими потребностями.
Update:
В обсуждении запроса на растяжение автор requirejs предполагает, что это может работать как решение для префикса номера версии:
var require = {
baseUrl: "/scripts/buildNumber."
};
Я не пробовал это, но подразумевается, что это запросит следующий URL-адрес:
http://yourserver.com/scripts/buildNumber.myModule.js
Что может работать очень хорошо для многих людей, которые могут использовать префикс.
Вот несколько возможных повторяющихся вопросов:
Требование и кэширование прокси-серверов
require.js - Как установить версию на требуемые модули как часть URL?
Вдохновленный Истекает кеш на main.Is data, мы обновили наше развертывание script со следующей задачей 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>
Где начинается main.js:
require.config({
baseUrl: '/js',
urlArgs: 'bust=@Revision@',
...
});
urlArgs
может вызвать проблемы!
Главный автор requirejs предпочитает не использовать urlArgs
:
Для развернутых активов я предпочитаю ставить версию или хеш для всего построить как каталог сборки, а затем просто изменить конфигурацию
baseUrl
для проекта использовать этот каталог версий какbaseUrl
. затем никакие другие файлы не изменяются, и это помогает избежать некоторых проблем с прокси-серверами, где они может не кэшировать URL с строкой запроса на нем.
[Укладка шахты.]
Я следую этому совету.
Я предпочитаю использовать сервер, который интеллектуально кэширует файлы, которые могут часто меняться: сервер, который испускает Last-Modified
и отвечает на If-Modified-Since
при необходимости 304. Даже сервер на основе Node express, установленный для обслуживания статических файлов, делает это прямо из коробки. Он не требует ничего делать в моем браузере и не испортит контрольные точки.
Я взял этот фрагмент из AskApache и поместил его в отдельный .conf файл моего локального веб-сервера Apache (в моем случае /etc/apache 2/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>
Для разработки это отлично работает, без необходимости менять код. Что касается производства, я мог бы использовать подход @dvtoever.
Быстрое исправление для разработки
Для разработки вы можете просто отключить кеш в Chrome Dev Tools (Отключить кеш Chrome для разработки веб-сайта). Отключение кеша происходит только в том случае, если диалог Dev Tools открыт, поэтому вам не нужно беспокоиться о переключении этой опции каждый раз, когда вы регулярно просматриваете.
Примечание. Использование urlArgs - это правильное решение для производства, чтобы пользователи получили последний код. Но это затрудняет отладку, поскольку хром делает недействительными точки останова при каждом обновлении (потому что каждый раз он "новый" файл).
Я не рекомендую использовать urlArgs для разрыва кэша с RequireJS. Поскольку это не решает проблему полностью. Обновление версии no приведет к загрузке всех ресурсов, даже если вы только что изменили один ресурс.
Чтобы справиться с этой проблемой, я рекомендую использовать модули Grunt, такие как "filerev" для создания ревизии no. Кроме того, я написал пользовательскую задачу в файле Gruntfile, чтобы обновить версию там, где это было необходимо.
При необходимости я могу поделиться фрагментом кода для этой задачи.
Вот как я это делаю в Django/Flask (можно легко адаптировать к другим языкам/системам VCS):
В вашем config.py
(я использую это в python3, поэтому вам может понадобиться настроить кодировку в python2)
import subprocess
GIT_HASH = subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip().decode('utf-8')
Затем в вашем шаблоне:
{% if config.DEBUG %}
require.config({urlArgs: "bust=" + (new Date().getTime())});
{% else %}
require.config({urlArgs: "bust=" + {{ config.GIT_HASH|tojson }}});
{% endif %}
git rev-parse HEAD
один раз при запуске приложения и сохраняет его в объекте config
Существует простое решение этой проблемы, так что вы можете загрузить уникальный номер версии для каждого модуля.
Вы можете сохранить исходную функцию requirejs.load, перезаписать ее с помощью своей собственной функции и снова проанализировать свой модифицированный URL-адрес в файле requirejs.load:
var load = requirejs.load;
requirejs.load = function (context, moduleId, url) {
url += "?v=" + oRevision[moduleId];
load(context, moduleId, url);
};
В нашем процессе строительства я использовал "gulp -rev" для создания файла манифеста со всей ревизией всех используемых модулей. Упрощенная версия моей задачи gulp:
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('./'));
});
это приведет к созданию модуля AMD с номерами версий для имен модулей, который включается как "oRevision" в main.js, где вы перезаписываете функцию requirejs.load, как показано ранее.
Это дополнение к принятому ответу @phil mccull.
Я использую его метод, но я также автоматизирую процесс, создав шаблон T4 для предварительной сборки.
Команды предварительной сборки:
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"
Шаблон T4:
Сгенерированный файл:
Хранить в переменной до загрузки require.config.js:
Ссылка в require.config.js:
Ummm, а что насчет requirejs.undef
?
https://groups.google.com/forum/#!topic/requirejs/gq4bX8u6lPU
от самого Джеймса Берка, автора RequireJS:
Обязательно используйте requirejs 2.0, затем используйте requirejs.undef() функция для определения неопределенности mod2 перед ее переопределением:
http://requirejs.org/docs/api.html#undef
Джеймс
В моем случае я хотел загружать одну и ту же форму каждый раз, когда я нажимаю, я не хотел, чтобы изменения, которые я сделал в файле, остались. Он может не иметь отношения к этому сообщению точно, но это может быть потенциальным решением на стороне клиента без необходимости настройки config для require. Вместо того, чтобы напрямую отправлять содержимое, вы можете сделать копию требуемого файла и сохранить фактический файл в целости.
LoadFile(filePath){
const file = require(filePath);
const result = angular.copy(file);
return result;
}