Önbelleğe Alma Sorunlarını Giderme

Faydalı Alternatif Metin Yazma

başlatırken bu sitenin yeni versiyonu Son zamanlarda, bazı tarayıcıların beklenmedik bir şekilde eski sürümü önbelleğe almasıyla ilgili birkaç sorunla karşılaştım – bunun tamamen yeniden oluşturulmasına rağmen. Bu, bazı kullanıcıların önbelleklerini manuel olarak temizlemedikleri sürece sitenin önceki sürümünü görmeye devam ettikleri anlamına geliyordu. Açıkçası bu, her kullanıcıdan yapılacak makul bir istek değil!

Dosyaların önbelleğe alınmasının farklı nedenleri vardır ve çoğunlukla önbelleğe alma iyi bir şeydir. Bir kez indirildikten sonra, tarayıcının tekrar tekrar istekte bulunmasını ve dosyaları yeniden indirmesini önlemek için varlıklar bir önbellekte saklanabilir. Önbelleğe almanın içini ve dışını öğrenmek istiyorsanız, MDN belgeleri başlamak için harika bir yerdir. Bu makale, özellikle yukarıda açıklandığı gibi istenmeyen çeşitliliğin önbelleğe alınmasını ele almaktadır. Değişiklikleri dağıttıktan sonra tarayıcıların en son dosyaları sunmasını sağlamak için attığım bazı adımları paylaşacağım.

Benzersiz dosyaları dağıtma

Benzersiz bir ada sahip bir dosyayı dağıtırsak, tarayıcı onu yeni bir dosya olarak tanıyacak ve sunacaktır. Yaygın olarak kullanılan bir önbellek bozma tekniği, varlık dosyalarımızın (CSS ve JS dosyaları gibi) adlarına benzersiz bir tanımlayıcı (veya karma) eklemektir. style.css olur style27850398694772.cssörneğin.

Bu sitenin önceki sürümü ile inşa edilmiştir Gatsby, bunu kutudan çıkardı. İle birlikte onbir, şu anda kullandığım statik site oluşturucu, biraz çalışmamız gerekiyor. Eleveny’nin aslında bunun için bir eklentisi var, ancak kullanıyorum Parsel varlık dosyalarını oluşturmak için, bu yüzden burada bizim için çalışmayacak. Dosya karmasını uygulamak için aşağıdakileri yapabiliriz:

  1. Varlık dosyalarını oluşturun.
  2. Benzersiz bir karma değişken oluşturun ve bunu bir veri dosyasına yazın. Bunun bir değişken olarak Eleventy dosyalarımıza alındığından emin olun.
  3. Hash değişkenini ekleyerek varlık dosyalarını yeniden adlandırın.

Bunu parçalayalım:

Varlık dosyalarını oluşturun

Parsel’i çalıştırıyorum build CSS’imizi Sass’tan derlemek ve JS modüllerimizi bir araya getirmek için komut paket.json:

"prod:parcel": "build:*",
"build:css": "parcel build src/css/styles.scss",
"build:js": "parcel build src/js/index.js",

Varsayılan olarak bunlar bir dist dizin, ancak gerekirse çıktı dosyasını değiştirebilirsiniz. Parselin inşa seçenekleri.

Eleventy’yi çalıştırmak, şablon dosyalarımızı aynı dizine oluşturur. benim içinde tanımlanmış bir komut dosyası var paket.json bunun için de:

"prod:eleventy": "npx @11ty/eleventy",

Dosyaları karma hale getirmeden, HTML’mdeki yollarına göre kolayca başvurabilirim:

<link rel="stylesheet" href="/styles.css" />

Benzersiz bir karma oluşturun

Benzersiz bir karmaya sahip bir dosya oluşturmak için biraz JS yazmamız gerekiyor. Bir dosya oluşturdum, onBuild.jsaşağıdaki komut dosyasıyla çalıştırabilirim:

"hash": "node onBuild.js"

Bu dosyada, benzersiz bir karma oluşturmak için bir zaman damgası kullanabiliriz – çünkü her seferinde farklı olacaktır:

let hash = Date.now()
hash = hash.toString()

