Технология кэширования для оптимизации производительности интерфейса

HTTP JavaScript

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

Начните с протокола HTTP

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

Вообще говоря, для полного процесса кэширования запроса HTTP GET есть семь основных шагов: ①Начиная с получения сетевого запроса, ②Клиент прочитает сообщение запроса и проанализирует сообщение, а затем извлечет URL-адрес и различные заголовки, ③Затем отправит запрос есть ли локальная копия, если локальной копии нет, копия будет получена с сервера и сохранена локально. ④ Затем он проверит, достаточно ли свежа копия (обнаружение свежести), если срок действия кеша истек, он спросит сервер, есть ли какие-либо обновления, ⑤ Сервер будет использовать новый заголовок и кешированное тело для создания ответного сообщения. , ⑥ Наконец отправлено клиенту. ⑦ В зависимости от сервера вы можете дополнительно создать журнал для записи процесса.

Конкретный процесс можно увидеть на следующем рисунке (это изображение взято из официального руководства по HTTP):

http请求缓存流程图

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

Сильный кеш

Сильное кэширование в основном контролируется полями Cache-Control и Expires в заголовке ответа. Где Expires определяется в HTTP 1.0, который указывает абсолютный срок действия. Cache-Control — это поле управления кешем, появившееся в HTTP 1.1. Cache-Control:max-age определяет максимальный период использования, который является допустимым временем существования с момента создания документа до момента, когда кеш больше не действителен. Поскольку Expires является продуктом эпохи HTTP1.0, в его дизайне изначально есть некоторые недостатки: если локальное время и время сервера слишком отличаются друг от друга, это может привести к путанице в кеше. Когда эти два поля используются одновременно, приоритет Cache-Control выше. Эффект от этих двух полей аналогичен, и клиент будет проверять, доступен ли кеш, сравнивая локальное время со временем жизни сервера. Если кеш не превышает своего времени жизни, клиент будет напрямую использовать локальный кеш. Если срок действия истек, кэш также объявляется недействительным. Затем клиент снова свяжется с сервером, чтобы убедиться, что кеш необходимо обновить.

Согласовать кеш

Если надежный механизм кэширования обнаруживает недействительность кэша, ему необходимо выполнить повторную проверку сервера. Этот механизм кэширования также называется согласованным кэшированием. Когда браузер получает запрос в первый раз, он будет содержать дату последней модификации сервера (Last-Modified) ресурса или тег (Etag) ресурса в заголовке ответа. В последующих запросах сервер будет определять, является ли ресурс недействительным, в соответствии с полями If-Modified-Since (соответствует Last-Modified) и (If-None-Match) в заголовке запроса. повторно отправить новый ресурс клиенту, чтобы обеспечить доступность ресурсов.

Поле Last-Modified соответствует времени последней модификации ресурса, например: `Last-Modified:

Сб, 30 декабря 2017 г., 20:18:56 по Гринвичу`, когда клиент снова запрашивает ресурс, он прикрепляет поле If-Modified-Since к заголовку своего запроса, а значение является последним измененным значением, возвращенным ранее. Если срок действия ресурса не истек и кеш попал, сервер напрямую вернет код состояния 304, и клиент будет напрямую использовать локальный ресурс. В противном случае сервер повторно отправляет ресурс ответа.

Другой способ согласования проверки кеша — передача кода проверки вместо времени, что гарантирует, что сетевые ресурсы не будут заняты повторно, когда содержимое файла остается неизменным. Поле Etag в заголовке ответа — это тег, помечаемый сервером для ресурса, и с помощью этого тега можно обновить кеш. Последующие запросы будут сопровождаться полем If-None-Match в заголовке запроса, значением которого является значение этого тега.

Следует отметить, что если в заголовке ответа присутствуют и Etag, и Last-Modified, сначала будет сравниваться Etag, а затем Last-Modified.

кеш браузера

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

localstorage

Первоначально схема кэширования протокола HTTP очень хороша, но когда пользователь активно запускает обновление содержимого страницы, например, F5 и т. д., сильный кеш браузера становится недействительным, а затем он преобразуется в согласованный кеш. Использование LocalStorage может игнорировать активное поведение пользователя при обновлении и может хранить большие ресурсы (более 2 МБ).

Использование localStorage также проще:

const key = 'scq000';
const value = 'hello world';

// 存
localStorage.setItem(key, value);

// 取
localStorage.getItem(key);

