Эта статья также размещена в -

DEV - Удаление кеша приложения React

TL; DR - SemVer ваше приложение и создание meta.json файла для каждой сборки, который не будет кэшироваться браузером. Сделайте недействительный кеш и принудительно перезагрузите приложение при несовпадении версий. Примечание. Примеры и пояснения в этом сообщении основаны на React. Но эта стратегия будет работать с любым веб-приложением / фреймворком.

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

Краткое введение в кеширование -

Кеширование сервера. Веб-серверы кэшируют ресурсы при первом запросе. Во второй раз ресурсы обслуживаются из кеша сервера. Есть еще много всего - CDN, исходные серверы, пограничные серверы и т. Д., Но мы не будем вдаваться в подробности. Аннулировать кеш сервера довольно просто, поскольку мы контролируем наш сервер, и при каждом новом развертывании мы можем автоматически или вручную очищать старый кеш.

Кеширование браузера. Браузеры также кэшируют ресурсы по-своему. Когда сайт загружается в первый раз в браузере пользователя, браузер решает кэшировать некоторые ресурсы (в основном ресурсы, такие как изображения, js и CSS) локально, и в следующий раз, когда пользователь посещает тот же сайт, браузер обслуживает ресурсы из локальный кеш. Поскольку у нас нет контроля над браузером пользователя, очистка кеша в браузере пользователя в прошлом всегда была сложной задачей. Заголовки кеша и инструменты сборки, такие как webpack, генерирующие уникальные фрагменты для каждой сборки, немного упрощают управление, но все же здесь есть подводные камни.

Вот некоторые из ошибок с кешированием браузера:

  1. Браузеры обычно игнорируют проверку кеша несколько раз, если сайт обновляется на той же вкладке - если пользователь закрепляет вкладку, велика вероятность сайт будет загружен из кеша браузера, даже если кеш сервера очищен.
  2. Если ваше приложение регистрирует работника службы, то кеш работника службы будет недействительным только в том случае, если пользователь откроет сайт в новая вкладка. Пользователь навсегда застрянет в кэше сервис-воркера, если вкладка никогда не будет закрыта.
  3. Если пользователь добавляет сайт на главный экран на мобильном устройстве / планшете, то кеш браузера будет недействителен только если пользователь явно выходит из приложения - это почти то же самое, что открыть ту же вкладку в браузере. Я знаю людей, которые месяцами не закрывают приложения на главном экране.

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

Простой, но эффективный подход

  • СемВер ваши деплои
  • Свяжите версию приложения с приложением
  • Создавать meta.json файл с версией приложения для каждой сборки
  • Получать meta.json при загрузке и сравнивать версии
  • Принудительно очистить кеш и выполнить полную перезагрузку при несоответствии версий

СемВер ваши деплои

Версируйте все свои деплои с помощью SemVer. Я лично использую эти три команды npm, которые автоматически увеличивают версию пакета и создают коммит git вместе с соответствующим тегом версии.

  • npm version patch - для выпусков только с исправлением ошибок
  • npm version minor - для выпусков с новыми функциями без исправлений ошибок или без них
  • npm version major - для основных выпусков или критических функций

Не забудьте нажать на коммит с атрибутом --tag - git push origin master --tags

Свяжите версию приложения с приложением

Разберите версию пакета во время сборки webpack (или соответствующего инструмента сборки) и установите глобальную переменную в приложении, чтобы вы могли легко проверить версию в консоли браузера, а также использовать ее для сравнения с последней версией.

import packageJson from '{root-dir}/package.json';
global.appVersion = packageJson.version;

Как только это будет установлено, вы сможете проверить версию приложения в консоли браузера, набрав appVersion.

Создавать meta.json файл с версией приложения для каждой сборки

Запустите сценарий, чтобы сгенерировать meta.json файл в public каталоге вашего приложения.

Добавьте prebuild сценарий npm, который будет генерировать файл meta.json перед каждым build.

/* package.json */

{
    "scripts": {
        "generate-build-version": "node generate-build-version",
        "prebuild": "npm run generate-build-version",
        // other scripts
     }
}
/* generate-build-version.js */

const fs = require('fs');
const packageJson = require('./package.json');

const appVersion = packageJson.version;

const jsonData = {
  version: appVersion
};

var jsonContent = JSON.stringify(jsonData);

fs.writeFile('./public/meta.json', jsonContent, 'utf8', function(err) {
  if (err) {
    console.log('An error occured while writing JSON Object to meta.json');
    return console.log(err);
  }

  console.log('meta.json file has been saved with latest version number');
});

