Запретить RequireJS кэшировать необходимые сценарии

StackOverflow https://stackoverflow.com/questions/8315088

  •  25-10-2019
  •  | 
  •  

Вопрос

RequireJS, кажется, делает что-то внутри, что кэширует необходимые файлы javascript.Если я вношу изменения в один из необходимых файлов, мне придется переименовать файл, чтобы изменения были применены.

Обычный прием добавления номера версии в качестве параметра строки запроса в конец имени файла не работает с requirejs. <script src="jsfile.js?v2"></script>

Я ищу способ предотвратить внутреннее кэширование требуемых сценариев RequireJS без необходимости переименовывать файлы сценариев каждый раз, когда они обновляются.

Кроссплатформенное решение:

сейчас я использую urlArgs: "bust=" + (new Date()).getTime() для автоматической очистки кеша во время разработки и urlArgs: "bust=v2" для производства, где я увеличиваю жестко запрограммированный номер версии после развертывания обновленного обязательного сценария.

Примечание:

@Dustin Getz упомянул в недавнем ответе, что инструменты разработчика Chrome будут сбрасывать точки останова во время отладки, когда файлы Javascript постоянно обновляются таким образом.Один из обходных путей — написать debugger; в коде для запуска точки останова в большинстве отладчиков Javascript.

Серверные решения:

Конкретные решения, которые могут лучше работать для вашей серверной среды, например Node или Apache, см. в некоторых ответах ниже.

Это было полезно?

Решение

Требование может быть настроено для добавления значения к каждому из URL -адресов сценария для разрыва кэша.