Хотя localStorage обычно используется для хранения данных приложения, его также можно использовать для хранения статических ресурсов, таких как js и css.

<script id="testJs" src="example.js"></script>
// 以js为例
var lsKey = 'loadJSv1.0'; // 作为localStorage存取的key;

// 获取要缓存或者执行的源码内容
function getScriptContent(url, callback) {
    var httpRequest = new XMLHttpRequest();
	httpRequest.onreadystatechange = function() {
        if (httpRequest.readyState === 4) {
            if (httpRequest.status === 200) {
            	// 获取代码内容
                var codeStr = httpRequest.responseText;
          	    callback && callback(codeStr);
            }
        }
    };
	httpRequest.open('GET', url);
	httpRequest.send();
}

// 第一次运行的时候缓存
function cacheJs(url) {
  	// 获取代码内容 
  	getScriptContent(url, function(codeStr) {
        console.log(codeStr);
        // 执行代码并缓存
        var script = document.createElement('script');
		script.innerHTML = codeStr;
  		localStorage.setItem(lsKey, codeStr);
    });
}

// 加载源码
function loadJs(url) {
    // 读取缓存
 	var cacheStr = localStorage.getItem(lsKey);
  	if(cacheStr) {
      	// 插入浏览器中,或者也可以直接使用eval执行
        var script = document.createElement('script');
		script.innerHTML = cacheStr;
        console.log("使用缓存成功");
    } else {
        // 没有缓存,就会从服务器获取源码并缓存到本地
        cacheJs(url);
    }
}

// 第一次执行的时候,会直接执行并缓存到localhost中去,第二次进入的时候,会直接使用缓存
loadJs('http://code.jquery.com/jquery-3.2.1.min.js')

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

В качестве решения для оптимизации производительности этот метод также широко используется на мобильных веб-страницах. Однако очевидны и недостатки: поскольку localStorage хранится локально, легко вызвать атаки с внедрением xss. Если вы хотите использовать эту схему, вы должны принять соответствующие меры безопасности. Рекомендовать статью здесь:Повысьте безопасность кода localStorage с помощью SRI.

Решение для кэширования приложений

HTML5 использовался для предоставления механизма кэширования приложений, который позволял веб-приложениям работать в автономном режиме. Это кэш приложения (использующий файл mainfest для кэширования), и, поскольку решение в настоящее время удаляется из веб-стандарта, здесь оно представлено лишь вкратце.

  1. При создании нового html-файла добавьте атрибут mainfest и укажите файл манифеста кеша, который используется, когда приложение находится в автономном режиме.
<!DOCTYPE html>
<html manifest="index.appcache">
<head>
	<title></title>
</head>
<body>

</body>
</html>
  1. Создайте новый файл манифеста кеша index.appcache
CACHE MANIFEST
# v1 - 2017-11-11 
# 缓存版本号

# 指定需要被缓存的文件
CACHE:
index.html
script.js

# 指定需要和服务器连接的白名单,将不进行缓存
NETWORK:
style.css

# 回退页面,当资源无法访问,浏览器将采用该页面
FALLBACK:
index_bak.html

Относительно плохой частью этой схемы является то, что она должна взаимодействовать с сервером, обновление версии файла mainfest также является проблемой, а ресурсы не поддерживают частичное обновление. Если вы хотите узнать больше, вы можете посетитьUsing the application cache

Service Worker

В качестве альтернативы AppCache, Service Worker является относительно новой технологией.Его цель состоит в том, чтобы улучшить взаимодействие с пользователем веб-приложений.Он может реализовать ряд функций, таких как отправка сообщений в автономном режиме, чтобы еще больше сократить количество собственных приложений. . Service Worker можно рассматривать как прокси-скрипт Javascript, независимый от браузера.Благодаря управлению JS приложение может сначала получить ресурсы локального кэша (сначала в автономном режиме), чтобы обеспечить основные функции даже в автономном режиме. Из соображений безопасности Service Worker можно использовать только по протоколу https, но для простоты разработки текущие браузеры поддерживают localhost для использования Service Worker по умолчанию.

Весь процесс использования Service Worker включает в себя ряд состояний, таких как регистрация, установка, активация, уничтожение сна и так далее.

регистр

Сначала вам нужно зарегистрировать Service Worker на странице. В файле ввода нужно прописать:

if(‘serviceWorker' in navigator) {
  navigator.serviceWorker.register('./testSW.js', {scope: '/src'}).then(reg => {
	console.log('service worker is working', reg);    
  }).catch(e => console.log('register service worker failed'));
}

