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

288

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

  • 0
    Вы уверены, что это не сервер или клиент, выполняющий кеширование? (уже несколько месяцев использовал требуемый js и ничего подобного не заметил) IE обнаружил, что кэширует результаты действий MVC, chrome кэширует наши html-шаблоны, но все файлы js, похоже, обновляются после сброса кэша браузера. Я полагаю, если вы пытались использовать кэширование, но вы не можете сделать это как обычно, потому что запросы от требуемого js удаляли строку запроса, которая могла вызвать проблему?
  • 0
    Я не уверен, что RequireJS удаляет добавленные номера версий, как это. Возможно, это был мой сервер. Интересно, что в RequireJS есть настройка кеша, так что вы можете быть правы, удалив номера добавленной версии необходимых файлов.
Показать ещё 3 комментария
Теги:
requirejs

13 ответов

460
Лучший ответ

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()
});
  • 46
    Очень полезно, спасибо. Я использую urlArgs: "bust=" + (new Date()).getTime() для автоматической urlArgs: "bust=" + (new Date()).getTime() кэша во время разработки и urlArgs: "bust=v2" для производства, где я увеличиваю жестко закодированную версию num после развертывания обновлен необходимый скрипт.
  • 9
    ... также в качестве оптимизатора производительности вы можете использовать Math.random () вместо (new Date ()). getTime (). Это больше красоты, а не создать объект и немного быстрее jsperf.com/speedcomparison .
Показать ещё 14 комментариев
52

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

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

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

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

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

  • 0
    Хотите прокомментировать, почему использование urlArgs затрудняет отладку?
  • 8
    @ JamesP.Wright, потому что (по крайней мере, в Chrome), когда вы устанавливаете точку останова для чего-то, что происходит при загрузке страницы, затем нажимаете кнопку «Обновить», точка останова не срабатывает, поскольку URL-адрес изменился, и Chrome отбросил точку останова. Я хотел бы знать обходной путь только для клиента к этому.
Показать ещё 9 комментариев
21

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

  • 1
    Мне бы очень хотелось, чтобы ваше обновление появилось в официальной сборке requirejs. Основной автор requirejs тоже может быть заинтересован (см. Ответ @ Louis выше).
  • 0
    @ BumbleB2na - не стесняйтесь комментировать PullRequest ( github.com/jrburke/requirejs/pull/1017 ), jrburke не заинтересовался. Он предлагает решение с использованием префикса имени файла, я обновлю свой ответ, чтобы включить его.
Показать ещё 4 комментария
19

Вдохновленный Истекает кеш на 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@',
    ...
});
11

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

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

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

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

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

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

В разработке

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

  • 0
    Хорошие моменты, но ваш ответ зависит от вашей серверной среды. Возможно, хорошей альтернативой для всех, кто наткнулся на это, является недавняя рекомендация по добавлению номера версии в имя файла вместо параметра querystring. Вот больше информации на эту тему: stevesouders.com/blog/2008/08/23/…
  • 0
    Мы сталкиваемся с этой конкретной проблемой в производственной системе. Я рекомендую сохранять ваши имена файлов, а не использовать параметр.
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.

  • 1
    Работает конечно в файле .htaccess тоже.
5

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

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

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

2

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

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

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

  • 0
    Я использую комбинацию grunt-filerev и grunt-cache-buster для перезаписи файлов Javascript.
1

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

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

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

0

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

Изображение 2279

Шаблон T4:

Изображение 2280

Сгенерированный файл: Изображение 2281

Хранить в переменной до загрузки require.config.js: Изображение 2282

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

Изображение 2283

  • 2
    Вероятно, потому что ваше решение проблемы JavaScript - это куча кода на C #. Это также куча дополнительной работы и кода для выполнения чего-то, что в конечном итоге делается точно так же, как принятый ответ.
  • 0
    @mAAdhaTTah Вы можете сделать это с любым языком на стороне сервера ... не обязательно должен быть c #. Кроме того, это автоматизирует процесс, обновляя кэш-память при создании новой версии проекта, гарантируя, что клиент всегда будет кэшировать последнюю версию сценариев. Я не думаю, что это заслуживает негативной уценки. Он просто расширяет ответ, предлагая автоматизированный подход к решению.
Показать ещё 2 комментария
0

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

Джеймс

-1

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню