Запобігати вимогамJS від кешування необхідних сценаріїв


302

Схоже, RequireJS робить щось внутрішнє, що кешує необхідні файли javascript. Якщо я вношу зміни до одного з необхідних файлів, я повинен перейменувати файл, щоб зміни були застосовані.

Загальний трюк додавання номера версії як параметр запиту до кінця імені файлу не працює з Requjs <script src="jsfile.js?v2"></script>

Що я шукаю, це спосіб запобігти це внутрішнє кешування необхідних скриптів RequireJS без необхідності перейменувати мої файли сценаріїв щоразу, коли вони оновлюються.

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

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

Примітка:

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

Рішення для сервера:

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


Ви впевнені, що кеш не керує сервером чи клієнтом? (вже декілька місяців використовую необхідні js і не помічаю нічого подібного) IE потрапив у кешування результатів MVC дій, хром кеширував наші HTML-шаблони, але js-файли, здається, оновлюються, коли кеш браузера був скинутий. Я думаю, якщо ви хотіли скористатися кешуванням, але не можете зробити звичайне, оскільки запити від необхідних js видаляли рядок запиту, що може спричинити проблему?
PJUK

Я не впевнений, чи RequireJS видаляє додані номери версій на зразок цього. Можливо, це був мій сервер. Цікаво, як у RequireJS є налаштування кеш-кестера, тож ви можете мати рацію щодо цього, видаляючи мої додані номери версій у потрібних файлах.
BumbleB2na

я оновив свою відповідь потенційним рішенням кешування
Дастін Гец

Тепер я можу додати наступне до літанії, яку я виклав у своєму блозі сьогодні вранці: codrspace.com/dexygen/… І це означає, що я не тільки повинен додавати перебір кеш-пам'яті, але і вимога.js ігнорує жорстке оновлення.
Dexygen

Я розгублений щодо випадку використання для цього .... Це для гарячого перезавантаження модулів AMD на передній план чи що?
Олександр Міллз

Відповіді:


457

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

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

9
... також як оптимізатор продуктивності ви можете використовувати Math.random () замість (new Date ()). getTime (). Це більше краси, а не створення об’єкта і трохи швидше jsperf.com/speedcomparison .
Влад Цепелєв

2
Ви можете отримати його трохи менше:urlArgs: "bust=" + (+new Date)
mrzmyr

11
Я думаю, що щоразу переробляти кеш - це жахлива ідея. На жаль, RequireJS не пропонує іншої альтернативи. Ми використовуємо urlArgs, але не використовуємо для цього випадкову або часову позначку. Замість цього ми використовуємо наш поточний Git SHA, який змінюється лише тоді, коли ми розгортаємо новий код.
Іван Торрес

5
Як цей варіант "v2" може працювати у виробництві, якщо файл, що забезпечує сам рядок "v2", кешований? Якщо я випускаю нову програму у виробництво, додавання "v3" нічого не робитиме, оскільки програма із задоволенням продовжує працювати з кешованими файлами v2, включаючи старий конфігурацію з v2 urlArgs.
Бенні Боттема

54

Не використовуйте для цього urlArgs!

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

Обслуговуйте свої активи JavaScript за допомогою відповідних заголовків HTTP, щоб відключити кешування під час розробки.

Використовуючи urlArgs Require означає, що будь-які встановлені точки перерви не зберігатимуться під час оновлення; вам в кінцевому підсумку потрібно вставити debuggerзаяви у свій код. Поганий. Я використовую urlArgsдля активізації кеш-пам'яті під час модернізації виробництва за допомогою git sha; тоді я можу встановити, що мої активи будуть кешовані назавжди і гарантуються, що ніколи не матимуть застарілих активів.

У процесі розробки я знущаюся над усіма запитами ajax зі складною конфігурацією mockjax , тоді я можу обслуговувати свою програму в режимі javascript, лише з 10-ти рядковим http-сервером python з вимкненим кешуванням . Це призвело до масштабу для мене досить великого «корпоративного» додатка із сотнями спокійних кінцевих точок веб-сервісу. У нас навіть є контрактний дизайнер, який може працювати з нашою реальною кодовою базою даних, не надаючи йому доступу до нашого резервного коду.


8
@ JamesP. Райт, тому що (принаймні в Chrome), коли ви встановлюєте точку перерви для того, що відбувається під час завантаження сторінки, потім натисніть кнопку "Оновити", точка перелому не потрапила, оскільки URL-адреса змінилася, а Chrome впав. Я хотів би знати, що це стосується лише клієнта.
Дрю Ноакс

1
Спасибі, Дастіне. Якщо ви знайдете спосіб цього, будь ласка, опублікуйте. Тим часом ви можете використовувати debugger;у своєму коді, де ви хочете, щоб точка перерви зберігалася.
BumbleB2na