После каждой сборки, как только вы развернете приложение, доступ к meta.json можно будет получить по пути /meta.json, и вы можете получить json как конечную точку REST. Он не будет кэшироваться браузером, поскольку браузеры не кэшируют запросы XHR. Таким образом, вы всегда будете получать meta.json самый последний файл, даже если файлы вашего пакета кэшированы.

Таким образом, если appVersion в вашем файле пакета меньше, чем version в meta.json, то мы знаем, что кеш браузера устарел, и нам необходимо сделать его недействительным.

Вы можете использовать этот скрипт для сравнения семантических версий -

// version from `meta.json` - first param
// version in bundle file - second param
const semverGreaterThan = (versionA, versionB) => {
  const versionsA = versionA.split(/\./g);

  const versionsB = versionB.split(/\./g);
  while (versionsA.length || versionsB.length) {
    const a = Number(versionsA.shift());

    const b = Number(versionsB.shift());
    // eslint-disable-next-line no-continue
    if (a === b) continue;
    // eslint-disable-next-line no-restricted-globals
    return a > b || isNaN(b);
  }
  return false;
};

Вы также можете найти этот код в моем примере на GitHub.

Получать meta.json при загрузке и сравнивать версии

Когда App смонтирован, загрузите meta.json и сравните текущую версию с последней версией на сервере.

При обнаружении несоответствия версий = ›принудительно очистить кеш и выполнить полную перезагрузку. Если версии совпадают =› Выполните рендеринг остальной части приложения.

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

/* CacheBuster component */
import packageJson from '../package.json';
global.appVersion = packageJson.version;

const semverGreaterThan = (versionA, versionB) => {
    // code from above snippet goes here
}

export default class CacheBuster extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      loading: true,
      isLatestVersion: false,
      refreshCacheAndReload: () => {
        console.log('Clearing cache and hard reloading...')
        if (caches) {
          // Service worker cache should be cleared with caches.delete()
          caches.keys().then(function(names) {
            for (let name of names) caches.delete(name);
          });
        }
        // delete browser cache and hard reload
        window.location.reload(true);
      }
    };
  }

  componentDidMount() {
    fetch('/meta.json')
      .then((response) => response.json())
      .then((meta) => {
        const latestVersion = meta.version;
        const currentVersion = global.appVersion;

        const shouldForceRefresh = semverGreaterThan(latestVersion, currentVersion);
        if (shouldForceRefresh) {
          console.log(`We have a new version - ${latestVersion}. Should force refresh`);
          this.setState({ loading: false, isLatestVersion: false });
        } else {
          console.log(`You already have the latest version - ${latestVersion}. No cache refresh needed.`);
          this.setState({ loading: false, isLatestVersion: true });
        }
      });
  }

  render() {
    const { loading, isLatestVersion, refreshCacheAndReload } = this.state;
    return this.props.children({ loading, isLatestVersion, refreshCacheAndReload });
  }
}

И мы можем использовать этот CacheBuster компонент для управления рендерингом в App компоненте.

/* App component */
class App extends Component {
  render() {
    return (
      <CacheBuster>
        {({ loading, isLatestVersion, refreshCacheAndReload }) => {
          if (loading) return null;
          if (!loading && !isLatestVersion) {
            // You can decide how and when you want to force reload
            refreshCacheAndReload();
          }

          return (
            <div className="App">
              <header className="App-header">
                <h1>Cache Busting - Example</h1>
                <p>
                  Bundle version - <code>v{global.appVersion}</code>
                </p>
              </header>
            </div>
          );
        }}
      </CacheBuster>
    );
  }
}

Вы также можете найти код для обоих этих компонентов здесь -

CacheBuster - CacheBuster.js

Принудительно очистить кеш и выполнить полную перезагрузку при несоответствии версий

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

Например,

  • Вы можете выполнить полную перезагрузку перед рендерингом приложения.
  • Вы можете показать модальное / всплывающее окно, предлагающее пользователю нажать кнопку и вызвать жесткую перезагрузку
  • Вы можете выполнить жесткую перезагрузку, когда приложение простаивает
  • Вы можете жестко перезагрузить через несколько секунд с помощью setTimeout()

Вы можете найти весь код из этого поста с рабочим примером в этом репо - cache-busting-example

Это все, ребята. Если у вас есть отзывы об этом подходе (хорошие и плохие), дайте мне знать в комментариях.

Очистка кеша - это весело. 🎉

Первоначально опубликовано на https://dineshpandiyan.com.