предисловие
Кэш — это распространенная тема, и ее часто используют в качестве источника знаний на фронтенд-интервью.
В этой статье основное внимание уделяется и обсуждается, как установить кеш в реальном проекте, и дается более разумное решение.
Надежный кеш и кеш согласования
При внедрении кэширования мы привыкли классифицировать кэши как сильные кэши и согласованные кэши. Основное различие между ними заключается в том, нужно ли вам проверять на сервере, действителен ли локальный кеш при использовании локального кеша. Как следует из названия, согласование кеша означает согласование с сервером, чтобы окончательно определить, использовать ли локальный кеш.
Проблемы с двумя схем кэширования
Сильный кеш
Мы знаем, что сильное кэширование в основном контролируется полями Cache-Control и Expire в заголовке http-запроса. Expire — это поле стандарта HTTP1.0, которое здесь можно игнорировать. Мы сосредоточимся на обсуждаемой области Cache-Control.
Как правило, мы устанавливаем значение Cache-Control равным «public, max-age=xxx», что означает, что если к ресурсу снова обращаться в течение xxx секунд, будет использоваться локальный кеш, и никаких запросов к сервер.
Очевидно, что если ресурсы на сервере обновляются в течение xxx секунд, клиент видит старый контент без принудительного обновления. Если вы не торопитесь и можете принять это, разве это не прекрасно? Однако во многих случаях это не так просто, как вы думаете, если фоновый интерфейс также синхронно обновляется при выпуске новой версии, то gg. Кэшированные пользователи все еще используют старый интерфейс, и этот интерфейс был отключен в фоновом режиме. Как сделать?
Согласовать кеш
Самая большая проблема с согласованием кеша заключается в том, что вы должны каждый раз проверять действительность кеша с сервером, что кажется очень простым. Однако для начинающего кодера это неприемлемо. Каждый раз захожу на сервер, какой смысл кешировать.
Лучшие практики
Значение кэширования заключается в сокращении запросов, использовании большего количества локальных ресурсов, повышении удобства работы пользователей и снижении нагрузки на сервер. Таким образом, лучше всего использовать сильный кеш как можно чаще, и в то же время кеш клиента может стать недействительным при обновлении версии.
После обновления версии, как разрешить пользователям использовать последние файлы ресурсов в первый раз? Умные фронтенды придумали способ, при обновлении версии путь к статическим ресурсам меняется кстати, так что это равносильно доступу к этим ресурсам в первый раз, и не будет проблемы с кэшированием .
Отличный веб-пакет позволяет нам приводить хеш-значение к имени файла при его упаковке.
entry:{
main: path.join(__dirname,'./main.js'),
vendor: ['react', 'antd']
},
output:{
path:path.join(__dirname,'./dist'),
publicPath: '/dist/',
filname: 'bundle.[chunkhash].js'
}
Подводя итог, мы можем нарисовать более разумную схему кэширования:
- HTML: использовать согласованный кеш.
- CSS&JS&images: используйте сильный кеш, имя файла с хеш-значением.
Хэш также важен
Webpack предоставляет нам три метода расчета значения хеш-функции, а именно хэш, хэш-функция и хеш-контент. Так в чем разница между тремя?
- хэш: связанный со сборкой всего проекта, хеш-значение файлов, сгенерированных сборкой, одинаковое. Пока в проекте есть изменения файлов, хэш-значение всей конструкции проекта будет меняться.
- Chunkhash: в соответствии с различными входными файлами (Entry) анализируется файл зависимостей, создается соответствующий фрагмент и генерируется соответствующее хэш-значение.
- contenthash: значение хеш-функции, сгенерированное содержимым файла, значение хеш-суммы, сгенерированное другим содержимым, также отличается.
Очевидно, что мы не будем использовать первый. После смены файла и упаковки хэш других файлов изменился, и кеш естественно станет недействительным. Это не то, чего мы хотим.
Каковы основные сценарии применения chunkhash и contenthash? В реальных проектах мы обычно извлекаем CSS в проекте из соответствующих файлов CSS для справки. Если мы используем chunkhash, то при изменении кода CSS мы обнаружим, что при изменении хеш-значения CSS-файла хеш-значение js-файла также изменится. В это время пригодится contenthash.
Расчет ETag
Nginx
Официальный метод расчета ETag по умолчанию для Nginx: «время последней модификации файла в шестнадцатеричном формате — длина файла в шестнадцатеричном формате». Пример: ETag: "59e72c84-2404"
Express
Инфраструктура Express использует промежуточное ПО serve-static для настройки схемы кэширования, которая используетetagПакет npm для реализации вычисления etag. Как видно из его исходного кода, существует два метода расчета:
- Способ 1: использовать размер файла и время модификации
function stattag (stat) {
var mtime = stat.mtime.getTime().toString(16)
var size = stat.size.toString(16)
return '"' + size + '-' + mtime + '"'
}
- Способ 2. Используйте хеш-значение и длину содержимого файла.
function entitytag (entity) {
if (entity.length === 0) {
// fast-path empty
return '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"'
}
// compute hash of entity
var hash = crypto
.createHash('sha1')
.update(entity, 'utf8')
.digest('base64')
.substring(0, 27)
// compute length of entity
var len = typeof entity === 'string'
? Buffer.byteLength(entity, 'utf8')
: entity.length
return '"' + len.toString(16) + '-' + hash + '"'
}
ETag или Last-Modified, кто предпочтительнее
Кэш согласования, есть два поля ETag и Last-Modified. Когда эти два поля существуют одновременно, какое из них преобладает?
В экспрессе с помощьюfreshЭтот пакет, чтобы определить, является ли он последним ресурсом. Основной исходный код выглядит следующим образом:
function fresh (reqHeaders, resHeaders) {
// fields
var modifiedSince = reqHeaders['if-modified-since']
var noneMatch = reqHeaders['if-none-match']
// unconditional request
if (!modifiedSince && !noneMatch) {
return false
}
// Always return stale when Cache-Control: no-cache
// to support end-to-end reload requests
// https://tools.ietf.org/html/rfc2616#section-14.9.4
var cacheControl = reqHeaders['cache-control']
if (cacheControl && CACHE_CONTROL_NO_CACHE_REGEXP.test(cacheControl)) {
return false
}
// if-none-match
if (noneMatch && noneMatch !== '*') {
var etag = resHeaders['etag']
if (!etag) {
return false
}
var etagStale = true
var matches = parseTokenList(noneMatch)
for (var i = 0; i < matches.length; i++) {
var match = matches[i]
if (match === etag || match === 'W/' + etag || 'W/' + match === etag) {
etagStale = false
break
}
}
if (etagStale) {
return false
}
}
// if-modified-since
if (modifiedSince) {
var lastModified = resHeaders['last-modified']
var modifiedStale = !lastModified || !(parseHttpDate(lastModified) <= parseHttpDate(modifiedSince))
if (modifiedStale) {
return false
}
}
return true
}
Мы видим, что если он не обновляется принудительно, а в заголовке запроса есть два поля, if-modified-since и if-none-match, сначала будет оцениваться etag, а затем последний измененный. Конечно, если вам не нравится эта стратегия, вы можете реализовать ее самостоятельно.
Дополнение: Как настроить бэкенд
Вышеизложенное в основном касается того, как упаковывается внешний интерфейс, но как насчет внутреннего интерфейса? Мы знаем, что браузер выбирает схему кэширования на основе соответствующих полей заголовка ответа. Поэтому ключ к бэкенду — возвращать соответствующие поля кеша по разным запросам. Взяв в качестве примера nodejs, если требуется надежный кеш браузера, мы можем установить его следующим образом:
res.setHeader('Cache-Control', 'public, max-age=xxx');
Если вам нужно согласовать кеш, вы можете установить это:
res.setHeader('Cache-Control', 'public, max-age=0');
res.setHeader('Last-Modified', xxx);
res.setHeader('ETag', xxx);
Конечно, уже есть много готовых библиотек, которые позволяют нам легко настроить эти вещи. Я написал простое демо, которое удобно для друзей, нуждающихся в том, чтобы понять принцип. Если вы заинтересованы, вы можетеЧитать исходный код
Суммировать
При выполнении внешнего кеша мы пытаемся настроить длинный и надежный кеш, а также хэш имени файла, кстати, делаем обновления версии. Когда код передается на субподряд, если некоторые из них редко становятся общедоступными библиотеками, упакованными отдельно, это может быть более постоянный кеш.
Если есть какие-либо ошибки или упущения выше, пожалуйста, поправьте меня!
@Author: TDGarden