2
Для всіх, хто використовує http-сервер у вузлі (npm install http-server). Ви також можете відключити кешування за допомогою -c-1 (тобто http-сервер -c-1).
Yourpalal

5
Ви можете відключити кешування в Chrome, щоб усунути проблему налагодження під час розробки: stackoverflow.com/questions/5690269/…
Deepak Joy

5
+1 до !!! НЕ ВИКОРИСТОВУЙТЕ urlArgs У ВИРОБНИЦТВІ !!! . Уявіть собі, що на вашому веб-сайті є 1000 файлів JS (так, можливо!), А їх завантаження контролюється необхідним JS. Тепер ви випускаєте v2 або ваш сайт, де змінюється лише кілька файлів JS! але додаючи urlArgs = v2, ви змушуєте перезавантажувати всі 1000 файлів JS! ви заплатите багато трафіку! тільки модифіковані файли повинні бути повторно завантажені, всі інші повинні відповідати статусом 304 (Не змінено).
walv

24

Рішення urlArgs має проблеми. На жаль, ви не можете контролювати всі проксі-сервери, які можуть бути між вами та веб-браузером користувача. Деякі з цих проксі-серверів, на жаль, можуть бути налаштовані на ігнорування параметрів URL-адреси під час кешування файлів. Якщо це трапиться, неправильна версія файлу JS буде доставлена ​​вашому користувачеві.

Нарешті я відмовився і застосував власне виправлення безпосередньо у Requ.js. Якщо ви готові змінити свою версію бібліотеки Requjs, це рішення може працювати для вас.

Ви можете побачити виправлення тут:

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

Після додавання ви можете зробити щось подібне у своїй конфігурації вимагати:

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

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

Використовувати потрібно так:

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

Викличе запит на цей файл:

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

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

Патч буде ігнорувати будь-який скрипт, який визначає протокол, і він не вплине на будь-які не JS-файли.

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

Оновлення:

У обговоренні запиту про те, що вимагає, автор вимагає, щоб це могло працювати як рішення для префіксації ревізійного номера:

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

Я цього не пробував, але мається на увазі, що це вимагатиме наступної URL-адреси:

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

Що може працювати дуже добре для багатьох людей, які можуть використовувати префікс.

Ось кілька можливих повторюваних питань:

Потрібно кешування JS та кешування проксі

Requ.js - Як я можу встановити версію для необхідних модулів як частину URL-адреси?


1
Я дуже хотів би, щоб ваше оновлення пробилося в офіційну збірку Requjs. Головний автор Requjs теж може бути зацікавлений (див. Відповідь Луї вище).
BumbleB2na

@ BumbleB2na - не соромтесь коментувати PullRequest ( github.com/jrburke/requirejs/pull/1017 ), jrburke не здавався зацікавленим. Він пропонує рішення, використовуючи префікс імені файлу, я оновлю свою відповідь, щоб включити це.
JBCP

1
Приємне оновлення. Я думаю , що мені дійсно подобається ця пропозиція автор, але це тільки тому , що я використовую цю угоду про іменах в останній час: /scripts/myLib/v1.1/. Я спробував додати постфікс (або префікс) до моїх імен, можливо, тому, що це робить jquery, але через деякий час я [залянувся і] почав збільшувати номер версії в батьківській папці. Я думаю, що це полегшує технічне обслуговування для мене на великому веб-сайті, але тепер ти переживаєш про переписування кошмарів URL-адрес.
BumbleB2na

1
Сучасні системи на передньому кінці просто переписують файли JS та мають суму MD5 у назві файлу, а потім переписують HTML-файли для використання нових імен файлів під час створення, але це стає складним із застарілими системами, де код переднього кінця обслуговується стороною сервера.
JBCP

це працює, коли мені потрібен якийсь js всередині файлу jspx ?, як це<script data-main="${pageContext.request.contextPath}/resources/scripts/main" src="${pageContext.request.contextPath}/resources/scripts/require.js"> <jsp:text/> </script> <script> require([ 'dev/module' ]); </script>
masT

19

Натхненний кеш-пам’яті Expire у data.js main.js, ми оновили сценарій розгортання із таким завданням:

<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 може спричинити проблеми!

Головний автор Requjs вважає за краще не використовуватиurlArgs :

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

[Стильна шахта.]

Я дотримуюся цієї поради.

У розвитку

Я вважаю за краще використовувати сервер, який інтелектуально кешує файли, які можуть часто змінюватися: сервер, який видає Last-Modifiedта відповідає на If-Modified-Since304, коли це доречно. Навіть сервер, заснований на експрес- наборі Node для обслуговування статичних файлів, робить це прямо з вікна. Він не вимагає нічого робити з моїм браузером і не псує точки прориву.