Из документации «Восстания» (http://requirejs.org/docs/api.html#config):

Урларгс: Дополнительные аргументы строки запроса, добавленные к URL -адресам, которые требуются для получения ресурсов. Наиболее полезно для кэширования бюста, когда браузер или сервер не настроены правильно.

Пример, добавление «v2» ко всем сценариям:

require.config({
    urlArgs: "bust=v2"
});

Для целей разработки вы можете заставить требования обходить кэш, добавив временную метку:

require.config({
    urlArgs: "bust=" + (new Date()).getTime()
});

Другие советы

Не используйте Urlargs для этого!

Требовать нагрузки сценариев уважать заголовки кэширования HTTP. (Сценарии загружены динамически вставленным <script>, что означает, что запрос выглядит так же, как и любой старый активы, загружаемый.)

Подавайте свои активы JavaScript с соответствующими заголовками HTTP, чтобы отключить кэширование во время разработки.

Использование Urlargs recement означает, что любые заданные вами точки остановки не будут сохранены по обновлениям; В конечном итоге вам нужно положить debugger Заявления повсюду в вашем коде. Плохо. я использую urlArgs для активах с кешем во время модернизации производства с Git SHA; Тогда я могу установить свои активы на вечность и гарантированно, что никогда не будет устаревших активов.

В разработке я издевается над всеми запросами AJAX со сложным Mockjax конфигурация, тогда я могу обслуживать свое приложение в режиме только JavaScript с 10 линий Python Http Server со всеми выключенными кэшированием. Анкет Это увеличилось для меня до довольно большого «предприятия» с сотнями конечных точек с спокойным веб -сервисом. У нас даже есть контрактный дизайнер, который может работать с нашей реальной производственной кодовой базой, не предоставляя ему доступ к нашему бэкэнд -коду.

Решение urlArgs имеет проблемы.К сожалению, вы не можете контролировать все прокси-серверы, которые могут находиться между вами и веб-браузером вашего пользователя.К сожалению, некоторые из этих прокси-серверов могут быть настроены так, чтобы игнорировать параметры URL-адреса при кэшировании файлов.Если это произойдет, вашему пользователю будет доставлена ​​неправильная версия вашего JS-файла.

Я наконец сдался и внедрил свое собственное исправление непосредственно в require.js.Если вы хотите изменить свою версию библиотеки requirejs, это решение может вам подойти.

Посмотреть патч можно здесь:

https://github.com/jbcpollak/requirejs/commit/589ee0cdfe6f719cd761eee631ce68eee09a5a67

После добавления вы можете сделать что-то вроде этого в вашей конфигурации require:

var require = {
    baseUrl: "/scripts/",
    cacheSuffix: ".buildNumber"
}

Используйте свою систему сборки или серверную среду для замены buildNumber с идентификатором ревизии/версией программного обеспечения/любимым цветом.

Использование require следующим образом:

require(["myModule"], function() {
    // no-op;
});

Приведет к требованию запроса этого файла:

http://yourserver.com/scripts/myModule.buildNumber.js

В нашей серверной среде мы используем правила перезаписи URL-адресов, чтобы удалить buildNumber и предоставить правильный файл JS.Таким образом, нам фактически не придется беспокоиться о переименовании всех наших JS-файлов.

Патч будет игнорировать любой сценарий, определяющий протокол, и не повлияет на файлы, отличные от JS.

Это хорошо работает для моей среды, но я понимаю, что некоторые пользователи предпочитают префикс, а не суффикс, поэтому мой коммит легко изменить в соответствии с вашими потребностями.

Обновлять:

В обсуждении запроса на включение автор requirejs предполагает, что это может сработать в качестве решения для префикса номера версии:

var require = {
    baseUrl: "/scripts/buildNumber."
};

Я не пробовал этого, но предполагается, что при этом будет запрошен следующий URL-адрес:

http://yourserver.com/scripts/buildNumber.myModule.js

Это может сработать очень хорошо для многих людей, которые могут использовать префикс.

Вот несколько возможных дублирующих вопросов:

RequireJS и прокси-кеширование

require.js – Как я могу установить версию необходимых модулей как часть URL-адреса?

Вдохновлен Срок действия кэша на redate.js-main Мы обновили наш сценарий развертывания с помощью следующей задачи 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 может вызвать проблемы!

Главный автор книги «Восстания предпочитает не использовать urlArgs:

Для развернутых активов я предпочитаю разместить версию или хэш для всей сборки в качестве каталога сборки, а затем просто изменить baseUrl конфигурация, используемая для проекта для использования этого каталога версии в качестве baseUrl. Анкет Тогда никакие другие файлы не меняются, и это помогает избежать Некоторые прокси -проблемы, где они не могут кэшировать URL -адрес с помощью строки запроса.

Styling Mine.

Я следую за этот совет.

В развитие

Я предпочитаю использовать сервер, который разумно кэширует файлы, которые могут часто меняться: сервер, который издает Last-Modified и отвечает на If-Modified-Since с 304, когда это уместно. Даже сервер на основе узла выражать Установка для обслуживания статических файлов делает это прямо в коробке. Это не требует ничего делать с моим браузером и не испортит точки останова.

Я взял этот фрагмент из Askapache и поместите его в отдельный файл .conf моего локального Apache Webserver (в моем случае /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>

Для разработки это работает нормально, не нужно изменять код. Что касается производства, я мог бы использовать подход @dvtoever.

Быстрое исправление для разработки

Для развития вы могли бы просто Отключить кэш в инструментах Chrome Dev (Отключение хромированного кеша для разработки веб -сайтов) Отключение кэша происходит только в том случае, если диалоговое окно Dev Tools открыт, поэтому вам не нужно беспокоиться о том, чтобы переключать эту опцию каждый раз, когда вы регулярно просмотрите.

Примечание: используя 'Урларгс«Это правильное решение в производстве, чтобы пользователи получали последний код. Но это затрудняет отладку, потому что Chrome недействительный останова с каждым обновлением (потому что это «новый» файл, который обслуживается каждый раз).

Я не рекомендую использовать 'Урларгс«Для кеша разрывается с требованиями. Поскольку это не решает проблему полностью. Обновление версии NO приведет к загрузке всех ресурсов, даже если у вас просто изменится один ресурс.

Чтобы решить эту проблему, я рекомендую использовать модули Grunt, такие как «filerev» для создания ревизии №. Кроме того, я записал пользовательскую задачу в 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 объект

Динамическое решение (без Urlargs)

Для этой проблемы есть простое решение, чтобы вы могли загрузить уникальный номер ревизии для каждого модуля.

Вы можете сохранить исходную функцию 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"

enter image description here

Шаблон T4:

enter image description here

Сгенерированный файл:enter image description here

Хранить в переменной перед требованием.config.js загружен:enter image description here

Ссылка на require.config.js:

enter image description here

В моем случае я хотел загружать одну и ту же форму каждый раз, когда я нажимаю, я не хотел, чтобы изменения, внесенные в файл, оставались. Это может не относиться к этому сообщению, но это может быть потенциальным решением на стороне клиента без настройки конфигурации для требований. Вместо того, чтобы отправлять содержимое напрямую, вы можете сделать копию необходимого файла и сохранить фактический файл нетронутым.

LoadFile(filePath){
    const file = require(filePath);
    const result = angular.copy(file);
    return result;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top