Date.now() bir sayı çıktısı veriyor, ancak bunu bir JSON dosyasına yazmak istiyoruz, bu yüzden onu bir dizgeye dönüştürüyorum.

Ardından, bu hash’i Eleventy’deki bir JSON dosyasına yazmak için Node File System modülünü kullanabiliriz. _veri dizin:

fs.writeFile('src/_data/version.json', hash, function (err) {
if (err) return console.log(err)
console.log(`${hash} > src/_data/version.json`)
})

Artık bu değişkeni Eleventy şablon dosyalarımızda kullanmakta özgürüz. kullanıyorum Rahibeler benim şablonlama dilim olarak, bu şekilde ekleyebiliriz:

<link rel="stylesheet" href="styles{ { version } }.css" />

(Benzer şekilde JS dosyamıza da uygulayabiliriz.)

Dosyaları yeniden adlandırın

Aynısı onBuild.js dosyasında, hash’i ekleyerek derlenmiş CSS ve JS dosyalarımızı yeniden adlandıracağız:

fs.rename('dist/styles.css', `dist/styles${hash}.css`, function (err) {
if (err) return console.log(err)
console.log(`dist/styles.css > dist/styles${hash}.css`)
})

fs.rename('dist/index.js', `dist/index${hash}.js`, function (err) {
if (err) return console.log(err)
console.log(`dist/index.js > dist/index${hash}.js`)
})

Bu betiği derlemede çalıştırmamız gerekiyor, ancak bu, zaten mevcut olan CSS ve JS dosyalarına bağlıdır. uzak dizin (yapı dizinimiz). paketi kullanıyorum npm-run-all komut dosyalarının ne zaman eşzamanlı veya sıralı olarak çalıştırılacağını belirtmek için. geri paket.jsonkullanmak run-s komutunu sağlayabiliriz hash komut dosyası çalışır sonrasında CSS ve JS dosyalarını oluşturan komut:

"build": "run-s prod:parcel hash",

Son olarak, CSS ve JS dosyaları oluşturulduktan ve karma değişken oluşturulup dosya adlarına eklendikten sonra, karma değişkenin var olacağını bilerek Eleventy şablon dosyalarımızı derleyebiliriz. Öyleyse, Eleventy build komutumuzu ekleyelim. build komut dosyası, böylece komutlar sırayla çalıştırılır:

"build": "run-s prod:parcel hash prod:eleventy",

Dev modunda hash’i kaldırma

Parsel’i çalıştırdığımda watch modunda (yani sitemi geliştirirken), hash değişken, çünkü Parsel bu noktada yalnızca karma olmayan dosyaları oluşturacaktır. Ek olarak, bir komut dosyası oluşturabiliriz (ki buna arıyorum onStart.js) çalıştırdığımda çalıştırmak için npm start emretmek:

const fs = require('fs')

fs.writeFile('src/_data/version.json', '', function (err) {
if (err) return console.log(err)
console.log(`${''} > src/_data/version.json`)
})

Bu, içindeki karma değişkeni kaldırır. sürüm.json veri dosyası, Nunjucks dosyalarımızda atıfta bulunulan dosya adlarına eklenmemesini sağlar.

sınırlamalar

Şu anda resimler gibi diğer varlık dosyalarını değil, yalnızca CSS ve JS dosyalarını karıştırıyorum, bu da bunların hala önbelleğe alınma riskini taşıdığı anlamına geliyor. Görüntülerle bu şekilde uğraşmak daha karmaşık olurdu ve çok sık değişmeleri pek mümkün olmadığı için bu durumda bu ekstra adımları atmamaya karar verdim.

İçerik tabanlı sürüm oluşturma

Bir zaman damgası kullanıyorsak, dosyaların her derlemede yeniden karma oluşturulacağını akılda tutmakta fayda var. Dosya içeriği değişmemiş olsa bile, kullanıcıların yeni dosyalar indirmeleri gerekecektir. İçerik tabanlı hash için bunun yerine şunu kullanabiliriz: MD5 paket. İçerik tabanlı karma uygulama adımı burada ele alacağımızdan biraz daha kapsamlıdır, ancak Eleventy başlangıç ​​projem, Elli-Parsel yerleşik içerik tabanlı sürüm oluşturma özelliğine sahiptir.