Из-за проблем совместимости обработку обнаружения функций браузера необходимо выполнять в начале кода. При регистрации параметр scope является необязательным и используется для ограничения рабочей области ПО.

Установить и активировать

// 用来标记缓存
const CACHE_FILE = 'my-sw-demo-v1';
let filesToCache = [
  '/',
  '/index.html',
  '/scripts/main.js',
  '/styles/main.css'
];

// 安装
self.addEventListener('install', event => {
  event.waitUntil(
  	caches.open(CACHE_FILE)
  		.then(cache => cache.addAll(filesToCache));
  );
});

// 添加fetch事件监听
self.addEventListener('fetch', event => {
  event.responseWith(
  	caches.match(event.request)
  		.then(response => response)
  		.catch(() => fetch(event.request));
  );
});

Когда пользователь посещает страницу в первый раз, происходит событие установки ПО.Метод addAll получает список URL-адресов файлов для кэширования, автоматически получает эти файлы и сохраняет их в кэше. Следующий зарегистрированный прослушиватель событий выборки будет запускаться каждый раз, когда запрашивается ресурс, контролируемый ПО, перехватывать запрос и сопоставлять соответствующий ресурс в кэше. Если кеш попадает, верните ресурс напрямую, в противном случае инициируйте запрос на выборку. Конечно, если вы хотите пойти дальше, вы можете получить ресурс и добавить полученный ресурс в кеш при промахе кеша. Кроме того, предусмотрена резервная схема, когда сеть недоступна. Приведенный выше код можно переписать следующим образом:

// 添加fetch事件监听
self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).catch(() => {
      return fetch(event.request).then(response => {
        return caches.open('v1').then(cache => {
          cache.put(event.request, response.clone());
          return response;
        });
      });
    }).catch(() => {
      // 回退资源
      return caches.match('/fallback.html');
    })
  );
});

Обновление ресурсов

Если ПО в приложении уже установлено, но обнаружено, что при обновлении доступна новая версия, она будет установлена ​​автоматически. Однако следует отметить, что новая версия ПО будет активирована только тогда, когда больше не будет загруженных страниц, использующих старую версию ПО.

Давайте изменим номер версии выше:

const CACHE_FILE = 'my-sw-demo-v2';

Обновите страницу в это время, когдаinstallПри возникновении события, если предыдущая версия (my-sw-demo-v1) все еще используется другими страницами, новая версия не будет активирована.Когда все страницы больше не используют v1, будет активирована v2 и начать реагировать на Запросы.

удалить старый кеш

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

// 清理缓存操作
self.addEventListener('activate', event => {
  // 设置白名单,不需要删除的缓存key
  const cacheWhiteList = ['v2'];
  
  event.waitUntil(
  	cache.keys().then(keyList => {
      return Promise.all(keyList.map(key => {
        if (!cacheWhiteList.includes(key)) {
          // 如果不在白名单里面,就删除该缓存
          return cache.delete(key);
        }
      }));
  	});
  )
});

инструменты отладки

При отладке можно ввести в Google Chromechrome://serviceworker-internals/Посмотрите, как работают отдельные сценарии программного обеспечения страницы. Вы также можете просмотреть SW-скрипт текущей страницы в инструментах разработчика:

ПО пока находится в стадии разработки, поддержка различных браузеров на ПК не очень высока, но большинство из них могут поддерживаться на стороне мобильного телефона. В качестве основной технологии PWA Google предоставляет множество полезных инструментов для SW, таких как:Sw-precache, Sw-toolbox, Заинтересованные могут пойти на некоторые исследования. Ниже приведены некоторые полезные инструменты и справочные статьи. Если вам нужно узнать больше, вы можете их прочитать:serviceworker-webpack-plugin

https://www.npmjs.com/package/workbox-webpack-plugin

http://air.ghost.io/using-workbox-webpack-to-precache-with-service-worker/

https://developer.mozilla.org/zh-CN/docs/Web/API/Service_Worker_API

https://developer.mozilla.org/zh-CN/docs/Web/API/Service_Worker_API/Using_Service_Workers

https://ivweb.io/topic/5876d4ee441a881744b0d6d6

https://x5.tencent.com/tbs/guide/serviceworker.html

https://foio.github.io/

https://huangxuan.me/2017/07/12/upgrading-eleme-to-pwa/

Наконец, открывая книгу 2018 года, я желаю всем читателям гладкой работы и счастливой жизни в новом году!