Предотвратить RequireJS от кэширования необходимых сценариев - PullRequest
293 голосов
/ 29 ноября 2011

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

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

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

Межплатформенное решение:

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

Примечание:

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

Решения для конкретного сервера:

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

Ответы [ 12 ]

448 голосов
/ 12 декабря 2011

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

Из документации 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()
});
54 голосов
/ 26 февраля 2013

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

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

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

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

В процессе разработки я выполняю макет всех ajax-запросов со сложной mockjax конфигурацией, а затем могу обслуживать свое приложение в режиме только для javascript с 10-строчным Python http-сервером с отключенным кэшированием . Для меня это масштабировалось до довольно крупного «корпоративного» приложения с сотнями спокойных конечных точек веб-сервиса. У нас даже есть контрактный дизайнер, который может работать с нашей реальной рабочей базой кода, не предоставляя ему доступ к нашему внутреннему коду.

22 голосов
/ 07 февраля 2014

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

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

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

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

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

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

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

Использование требуют, как это:

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

Вызовет необходимость запросить этот файл:

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

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

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

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

Обновление:

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

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

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

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

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

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

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

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

19 голосов
/ 12 сентября 2012

Вдохновленный Срок действия кэша на require.js data-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@',
    ...
});
11 голосов
/ 01 ноября 2013

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

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

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

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

[Styling mine.]

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

В разработке

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

7 голосов
/ 10 декабря 2013

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

5 голосов
/ 29 августа 2013

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

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

3 голосов
/ 03 марта 2015

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

Для решения этой проблемы я рекомендую использовать модули Grunt, такие как 'filerev', для создания ревизии №. Кроме того, я написал пользовательское задание в Gruntfile, чтобы обновить ревизию, где бы она ни требовалась.

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

2 голосов
/ 26 ноября 2014

Вот как я это делаю в 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 голосов
/ 21 марта 2017

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

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