Önbellek denetimi HTTP başlıklarını ayarla

Ek olarak, tarayıcıların HTML sayfalarımızı önbelleğe almasını engelleyebiliriz. HTTP üstbilgileri. Sitem şununla barındırılıyor: netleştirbu, başlıkları bir dizinde belirtebileceğim anlamına gelir. netlify.toml yapılandırma dosyası:

[[headers]]
for = "/*"

[headers.values]
cache-control = '''
max-age=0,
no-cache,
no-store,
must-revalidate'''

Ayarlayarak max-age="0"tarayıcının dosyayı eski olarak kabul etmesini sağlayarak sunucuda yeniden doğrulama yapmasını sağlarız.

Ayrıca önbellek kontrol başlıklarını HTML meta etiketleri olarak da ayarlayabiliriz:

<meta
http-equiv="Cache-Control"
content="no-store, no-store, must-revalidate"
/>

<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />

Ancak bu, meta etiketler yalnızca bir bazı tarayıcı önbellekleri, proxy önbellekleri değil. Meta etiketleri kullanma ile HTTP üstbilgileri arasındaki farkı öğrenmek için biraz araştırma yapmam gerekti, ancak Bu makale iyi açıklıyor.

Servis çalışanları

Yukarıdaki tüm adımları attıktan sonra bile sitemin önbelleğe alınmasıyla ilgili bazı sorunlar yaşadım. Anlaşıldı ki, cevap bir servis çalışanıydı. Servis çalışanları, web uygulamanızdan ayrı olarak ana iş parçacığında çalışan Javascript çalışanlarıdır. Özellikle ağ isteklerini ele geçirme ve yanıtları önbelleğe alma gibi her türlü görevi yerine getirebilirler. Servis çalışanlarının kendileri oldukça büyük bir konudur, bu yüzden burada ayrıntılara girmeyeceğim. Ancak daha fazla okumakla ilgileniyorsanız, kontrol etmenizi öneririm Google’ın resmi belgeleri.

Siteniz için kayıtlı hizmet çalışanlarını Chrome’da görmek için geliştirici araçları panelini açın ve ‘Uygulama’ sekmesine gidin. ‘Uygulama’ menüsünde, ‘Servis çalışanları’nı görmelisiniz.

Yeni sitemle ilgili ilginç olan şey, yapmadı bir servis çalışanım var – ancak geliştirme araçlarımda hala kayıtlı bir servis çalışanı görüyordum. Neden? Niye? Çünkü sitenin eski versiyonunda bir servis görevlisi vardı ve kayıtsız değildi. Geliştirme araçları panelinden bir hizmet çalışanının kaydını silebilirsiniz, ancak elbette her kullanıcının bunu yapmasını bekleyemeyiz! Bu yüzden eski servis çalışanının kaydını silmemiz gerekiyor.

Kayıtsız hizmet çalışanları

Servis çalışanları Javascript’te kayıtlıdır ve ayrıca kayıtlı olmayabilirler. Mevcut hizmet çalışanlarını kontrol etmek ve mevcut olanların kaydını silmek için ana JS dosyamıza aşağıdakileri ekleyebiliriz:

navigator.serviceWorker.getRegistrations().then(function (registrations) {
for (let registration of registrations) {
registration.unregister().then((unregistered) => {
console.log(
unregistered == true ? 'unregistered' : 'failed to unregister'
)
})
}
})

Servis çalışanının güncellenmesi

Başka bir seçenek de eskisini güncellemek için yeni bir hizmet çalışanı kurmaktır. Google’ın belgelerinde hizmet çalışanlarının güncellenmesiyle ilgili olarak şunlar bulunur:

Kullanıcı sitenize gittiğinde, tarayıcı arka planda hizmet çalışanını tanımlayan komut dosyasını yeniden indirmeye çalışır. Service Worker dosyasında şu anda sahip olduğu dosyayla karşılaştırıldığında bir baytlık bile fark varsa, onu yeni olarak kabul eder.

