Предотвращение необходимости использования обязательных скриптов RequireJS

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, см. некоторые ответы ниже.

Ответ 1

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()
});

Ответ 2

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

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

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

Использование require urlArgs означает, что любые точки останова, которые вы установили, не будут сохранены при обновлении; вам нужно вставлять операторы debugger везде в свой код. Плохо. Я использую urlArgs для ресурсов, связанных с кэшем, во время модернизации продукта с помощью git sha; то я могу установить, что мои активы будут кэшироваться навсегда и гарантированно никогда не иметь устаревших активов.

В разработке я макет всех аякс-запросов с сложной конфигурацией mockjax, затем я могу обслуживать свое приложение только в режиме javascript с помощью 10-страничный HTTP-сервер python с отключенным кэшированием. Это расширилось для меня до довольно большого приложения "enterpriseisey" с сотнями прекрасных конечных точек webservice. У нас даже есть контрактный дизайнер, который может работать с нашей реальной производственной кодовой базой, не предоставляя ему доступ к нашему внутреннему коду.

Ответ 3

Решение 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?

Ответ 4

Вдохновленный Истекает кеш на 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="@[email protected]" value="${buildNumber}" />
</target>

Где начинается main.js:

require.config({
    baseUrl: '/js',
    urlArgs: '[email protected]@',
    ...
});

Ответ 5

В производстве

urlArgs может вызвать проблемы!

Главный автор requirejs предпочитает не использовать urlArgs:

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

[Укладка шахты.]

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

В разработке

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

Ответ 6

Я взял этот фрагмент из 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.

Ответ 7

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

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

Примечание. Использование urlArgs - это правильное решение для производства, чтобы пользователи получили последний код. Но это затрудняет отладку, поскольку хром делает недействительными точки останова при каждом обновлении (потому что каждый раз он "новый" файл).

Ответ 8

Я не рекомендую использовать urlArgs для разрыва кэша с RequireJS. Поскольку это не решает проблему полностью. Обновление версии no приведет к загрузке всех ресурсов, даже если вы только что изменили один ресурс.

Чтобы справиться с этой проблемой, я рекомендую использовать модули Grunt, такие как "filerev" для создания ревизии no. Кроме того, я написал пользовательскую задачу в файле Gruntfile, чтобы обновить версию там, где это было необходимо.

При необходимости я могу поделиться фрагментом кода для этой задачи.

Ответ 9

Вот как я это делаю в 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

Ответ 10

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

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

Ответ 12

Это дополнение к принятому ответу @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:

введите описание изображения здесь

Ответ 13

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

LoadFile(filePath){
    const file = require(filePath);
    const result = angular.copy(file);
    return result;
}