Хороші моменти, але ваша відповідь специфічна для вашого серверного середовища. Можливо, вдалою альтернативою для всіх, хто натрапляє на це, є нещодавня рекомендація щодо додавання номера версії до імені файлу замість параметра запиту рядка. Ось більше інформації на цю тему: stevesouders.com/blog/2008/08/23/…
BumbleB2na

Ми стикаємося з цією конкретною проблемою у виробничій системі. Я рекомендую відновлювати свої імена файлів, а не використовувати параметр.
JBCP

7

Я взяв цей фрагмент з 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.


6

Швидке виправлення для розвитку

Для розробки ви можете просто відключити кеш у Chrome Dev Tools ( відключення кешу Chrome для розробки веб-сайтів ). Відключення кешу відбувається лише в тому випадку, якщо відкрито діалогове вікно інструментів розробників, тому вам не потрібно турбуватися про перемикання цієї опції щоразу, коли ви регулярно переглядаєте.

Примітка. Використання ' urlArgs ' є правильним рішенням у виробництві, щоб користувачі отримували останній код. Але це ускладнює налагодження, оскільки хром приводить до втрати точок переривання при кожному оновленні (тому що його "новий" файл подається кожен раз).


3

Я не рекомендую використовувати ' urlArgs ' для кеш-пам'яті, що розривається з RequireJS. Оскільки це не вирішує проблему повною мірою. Оновлення версії no не призведе до завантаження всіх ресурсів, навіть якщо ви просто змінили один ресурс.

Для вирішення цієї проблеми я рекомендую використовувати модулі Grunt на зразок 'filerev' для створення редакції №. На додаток до цього я написав спеціальне завдання в Gruntfile, щоб оновити редакцію не там, де потрібно.

Якщо потрібно, я можу поділитися фрагментом коду для цього завдання.


Я використовую комбінацію grunt-filerev та grunt-cache-buster, щоб переписати файли Javascript.
Ян Джеймісон

2

Ось так я це роблю в 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

Динамічне рішення (без urlArgs)

Існує просте рішення цієї проблеми, щоб ви могли завантажити унікальний номер редакції для кожного модуля.

Ви можете зберегти оригінальну функцію Requjs.load, перезаписати її власною функцією та знову проаналізувати модифікований URL на початковий файл Requjs.load:

var load = requirejs.load;
requirejs.load = function (context, moduleId, url) {
    url += "?v=" + oRevision[moduleId];
    load(context, moduleId, url);
};

У нашому процесі побудови я використав "gulp-rev" для створення файлу маніфесту з усією редакцією всіх модулів, які використовуються. Спрощена версія мого завдання з глотком:

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-модуль з номерами версій для модуля NameNames, який включений як 'oRevision' в main.js, де ви перезапишете функцію Requjs.load, як показано раніше.


-1

Це на додаток до прийнятої відповіді @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: введіть тут опис зображення

Посилання в Requ.config.js:

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


2
Ймовірно, тому що ваше рішення проблеми JavaScript - це купа C # коду. Це також купа додаткової роботи та коду, щоб зробити щось, що в кінцевому підсумку робиться точно так само, як прийнята відповідь.
mAAdhaTTah

@mAAdhaTTah Ви можете це зробити з будь-якою стороною мови сервера ... не повинно бути c #. Також це автоматизує процес, оновлюючи бюст кешу під час створення нової версії проекту, забезпечуючи, щоб клієнт завжди кешував останню версію сценаріїв. Я не думаю, що це заслуговує на негативну компенсацію. Це просто розширення відповіді, пропонуючи автоматизований підхід до рішення.
Зак Художник

Я в основному створив це, тому що, підтримуючи додаток, який використовував requ.js, мені було дуже прикро, що потрібно вручну коментувати "(нова дата ()). GetTime ()) і коментувати статичний кешбюстер щоразу, коли я оновлював додаток Легко забути. Раптом клієнт перевіряє зміни та бачить кешований сценарій, щоб вони думали, що нічого не змінилося .. Все тому, що ви просто забули змінити кеш-пам'ять. Цей невеликий додатковий код стирає шанс того, що це станеться
Зак Художник

2
Я цього не відзначав, не знаю, що насправді сталося. Я просто пропоную код, який не тільки не js, а мова # шаблонів C #, не буде корисним JS-розробникам, які намагаються отримати відповідь на свою проблему.
mAAdhaTTah

-2

У моєму випадку я хотів завантажувати одну і ту ж форму щоразу, коли клацнув, я не хотів, щоб зміни, внесені у файл, залишалися. Це може не стосуватися цієї публікації точно, але це може бути потенційним рішенням на стороні клієнта без встановлення конфігурації для вимоги. Замість того, щоб безпосередньо надсилати вміст, ви можете зробити копію потрібного файлу та зберегти недоторканий фактичний файл.

LoadFile(filePath){
    const file = require(filePath);
    const result = angular.copy(file);
    return result;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.