Bu nedenle, hizmet çalışanını güncellemek (veya yeniden kaydettirmek) eskisinin üzerine etkili bir şekilde yazmalıdır. Projemde dosyayı oluşturdum service-worker.jsve bunun Parsel ile oluşturulmasını sağladım. build.js emretmek:

"build:js": "parcel build src/js/index.js src/js/service-worker.js"

Öncelikle yeni servis çalışanını ana JS dosyamıza kaydetmemiz gerekiyor. Önce tarayıcının servis çalışanlarını desteklediğini kontrol ederiz, ardından yeni servis çalışanını yüke kaydederiz:

if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('/service-worker.js').then(
function (registration) {
console.log(
'ServiceWorker registration successful with scope: ',
registration.scope
)
},
function (err) {
console.log('ServiceWorker registration failed: ', err)
}
)
})
}

Bir kullanıcı sitenizi ziyaret ettiğinde, hizmet çalışanı yüklenir. Daha sonra bir “bekleme” durumuna girer – mevcut bir servis çalışanı varsa, yeni servis çalışanı kontrolü ele geçirmek için sayfanın yenilenmesini bekleyecektir.

Servis çalışanı dosyasının kendisinde (service-worker.js bu projede), servis çalışanı kurulduğunda ve etkinleştirildiğinde çalıştırılacak geri aramaları belirtebiliriz. Kurulumda arayabiliriz self.skipWaiting() hemen kontrolü ele almasını söylemek için:

self.addEventListener('install', function () {
self.skipWaiting()
})

Şimdi, herhangi bir eski hizmet çalışanının kaydını silmek istiyorsak (bu dahil!), activate Etkinlik:

self.addEventListener('activate', function() {
.then(() => {
self.registration.unregister()
.then(() => console.log('unregister'))
})
.catch((err) => console.log(err))
})

Muhtemelen servis çalışanımızla, mevcut bir servis çalışanını kaldırmaktan biraz daha fazlasını yapmak istiyoruz, ancak bu, bu makalenin kapsamı dışındadır.

Bir servis çalışanı ile önbelleği temizleme

Bu, sorunlu hizmet çalışanının kaydını silme işini yaptı ve başlangıçta yeterli göründü. Ancak bazı kullanıcıların, tam bir yeniden yükleme gerçekleştirene kadar sitenin eski sürümünü görmeye devam ettiği ortaya çıktı. Suçlu olduğu ortaya çıktı gatsby-eklenti-çevrimdışı, eski Gatsby siteme yüklediğim bir eklenti. Artık Gatsby kullanmıyor olsam da, tarayıcı geliştirme araçlarında önbelleği açtığımda, orada o eklentiyle ilgili birkaç dosya görebildim.

Sonunda, önbelleği temizlemek için yeni hizmet çalışanımı kullanarak bunu çözebildim:

self.addEventListener('install', function (e) {
self.skipWaiting()
})

self.caches
.keys()
.then((keys) => {
keys.forEach((key) => {
console.log(key)
self.caches.delete(key)
})
})
.then(() => {
self.registration.unregister()
console.log('unregister')
})
.catch((err) => console.log(err))

Son olarak, kullanıcının manuel olarak yapması yerine servis çalışanının kontrolü altındaki açık sekmelerin bir listesini alma ve bunları yeniden yüklemeye zorlama gibi isteğe bağlı adımı atabiliriz. self.clients.matchAll() açık tarayıcı sekmelerinin bir listesini alır. Servis çalışanı dosyamız şimdi şöyle görünüyor:

self.addEventListener('install', function (e) {
self.skipWaiting()
})

self.caches
.keys()
.then((keys) => {
keys.forEach((key) => {
console.log(key)
self.caches.delete(key)
})
})
.then(() => {
self.registration.unregister()
console.log('unregister')
})
.then(() => {
self.clients.matchAll()
console.log(self.clients)
})
.then((clients) => {
clients.forEach((client) => client.navigate(client.url))
})
.catch((err) => console.log(err))

Kaynaklar

Bu süreçte önbelleğe alma ve hizmet çalışanları hakkında çok şey öğrendim! İşte bana yardımcı olan kaynaklardan bazıları:

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.