Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 国产欧美另类第一页,久久亚洲国产成人影院,欧美极品另类

          整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          十分全面!作為前端應(yīng)當(dāng)了解的那些Web緩存知識

          存優(yōu)點(diǎn)

          通常所說的Web緩存指的是可以自動保存常見http請求副本的http設(shè)備。對于前端開發(fā)者來說,瀏覽器充當(dāng)了重要角色。除此外常見的還有各種各樣的代理服務(wù)器也可以做緩存。當(dāng)Web請求到達(dá)緩存時,緩存從本地副本`本地副本`中提取這個副本內(nèi)容而不需要經(jīng)過服務(wù)器。這帶來了以下優(yōu)點(diǎn):

          • 緩存減少了冗余的數(shù)據(jù)傳輸,節(jié)省流量

          • 緩存緩解了帶寬瓶頸問題。不需要更多的帶寬就能更快加載頁面

          • 緩存緩解了瞬間擁塞,降低了對原始服務(wù)器的要求。

          • 緩存降低了距離延時, 因?yàn)閺妮^遠(yuǎn)的地方加載頁面會更慢一些。

          緩存種類

          緩存可以是單個用戶專用的,也可以是多個用戶共享的。專用緩存被稱為私有緩存,共享的緩存被稱為公有緩存。

          私有緩存

          私有緩存只針對專有用戶,所以不需要很大空間,廉價。Web瀏覽器中有內(nèi)建的私有緩存——大多數(shù)瀏覽器都會將常用資源緩存在你的個人電腦的磁盤和內(nèi)存中。如Chrome瀏覽器的緩存存放位置就在:C:\Users\Your_Account\AppData\Local\Google\Chrome\User Data\Default中的Cache文件夾和Media Cache文件夾。

          公有緩存

          公有緩存是特殊的共享代理服務(wù)器,被稱為緩存代理服務(wù)器或代理緩存(反向代理的一種用途)。公有緩存會接受來自多個用戶的訪問,所以通過它能夠更好的減少冗余流量。

          下圖中每個客戶端都會重復(fù)的向服務(wù)器訪問一個資源(此時還不在私有緩存中),這樣它會多次訪問服務(wù)器,增加服務(wù)器壓力。而使用共享的公有緩存時,緩存只需要從服務(wù)器取一次,以后不用再經(jīng)過服務(wù)器,能夠顯著減輕服務(wù)器壓力。

          事實(shí)上在實(shí)際應(yīng)用中通常采用層次化的公有緩存,基本思想是在靠近客戶端的地方使用小型廉價緩存,而更高層次中,則逐步采用更大、功能更強(qiáng)的緩存在裝載多用戶共享的資源。

          緩存處理流程

          而對于前端開發(fā)者來說,我們主要跟瀏覽器中的緩存打交道,所以上圖流程簡化為:

          下面這張圖展示了某一網(wǎng)站,對不同資源的請求結(jié)果,其中可以看到有的資源直接從緩存中讀取,有的資源跟服務(wù)器進(jìn)行了再驗(yàn)證,有的資源重新從服務(wù)器端獲取。

          注意,我們討論的所有關(guān)于緩存資源的問題,都僅僅針對GET請求。而對于POST, DELETE, PUT這類行為性操作通常不做任何緩存

          新鮮度限值

          HTTP通過緩存將服務(wù)器資源的副本保留一段時間,這段時間稱為新鮮度限值。這在一段時間內(nèi)請求相同資源不會再通過服務(wù)器。HTTP協(xié)議中Cache-Control 和 Expires可以用來設(shè)置新鮮度的限值,前者是HTTP1.1中新增的響應(yīng)頭,后者是HTTP1.0中的響應(yīng)頭。二者所做的事時都是相同的,但由于Cache-Control使用的是相對時間,而Expires可能存在客戶端與服務(wù)器端時間不一樣的問題,所以我們更傾向于選擇Cache-Control。

          Cache-Control

          下面我們來看看Cache-Control都可以設(shè)置哪些屬性值:

          max-age(單位為s)指定設(shè)置緩存最大的有效時間,定義的是時間長短。當(dāng)瀏覽器向服務(wù)器發(fā)送請求后,在max-age這段時間里瀏覽器就不會再向服務(wù)器發(fā)送請求了。

          <html>

          <head>

          <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

          <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />

          <meta http-equiv="X-UA-Compatible" content="IE=EDGE" />

          <title>Web Cache</title>

          <link rel="shortcut icon" href="./shortcut.png">

          <script>

          </script>

          </head>

          <body class="claro">

          <img src="./cache.png">

          </body>

          </html>

          var http = require('http');

          var fs = require('fs');

          http.createServer(function(req, res) {

          if (req.url === '/' || req.url === '' || req.url === '/index.html') {

          fs.readFile('./index.html', function(err, file) {

          console.log(req.url)

          //對主文檔設(shè)置緩存,無效果

          res.setHeader('Cache-Control', "no-cache, max-age=" + 5);

          res.setHeader('Content-Type', 'text/html');

          res.writeHead('200', "OK");

          res.end(file);

          });

          }

          if (req.url === '/cache.png') {

          fs.readFile('./cache.png', function(err, file) {

          res.setHeader('Cache-Control', "max-age=" + 5);//緩存五秒

          res.setHeader('Content-Type', 'images/png');

          res.writeHead('200', "Not Modified");

          res.end(file);

          });

          }

          }).listen(8888)

          當(dāng)在5秒內(nèi)第二次訪問頁面時,瀏覽器會直接從緩存中取得資源

          • public 指定響應(yīng)可以在代理緩存中被緩存,于是可以被多用戶共享。如果沒有明確指定private,則默認(rèn)為public。

          • private 響應(yīng)只能在私有緩存中被緩存,不能放在代理緩存上。對一些用戶信息敏感的資源,通常需要設(shè)置為private。

          • no-cache 表示必須先與服務(wù)器確認(rèn)資源是否被更改過(依靠If-None-Match和Etag),然后再決定是否使用本地緩存。

          如果上文中關(guān)于cache.png的處理改成下面這樣,則每次訪問頁面,瀏覽器都需要先去服務(wù)器端驗(yàn)證資源有沒有被更改。

          fs.readFile('./cache.png', function(err, file) {

          console.log(req.headers);

          console.log(req.url)

          if (!req.headers['if-none-match']) {

          res.setHeader('Cache-Control', "no-cache, max-age=" + 5);

          res.setHeader('Content-Type', 'images/png');

          res.setHeader('Etag', "ffff");

          res.writeHead('200', "Not Modified");

          res.end(file);

          } else {

          if (req.headers['if-none-match'] === 'ffff') {

          res.writeHead('304', "Not Modified");

          res.end();

          } else {

          res.setHeader('Cache-Control', "max-age=" + 5);

          res.setHeader('Content-Type', 'images/png');

          res.setHeader('Etag', "ffff");

          res.writeHead('200', "Not Modified");

          res.end(file);

          }

          }

          });

          no-store 絕對禁止緩存任何資源,也就是說每次用戶請求資源時,都會向服務(wù)器發(fā)送一個請求,每次都會下載完整的資源。通常用于機(jī)密性資源。

          關(guān)于Cache-Control的使用,見下面這張圖(來自大額)

          客戶端的新鮮度限值

          Cache-Control不僅僅可以在響應(yīng)頭中設(shè)置,還可以在請求頭中設(shè)置。瀏覽器通過請求頭中設(shè)置Cache-Control可以決定是否從緩存中讀取資源。*這也是為什么有時候點(diǎn)擊瀏覽器刷新按鈕和在地址欄回車,在NetWork模塊中看到完全不同的結(jié)果*

          Expires

          不推薦使用Expires,它指定的是具體的過期日期而不是秒數(shù)。因?yàn)楹芏喾?wù)器跟客戶端存在時鐘不一致的情況,所以最好還是使用Cache-Control.

          服務(wù)器再驗(yàn)證

          瀏覽器或代理緩存中緩存的資源過期了,并不意味著它和原始服務(wù)器上的資源有實(shí)際的差異,僅僅意味著到了要進(jìn)行核對的時間了。這種情況被稱為服務(wù)器再驗(yàn)證。

          • 如果資源發(fā)生變化,則需要取得新的資源,并在緩存中替換舊資源。

          • 如果資源沒有發(fā)生變化,緩存只需要獲取新的響應(yīng)頭,和一個新的過期時間,對緩存中的資源過期時間進(jìn)行更新即可。 HTTP1.1推薦使用的驗(yàn)證方式是If-None-Match/Etag,在HTTP1.0中則使用If-Modified-Since/Last-Modified。

          Etag與If-None-Match

          根據(jù)實(shí)體內(nèi)容生成一段hash字符串,標(biāo)識資源的狀態(tài),由服務(wù)端產(chǎn)生。瀏覽器會將這串字符串傳回服務(wù)器,驗(yàn)證資源是否已經(jīng)修改,如果沒有修改,過程如下(圖片來自淺談Web緩存):

          上文的demo中我們見到過服務(wù)器端如何驗(yàn)證Etag:

          由于Etag有服務(wù)器構(gòu)造,所以在集群環(huán)境中一定要保證Etag的唯一性

          If-Modified-Since與Last-Modified

          這兩個是HTTP1.0中用來驗(yàn)證資源是否過期的請求/響應(yīng)頭,這兩個頭部都是日期,驗(yàn)證過程與Etag類似,這里不詳細(xì)介紹。使用這兩個頭部來驗(yàn)證資源是否更新時,存在以下問題:

          • 有些文檔資源周期性的被重寫,但實(shí)際內(nèi)容沒有改變。此時文件元數(shù)據(jù)中會顯示文件最近的修改日期與If-Modified-Since不相同,導(dǎo)致不必要的響應(yīng)。

          • 有些文檔資源被修改了,但修改內(nèi)容并不重要,不需要所有的緩存都更新(比如代碼注釋)

          關(guān)于緩存的更新問題,請大家看看這里張云龍的回答,本文就不詳細(xì)展開了。

          本文demo代碼如下:

          <!DOCTYPE HTML>

          <html>

          <head>

          <meta http-equiv="Content-Type" content="text/html; charset=utf-8">

          <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />

          <meta http-equiv="X-UA-Compatible" content="IE=EDGE" />

          <title>Web Cache</title>

          <link rel="shortcut icon" href="./shortcut.png">

          <script>

          </script>

          </head>

          <body class="claro">

          <img src="./cache.png">

          </body>

          </html>

          var http = require('http');

          var fs = require('fs');

          http.createServer(function(req, res) {

          if (req.url === '/' || req.url === '' || req.url === '/index.html') {

          fs.readFile('./index.html', function(err, file) {

          console.log(req.url)

          //對主文檔設(shè)置緩存,無效果

          res.setHeader('Cache-Control', "no-cache, max-age=" + 5);

          res.setHeader('Content-Type', 'text/html');

          res.writeHead('200', "OK");

          res.end(file);

          });

          }

          if (req.url === '/shortcut.png') {

          fs.readFile('./shortcut.png', function(err, file) {

          console.log(req.url)

          res.setHeader('Content-Type', 'images/png');

          res.writeHead('200', "OK");

          res.end(file);

          })

          }

          if (req.url === '/cache.png') {

          fs.readFile('./cache.png', function(err, file) {

          console.log(req.headers);

          console.log(req.url)

          if (!req.headers['if-none-match']) {

          res.setHeader('Cache-Control', "max-age=" + 5);

          res.setHeader('Content-Type', 'images/png');

          res.setHeader('Etag', "ffff");

          res.writeHead('200', "Not Modified");

          res.end(file);

          } else {

          if (req.headers['if-none-match'] === 'ffff') {

          res.writeHead('304', "Not Modified");

          res.end();

          } else {

          res.setHeader('Cache-Control', "max-age=" + 5);

          res.setHeader('Content-Type', 'images/png');

          res.setHeader('Etag', "ffff");

          res.writeHead('200', "Not Modified");

          res.end(file);

          }

          }

          });

          }

          }).listen(8888)

          更多深度技術(shù)內(nèi)容,請關(guān)注云棲社區(qū)微信公眾號:yunqiinsight。

          家都知道緩存的英文叫做 cache。但我發(fā)現(xiàn)一個有趣的現(xiàn)象:這個單詞在不同人的口中有不同的讀音。為了全面了解緩存,我們得先從讀音開始,這樣才能夠在和其他同事(例如 PM)交(zhuāng)流(bī)時體現(xiàn)自己的修(bī)養(yǎng)(gé)。cache 的發(fā)音是 /k??/(同 cash),這也是一個廣泛認(rèn)可的發(fā)音。

          前端緩存/后端緩存

          扯了些沒用的,我們先進(jìn)入定義環(huán)節(jié):什么是前端緩存?與之相對的什么又是后端緩存?

          基本的網(wǎng)絡(luò)請求就是三個步驟:請求,處理,響應(yīng)。

          后端緩存主要集中于“處理”步驟,通過保留數(shù)據(jù)庫連接,存儲處理結(jié)果等方式縮短處理時間,盡快進(jìn)入“響應(yīng)”步驟。當(dāng)然這不在本文的討論范圍之內(nèi)。

          而前端緩存則可以在剩下的兩步:“請求”和“響應(yīng)”中進(jìn)行。在“請求”步驟中,瀏覽器也可以通過存儲結(jié)果的方式直接使用資源,直接省去了發(fā)送請求;而“響應(yīng)”步驟需要瀏覽器和服務(wù)器共同配合,通過減少響應(yīng)內(nèi)容來縮短傳輸時間。這些都會在下面進(jìn)行討論。

          本文主要包含

          • 按緩存位置分類 (memory cache, disk cache, Service Worker 等)
          • 按失效策略分類 (Cache-Control, ETag 等)
          • 幫助理解原理的一些案例
          • 緩存的應(yīng)用模式

          按緩存位置分類

          我看過的大部分討論緩存的文章會直接從 HTTP 協(xié)議頭中的緩存字段開始,例如 Cache-Control, ETag, max-age 等。但偶爾也會聽到別人討論 memory cache, disk cache 等。那這兩種分類體系究竟有何關(guān)聯(lián)?是否有交叉?(我個人認(rèn)為這是本文的最大價值所在,因?yàn)樵趯懼拔易约阂彩潜粌煞N分類體系搞的一團(tuán)糟)

          實(shí)際上,HTTP 協(xié)議頭的那些字段,都屬于 disk cache 的范疇,是幾個緩存位置的其中之一。因此本著從全局到局部的原則,我們應(yīng)當(dāng)先從緩存位置開始討論。等講到 disk cache 時,才會詳細(xì)講述這些協(xié)議頭的字段及其作用。

          我們可以在 Chrome 的開發(fā)者工具中,Network -> Size 一列看到一個請求最終的處理方式:如果是大小 (多少 K, 多少 M 等) 就表示是網(wǎng)絡(luò)請求,否則會列出 from memory cache, from disk cache 和 from ServiceWorker。

          它們的優(yōu)先級是:(由上到下尋找,找到即返回;找不到則繼續(xù))

          1. Service Worker
          2. Memory Cache
          3. Disk Cache
          4. 網(wǎng)絡(luò)請求

          memory cache

          memory cache 是內(nèi)存中的緩存,(與之相對 disk cache 就是硬盤上的緩存)。按照操作系統(tǒng)的常理:先讀內(nèi)存,再讀硬盤。disk cache 將在后面介紹 (因?yàn)樗膬?yōu)先級更低一些),這里先討論 memory cache。

          幾乎所有的網(wǎng)絡(luò)請求資源都會被瀏覽器自動加入到 memory cache 中。但是也正因?yàn)閿?shù)量很大但是瀏覽器占用的內(nèi)存不能無限擴(kuò)大這樣兩個因素,memory cache 注定只能是個“短期存儲”。常規(guī)情況下,瀏覽器的 TAB 關(guān)閉后該次瀏覽的 memory cache 便告失效 (為了給其他 TAB 騰出位置)。而如果極端情況下 (例如一個頁面的緩存就占用了超級多的內(nèi)存),那可能在 TAB 沒關(guān)閉之前,排在前面的緩存就已經(jīng)失效了。

          剛才提過,幾乎所有的請求資源 都能進(jìn)入 memory cache,這里細(xì)分一下主要有兩塊:

          1. preloader。如果你對這個機(jī)制不太了解,這里做一個簡單的介紹,詳情可以參閱這篇文章。
          2. 熟悉瀏覽器處理流程的同學(xué)們應(yīng)該了解,在瀏覽器打開網(wǎng)頁的過程中,會先請求 HTML 然后解析。之后如果瀏覽器發(fā)現(xiàn)了 js, css 等需要解析和執(zhí)行的資源時,它會使用 CPU 資源對它們進(jìn)行解析和執(zhí)行。在古老的年代(大約 2007 年以前),“請求 js/css - 解析執(zhí)行 - 請求下一個 js/css - 解析執(zhí)行下一個 js/css” 這樣的“串行”操作模式在每次打開頁面之前進(jìn)行著。很明顯在解析執(zhí)行的時候,網(wǎng)絡(luò)請求是空閑的,這就有了發(fā)揮的空間:我們能不能一邊解析執(zhí)行 js/css,一邊去請求下一個(或下一批)資源呢?
          3. 這就是 preloader 要做的事情。不過 preloader 沒有一個官方標(biāo)準(zhǔn),所以每個瀏覽器的處理都略有區(qū)別。例如有些瀏覽器還會下載 css 中的 @import 內(nèi)容或者 <video> 的 poster等。
          4. 而這些被 preloader 請求夠來的資源就會被放入 memory cache 中,供之后的解析執(zhí)行操作使用。
          5. preload (雖然看上去和剛才的 preloader 就差了倆字母)。實(shí)際上這個大家應(yīng)該更加熟悉一些,例如 <link rel="preload">。這些顯式指定的預(yù)加載資源,也會被放入 memory cache 中。

          memory cache 機(jī)制保證了一個頁面中如果有兩個相同的請求 (例如兩個 src 相同的 <img>,兩個 href 相同的 <link>)都實(shí)際只會被請求最多一次,避免浪費(fèi)。

          不過在匹配緩存時,除了匹配完全相同的 URL 之外,還會比對他們的類型,CORS 中的域名規(guī)則等。因此一個作為腳本 (script) 類型被緩存的資源是不能用在圖片 (image) 類型的請求中的,即便他們 src 相等。

          在從 memory cache 獲取緩存內(nèi)容時,瀏覽器會忽視例如 max-age=0, no-cache 等頭部配置。例如頁面上存在幾個相同 src 的圖片,即便它們可能被設(shè)置為不緩存,但依然會從 memory cache 中讀取。這是因?yàn)?memory cache 只是短期使用,大部分情況生命周期只有一次瀏覽而已。而 max-age=0 在語義上普遍被解讀為“不要在下次瀏覽時使用”,所以和 memory cache 并不沖突。

          但如果站長是真心不想讓一個資源進(jìn)入緩存,就連短期也不行,那就需要使用 no-store。存在這個頭部配置的話,即便是 memory cache 也不會存儲,自然也不會從中讀取了。(后面的第二個示例有關(guān)于這點(diǎn)的體現(xiàn))

          disk cache

          disk cache 也叫 HTTP cache,顧名思義是存儲在硬盤上的緩存,因此它是持久存儲的,是實(shí)際存在于文件系統(tǒng)中的。而且它允許相同的資源在跨會話,甚至跨站點(diǎn)的情況下使用,例如兩個站點(diǎn)都使用了同一張圖片。

          disk cache 會嚴(yán)格根據(jù) HTTP 頭信息中的各類字段來判定哪些資源可以緩存,哪些資源不可以緩存;哪些資源是仍然可用的,哪些資源是過時需要重新請求的。當(dāng)命中緩存之后,瀏覽器會從硬盤中讀取資源,雖然比起從內(nèi)存中讀取慢了一些,但比起網(wǎng)絡(luò)請求還是快了不少的。絕大部分的緩存都來自 disk cache。

          關(guān)于 HTTP 的協(xié)議頭中的緩存字段,我們會在稍后進(jìn)行詳細(xì)討論。

          凡是持久性存儲都會面臨容量增長的問題,disk cache 也不例外。在瀏覽器自動清理時,會有神秘的算法去把“最老的”或者“最可能過時的”資源刪除,因此是一個一個刪除的。不過每個瀏覽器識別“最老的”和“最可能過時的”資源的算法不盡相同,可能也是它們差異性的體現(xiàn)。

          Service Worker

          上述的緩存策略以及緩存/讀取/失效的動作都是由瀏覽器內(nèi)部判斷 & 進(jìn)行的,我們只能設(shè)置響應(yīng)頭的某些字段來告訴瀏覽器,而不能自己操作。舉個生活中去銀行存/取錢的例子來說,你只能告訴銀行職員,我要存/取多少錢,然后把由他們會經(jīng)過一系列的記錄和手續(xù)之后,把錢放到金庫中去,或者從金庫中取出錢來交給你。

          但 Service Worker 的出現(xiàn),給予了我們另外一種更加靈活,更加直接的操作方式。依然以存/取錢為例,我們現(xiàn)在可以繞開銀行職員,自己走到金庫前(當(dāng)然是有別于上述金庫的一個單獨(dú)的小金庫),自己把錢放進(jìn)去或者取出來。因此我們可以選擇放哪些錢(緩存哪些文件),什么情況把錢取出來(路由匹配規(guī)則),取哪些錢出來(緩存匹配并返回)。當(dāng)然現(xiàn)實(shí)中銀行沒有給我們開放這樣的服務(wù)。

          Service Worker 能夠操作的緩存是有別于瀏覽器內(nèi)部的 memory cache 或者 disk cache 的。我們可以從 Chrome 的 F12 中,Application -> Cache Storage 找到這個單獨(dú)的“小金庫”。除了位置不同之外,這個緩存是永久性的,即關(guān)閉 TAB 或者瀏覽器,下次打開依然還在(而 memory cache 不是)。有兩種情況會導(dǎo)致這個緩存中的資源被清除:手動調(diào)用 API cache.delete(resource) 或者容量超過限制,被瀏覽器全部清空。

          如果 Service Worker 沒能命中緩存,一般情況會使用 fetch() 方法繼續(xù)獲取資源。這時候,瀏覽器就去 memory cache 或者 disk cache 進(jìn)行下一次找緩存的工作了。注意:經(jīng)過 Service Worker 的 fetch() 方法獲取的資源,即便它并沒有命中 Service Worker 緩存,甚至實(shí)際走了網(wǎng)絡(luò)請求,也會標(biāo)注為 from ServiceWorker。這個情況在后面的第三個示例中有所體現(xiàn)。

          發(fā)送網(wǎng)絡(luò)請求

          如果一個請求在上述 3 個位置都沒有找到緩存,那么瀏覽器會正式發(fā)送網(wǎng)絡(luò)請求去獲取內(nèi)容。之后容易想到,為了提升之后請求的緩存命中率,自然要把這個資源添加到緩存中去。具體來說:

          1. 根據(jù) Service Worker 中的 handler 決定是否存入 Cache Storage (額外的緩存位置)。
          2. 根據(jù) HTTP 頭部的相關(guān)字段(Cache-control, Pragma 等)決定是否存入 disk cache
          3. memory cache 保存一份資源 的引用,以備下次使用。

          按失效策略分類

          memory cache 是瀏覽器為了加快讀取緩存速度而進(jìn)行的自身的優(yōu)化行為,不受開發(fā)者控制,也不受 HTTP 協(xié)議頭的約束,算是一個黑盒。Service Worker 是由開發(fā)者編寫的額外的腳本,且緩存位置獨(dú)立,出現(xiàn)也較晚,使用還不算太廣泛。所以我們平時最為熟悉的其實(shí)是 disk cache,也叫 HTTP cache (因?yàn)椴幌?memory cache,它遵守 HTTP 協(xié)議頭中的字段)。平時所說的強(qiáng)制緩存,對比緩存,以及 Cache-Control 等,也都?xì)w于此類。

          強(qiáng)制緩存 (也叫強(qiáng)緩存)

          強(qiáng)制緩存的含義是,當(dāng)客戶端請求后,會先訪問緩存數(shù)據(jù)庫看緩存是否存在。如果存在則直接返回;不存在則請求真的服務(wù)器,響應(yīng)后再寫入緩存數(shù)據(jù)庫。

          強(qiáng)制緩存直接減少請求數(shù),是提升最大的緩存策略。 它的優(yōu)化覆蓋了文章開頭提到過的請求數(shù)據(jù)的全部三個步驟。如果考慮使用緩存來優(yōu)化網(wǎng)頁性能的話,強(qiáng)制緩存應(yīng)該是首先被考慮的。

          可以造成強(qiáng)制緩存的字段是 Cache-control 和 Expires。

          Expires

          這是 HTTP 1.0 的字段,表示緩存到期時間,是一個絕對的時間 (當(dāng)前時間+緩存時間),如

          Expires: Thu, 10 Nov 2017 08:45:11 GMT
          

          在響應(yīng)消息頭中,設(shè)置這個字段之后,就可以告訴瀏覽器,在未過期之前不需要再次請求。

          但是,這個字段設(shè)置時有兩個缺點(diǎn):

          1. 由于是絕對時間,用戶可能會將客戶端本地的時間進(jìn)行修改,而導(dǎo)致瀏覽器判斷緩存失效,重新請求該資源。此外,即使不考慮自信修改,時差或者誤差等因素也可能造成客戶端與服務(wù)端的時間不一致,致使緩存失效。
          2. 寫法太復(fù)雜了。表示時間的字符串多個空格,少個字母,都會導(dǎo)致非法屬性從而設(shè)置失效。

          Cache-control

          已知Expires的缺點(diǎn)之后,在HTTP/1.1中,增加了一個字段Cache-control,該字段表示資源緩存的最大有效時間,在該時間內(nèi),客戶端不需要向服務(wù)器發(fā)送請求

          這兩者的區(qū)別就是前者是絕對時間,而后者是相對時間。如下:

          Cache-control: max-age=2592000
          

          下面列舉一些 Cache-control 字段常用的值:(完整的列表可以查看 MDN)

          • max-age:即最大有效時間,在上面的例子中我們可以看到
          • must-revalidate:如果超過了 max-age 的時間,瀏覽器必須向服務(wù)器發(fā)送請求,驗(yàn)證資源是否還有效。
          • no-cache:雖然字面意思是“不要緩存”,但實(shí)際上還是要求客戶端緩存內(nèi)容的,只是是否使用這個內(nèi)容由后續(xù)的對比來決定。
          • no-store: 真正意義上的“不要緩存”。所有內(nèi)容都不走緩存,包括強(qiáng)制和對比。
          • public:所有的內(nèi)容都可以被緩存 (包括客戶端和代理服務(wù)器, 如 CDN)
          • private:所有的內(nèi)容只有客戶端才可以緩存,代理服務(wù)器不能緩存。默認(rèn)值。

          這些值可以混合使用,例如 Cache-control:public, max-age=2592000。在混合使用時,它們的優(yōu)先級如下圖:

          這里有一個疑問:max-age=0 和 no-cache 等價嗎?從規(guī)范的字面意思來說,max-age 到期是 應(yīng)該(SHOULD) 重新驗(yàn)證,而 no-cache 是 必須(MUST) 重新驗(yàn)證。但實(shí)際情況以瀏覽器實(shí)現(xiàn)為準(zhǔn),大部分情況他們倆的行為還是一致的。(如果是 max-age=0, must-revalidate 就和 no-cache 等價了)

          順帶一提,在 HTTP/1.1 之前,如果想使用 no-cache,通常是使用 Pragma 字段,如 Pragma: no-cache(這也是 Pragma 字段唯一的取值)。但是這個字段只是瀏覽器約定俗成的實(shí)現(xiàn),并沒有確切規(guī)范,因此缺乏可靠性。它應(yīng)該只作為一個兼容字段出現(xiàn),在當(dāng)前的網(wǎng)絡(luò)環(huán)境下其實(shí)用處已經(jīng)很小。

          總結(jié)一下,自從 HTTP/1.1 開始,Expires 逐漸被 Cache-control 取代。Cache-control 是一個相對時間,即使客戶端時間發(fā)生改變,相對時間也不會隨之改變,這樣可以保持服務(wù)器和客戶端的時間一致性。而且 Cache-control 的可配置性比較強(qiáng)大。

          Cache-control 的優(yōu)先級高于 Expires,為了兼容 HTTP/1.0 和 HTTP/1.1,實(shí)際項(xiàng)目中兩個字段我們都會設(shè)置。

          對比緩存 (也叫協(xié)商緩存)

          當(dāng)強(qiáng)制緩存失效(超過規(guī)定時間)時,就需要使用對比緩存,由服務(wù)器決定緩存內(nèi)容是否失效。

          流程上說,瀏覽器先請求緩存數(shù)據(jù)庫,返回一個緩存標(biāo)識。之后瀏覽器拿這個標(biāo)識和服務(wù)器通訊。如果緩存未失效,則返回 HTTP 狀態(tài)碼 304 表示繼續(xù)使用,于是客戶端繼續(xù)使用緩存;如果失效,則返回新的數(shù)據(jù)和緩存規(guī)則,瀏覽器響應(yīng)數(shù)據(jù)后,再把規(guī)則寫入到緩存數(shù)據(jù)庫。

          對比緩存在請求數(shù)上和沒有緩存是一致的,但如果是 304 的話,返回的僅僅是一個狀態(tài)碼而已,并沒有實(shí)際的文件內(nèi)容,因此 在響應(yīng)體體積上的節(jié)省是它的優(yōu)化點(diǎn)。它的優(yōu)化覆蓋了文章開頭提到過的請求數(shù)據(jù)的三個步驟中的最后一個:“響應(yīng)”。通過減少響應(yīng)體體積,來縮短網(wǎng)絡(luò)傳輸時間。所以和強(qiáng)制緩存相比提升幅度較小,但總比沒有緩存好。

          對比緩存是可以和強(qiáng)制緩存一起使用的,作為在強(qiáng)制緩存失效后的一種后備方案。實(shí)際項(xiàng)目中他們也的確經(jīng)常一同出現(xiàn)。

          對比緩存有 2 組字段(不是兩個):

          Last-Modified & If-Modified-Since

          1. 服務(wù)器通過 Last-Modified 字段告知客戶端,資源最后一次被修改的時間,例如
          Last-Modified: Mon, 10 Nov 2018 09:10:11 GMT
          
          1. 瀏覽器將這個值和內(nèi)容一起記錄在緩存數(shù)據(jù)庫中。
          2. 下一次請求相同資源時時,瀏覽器從自己的緩存中找出“不確定是否過期的”緩存。因此在請求頭中將上次的 Last-Modified 的值寫入到請求頭的 If-Modified-Since 字段
          3. 服務(wù)器會將 If-Modified-Since 的值與 Last-Modified 字段進(jìn)行對比。如果相等,則表示未修改,響應(yīng) 304;反之,則表示修改了,響應(yīng) 200 狀態(tài)碼,并返回數(shù)據(jù)。

          但是他還是有一定缺陷的:

          • 如果資源更新的速度是秒以下單位,那么該緩存是不能被使用的,因?yàn)樗臅r間單位最低是秒。
          • 如果文件是通過服務(wù)器動態(tài)生成的,那么該方法的更新時間永遠(yuǎn)是生成的時間,盡管文件可能沒有變化,所以起不到緩存的作用。

          Etag & If-None-Match

          為了解決上述問題,出現(xiàn)了一組新的字段 Etag 和 If-None-Match

          Etag 存儲的是文件的特殊標(biāo)識(一般都是 hash 生成的),服務(wù)器存儲著文件的 Etag 字段。之后的流程和 Last-Modified 一致,只是 Last-Modified 字段和它所表示的更新時間改變成了 Etag 字段和它所表示的文件 hash,把 If-Modified-Since 變成了 If-None-Match。服務(wù)器同樣進(jìn)行比較,命中返回 304, 不命中返回新資源和 200。

          Etag 的優(yōu)先級高于 Last-Modified

          緩存小結(jié)

          當(dāng)瀏覽器要請求資源時

          1. 調(diào)用 Service Worker 的 fetch 事件響應(yīng)
          2. 查看 memory cache
          3. 查看 disk cache。這里又細(xì)分:
          4. 如果有強(qiáng)制緩存且未失效,則使用強(qiáng)制緩存,不請求服務(wù)器。這時的狀態(tài)碼全部是 200
          5. 如果有強(qiáng)制緩存但已失效,使用對比緩存,比較后確定 304 還是 200
          6. 發(fā)送網(wǎng)絡(luò)請求,等待網(wǎng)絡(luò)響應(yīng)
          7. 把響應(yīng)內(nèi)容存入 disk cache (如果 HTTP 頭信息配置可以存的話)
          8. 把響應(yīng)內(nèi)容 的引用 存入 memory cache (無視 HTTP 頭信息的配置)
          9. 把響應(yīng)內(nèi)容存入 Service Worker 的 Cache Storage (如果 Service Worker 的腳本調(diào)用了 cache.put())

          一些案例

          光看原理不免枯燥。我們編寫一些簡單的網(wǎng)頁,通過案例來深刻理解上面的那些原理。

          1. memory cache & disk cache

          我們寫一個簡單的 index.html,然后引用 3 種資源,分別是 index.js, index.css 和 mashroom.jpg。

          我們給這三種資源都設(shè)置上 Cache-control: max-age=86400,表示強(qiáng)制緩存 24 小時。以下截圖全部使用 Chrome 的隱身模式。

          1、首次請求

          毫無意外的全部走網(wǎng)絡(luò)請求,因?yàn)槭裁淳彺娑歼€沒有。

          2、再次請求 (F5)

          第二次請求,三個請求都來自 memory cache。因?yàn)槲覀儧]有關(guān)閉 TAB,所以瀏覽器把緩存的應(yīng)用加到了 memory cache。(耗時 0ms,也就是 1ms 以內(nèi))

          3、關(guān)閉 TAB,打開新 TAB 并再次請求

          因?yàn)殛P(guān)閉了 TAB,memory cache 也隨之清空。但是 disk cache 是持久的,于是所有資源來自 disk cache。(大約耗時 3ms,因?yàn)槲募悬c(diǎn)小)

          2. no-cache & no-store

          我們在 index.html 里面一些代碼,完成兩個目標(biāo):

          • 每種資源都(同步)請求兩次
          • 增加腳本異步請求圖片

          1、當(dāng)把服務(wù)器響應(yīng)設(shè)置為 Cache-Control: no-cache 時,我們發(fā)現(xiàn)打開頁面之后,三種資源都只被請求 1 次。

          這說明兩個問題:

          • 同步請求方面,瀏覽器會自動把當(dāng)次 HTML 中的資源存入到緩存 (memory cache),這樣碰到相同 src 的圖片就會自動讀取緩存(但不會在 Network 中顯示出來)
          • 異步請求方面,瀏覽器同樣是不發(fā)請求而直接讀取緩存返回。但同樣不會在 Network 中顯示。

          總體來說,如上面原理所述,no-cache 從語義上表示下次請求不要直接使用緩存而需要比對,并不對本次請求進(jìn)行限制。因此瀏覽器在處理當(dāng)前頁面時,可以放心使用緩存。

          2、當(dāng)把服務(wù)器響應(yīng)設(shè)置為 Cache-Control: no-store 時,情況發(fā)生了變化,三種資源都被請求了 2 次。而圖片因?yàn)檫€多一次異步請求,總計 3 次。(紅框中的都是那一次異步請求)

          這同樣說明:

          • 如之前原理所述,雖然 memory cache 是無視 HTTP 頭信息的,但是 no-store 是特別的。在這個設(shè)置下,memory cache 也不得不每次都請求資源。
          • 異步請求和同步遵循相同的規(guī)則,在 no-store 情況下,依然是每次都發(fā)送請求,不進(jìn)行任何緩存。

          3、Service Worker & memory (disk) cache

          我們嘗試把 Service Worker 也加入進(jìn)去。我們編寫一個 serviceWorker.js,并編寫如下內(nèi)容:(主要是預(yù)緩存 3 個資源,并在實(shí)際請求時匹配緩存并返回)

          注冊 SW 的代碼這里就不贅述了。此外我們還給服務(wù)器設(shè)置 Cache-Control: max-age=86400 來開啟 disk cache。我們的目的是看看兩者的優(yōu)先級。

          1、當(dāng)我們首次訪問時,會看到常規(guī)請求之外,瀏覽器(確切地說是 Service Worker)額外發(fā)出了 3 個請求。這來自預(yù)緩存的代碼。

          2、第二次訪問(無論關(guān)閉 TAB 重新打開,還是直接按 F5 刷新)都能看到所有的請求標(biāo)記為 from SerciceWorker。

          from ServiceWorker 只表示請求通過了 Service Worker,至于到底是命中了緩存,還是繼續(xù) fetch() 方法光看這一條記錄其實(shí)無從知曉。因此我們還得配合后續(xù)的 Network 記錄來看。因?yàn)橹鬀]有額外的請求了,因此判定是命中了緩存。

          從服務(wù)器的日志也能很明顯地看到,3 個資源都沒有被重新請求,即命中了 Service Worker 內(nèi)部的緩存。

          3、如果修改 serviceWorker.js 的 fetch 事件監(jiān)聽代碼,改為如下:

          // 這個也叫做 NetworkOnly 的緩存策略。
          self.addEventListener('fetch', e => {
           return e.respondWith(fetch(e.request))
          })
          

          可以發(fā)現(xiàn)在后續(xù)訪問時的效果和修改前是 完全一致的。(即 Network 僅有標(biāo)記為 from ServiceWorker 的幾個請求,而服務(wù)器也不打印 3 個資源的訪問日志)

          很明顯 Service Worker 這層并沒有去讀取自己的緩存,而是直接使用 fetch() 進(jìn)行請求。所以此時其實(shí)是 Cache-Control: max-age=86400 的設(shè)置起了作用,也就是 memory/disk cache。但具體是 memory 還是 disk 這個只有瀏覽器自己知道了,因?yàn)樗]有顯式的告訴我們。(個人猜測是 memory,因?yàn)椴徽搹暮臅r 0ms 還是從不關(guān)閉 TAB 來看,都更像是 memory cache)

          瀏覽器的行為

          所謂瀏覽器的行為,指的就是用戶在瀏覽器如何操作時,會觸發(fā)怎樣的緩存策略。主要有 3 種:

          • 打開網(wǎng)頁,地址欄輸入地址: 查找 disk cache 中是否有匹配。如有則使用;如沒有則發(fā)送網(wǎng)絡(luò)請求。
          • 普通刷新 (F5):因?yàn)?TAB 并沒有關(guān)閉,因此 memory cache 是可用的,會被優(yōu)先使用(如果匹配的話)。其次才是 disk cache。
          • 強(qiáng)制刷新 (Ctrl + F5):瀏覽器不使用緩存,因此發(fā)送的請求頭部均帶有 Cache-control: no-cache(為了兼容,還帶了 Pragma: no-cache)。服務(wù)器直接返回 200 和最新內(nèi)容。

          緩存的應(yīng)用模式

          了解了緩存的原理,我們可能更加關(guān)心如何在實(shí)際項(xiàng)目中使用它們,才能更好的讓用戶縮短加載時間,節(jié)約流量等。這里有幾個常用的模式,供大家參考

          模式 1:不常變化的資源

          Cache-Control: max-age=31536000
          

          通常在處理這類資源資源時,給它們的 Cache-Control 配置一個很大的 max-age=31536000 (一年),這樣瀏覽器之后請求相同的 URL 會命中強(qiáng)制緩存。而為了解決更新的問題,就需要在文件名(或者路徑)中添加 hash, 版本號等動態(tài)字符,之后更改動態(tài)字符,達(dá)到更改引用 URL 的目的,從而讓之前的強(qiáng)制緩存失效 (其實(shí)并未立即失效,只是不再使用了而已)。

          在線提供的類庫 (如 jquery-3.3.1.min.js, lodash.min.js 等) 均采用這個模式。如果配置中還增加 public 的話,CDN 也可以緩存起來,效果拔群。

          這個模式的一個變體是在引用 URL 后面添加參數(shù) (例如 ?v=xxx 或者 ?_=xxx),這樣就不必在文件名或者路徑中包含動態(tài)參數(shù),滿足某些完美主義者的喜好。在項(xiàng)目每次構(gòu)建時,更新額外的參數(shù) (例如設(shè)置為構(gòu)建時的當(dāng)前時間),則能保證每次構(gòu)建后總能讓瀏覽器請求最新的內(nèi)容。

          特別注意: 在處理 Service Worker 時,對待 sw-register.js(注冊 Service Worker) 和 serviceWorker.js (Service Worker 本身) 需要格外的謹(jǐn)慎。如果這兩個文件也使用這種模式,你必須多多考慮日后可能的更新及對策。

          模式 2:經(jīng)常變化的資源

          Cache-Control: no-cache
          

          這里的資源不單單指靜態(tài)資源,也可能是網(wǎng)頁資源,例如博客文章。這類資源的特點(diǎn)是:URL 不能變化,但內(nèi)容可以(且經(jīng)常)變化。我們可以設(shè)置 Cache-Control: no-cache 來迫使瀏覽器每次請求都必須找服務(wù)器驗(yàn)證資源是否有效。

          既然提到了驗(yàn)證,就必須 ETag 或者 Last-Modified 出場。這些字段都會由專門處理靜態(tài)資源的常用類庫(例如 koa-static)自動添加,無需開發(fā)者過多關(guān)心。

          也正如上文中提到協(xié)商緩存那樣,這種模式下,節(jié)省的并不是請求數(shù),而是請求體的大小。所以它的優(yōu)化效果不如模式 1 來的顯著。

          模式 3:非常危險的模式 1 和 2 的結(jié)合 (反例)

          Cache-Control: max-age=600, must-revalidate
          

          不知道是否有開發(fā)者從模式 1 和 2 獲得一些啟發(fā):模式 2 中,設(shè)置了 no-cache,相當(dāng)于 max-age=0, must-revalidate。我的應(yīng)用時效性沒有那么強(qiáng),但又不想做過于長久的強(qiáng)制緩存,我能不能配置例如 max-age=600, must-revalidate 這樣折中的設(shè)置呢?

          表面上看這很美好:資源可以緩存 10 分鐘,10 分鐘內(nèi)讀取緩存,10 分鐘后和服務(wù)器進(jìn)行一次驗(yàn)證,集兩種模式之大成,但實(shí)際線上暗存風(fēng)險。因?yàn)樯厦嫣徇^,瀏覽器的緩存有自動清理機(jī)制,開發(fā)者并不能控制。

          舉個例子:當(dāng)我們有 3 種資源: index.html, index.js, index.css。我們對這 3 者進(jìn)行上述配置之后,假設(shè)在某次訪問時,index.js 已經(jīng)被緩存清理而不存在,但 index.html, index.css 仍然存在于緩存中。這時候?yàn)g覽器會向服務(wù)器請求新的 index.js,然后配上老的 index.html, index.css 展現(xiàn)給用戶。這其中的風(fēng)險顯而易見:不同版本的資源組合在一起,報錯是極有可能的結(jié)局。

          除了自動清理引發(fā)問題,不同資源的請求時間不同也能導(dǎo)致問題。例如 A 頁面請求的是 A.js 和 all.css,而 B 頁面是 B.js 和 all.css。如果我們以 A -> B 的順序訪問頁面,勢必導(dǎo)致 all.css 的緩存時間早于 B.js。那么以后訪問 B 頁面就同樣存在資源版本失配的隱患。

          有開發(fā)者朋友(wd2010)在知乎的評論區(qū)提了一個很好的問題:

          如果我不使用must-revalidate,只是Cache-Control: max-age=600,瀏覽器緩存的自動清理機(jī)制就不會執(zhí)行么?如果瀏覽器緩存的自動清理機(jī)制執(zhí)行的話那后續(xù)的index.js被清掉的所引發(fā)的情況都是一樣的呀!

          這個問題涉及幾個小點(diǎn),需要補(bǔ)充說明一下:

          1、'max-age=600' 和 'max-age=600,must-revalidate' 有什么區(qū)別?

          沒有區(qū)別。在列出 max-age 了之后,must-revalidate 是否列出效果相同,瀏覽器都會在超過 max-age 之后進(jìn)行校驗(yàn),驗(yàn)證緩存是否可用。

          在 HTTP 的規(guī)范中,只闡述了 must-revalidate 的作用,卻沒有闡述不列出 must-revalidate 時,瀏覽器應(yīng)該如何解決緩存過期的問題,因此這其實(shí)是瀏覽器實(shí)現(xiàn)時的自主決策。(可能有少數(shù)瀏覽器選擇在源站點(diǎn)無法訪問時繼續(xù)使用過期緩存,但這取決于瀏覽器自身)

          2、那 'max-age=600' 是不是也會引發(fā)問題?

          是的。問題的出現(xiàn)和是否列出 'must-revalidate' 無關(guān),依然會存在 JS CSS等文件版本失配的問題。因此常規(guī)的網(wǎng)站在不同頁面需要使用不同的 JS CSS 文件時,如果要使用 max-age 做強(qiáng)緩存,不要設(shè)置一個太短的時間。

          3、那這類比較短的 max-age 到底能用在哪里呢?

          既然版本存在失配的問題,那么要避開這個問題,就有兩種方法。

          a、整站都使用相同的 JS 和 CSS,即合并后的文件。這個比較適合小型站點(diǎn),否則可能過于冗余,影響性能。(不過可能還是會因?yàn)闉g覽器自身的清理策略被清理,依然有隱患)

          b、資源是獨(dú)立使用的,并不需要和其他文件配合生效。例如 RSS 就歸在此類。

          總結(jié)

          這篇文章有點(diǎn),不過如果大伙如果能靜下心來看完并理解,相信你對前端緩存一定能更上一層樓。


          eb存儲機(jī)制

          storage類型介紹

          storage類型是來存儲名值對兒,是保存在瀏覽器端的,存儲為key-value形式的。

          1. key:作為存儲數(shù)據(jù)的標(biāo)識(唯一且不可重復(fù))
          2. value:存儲數(shù)據(jù)的內(nèi)容(number/string)

          該類型的實(shí)例對象有關(guān)于存儲的方法如下:

          (1) clear():刪除所有值。

          (2) getItem():根據(jù)指定的名字(key)獲取對應(yīng)的值。

          (3) key(index):獲取index位置處的值的名字(key)。

          (4) removeItem():刪除由name指定的名值對兒。

          (5) setItem(name,value): 為指定的name 設(shè)置一個對應(yīng)的值。

          (6) Storage.length :返回一個整數(shù),表示存儲在 Storage 對象中的數(shù)據(jù)項(xiàng)數(shù)量。這個是 Storage 對象的一個屬性,而且是一個 只讀 屬性

          我自己是一名從事了多年開發(fā)的web前端老程序員,目前辭職在做自己的web前端私人定制課程,今年我花了一個月整理了一份最適合2020年學(xué)習(xí)的web前端學(xué)習(xí)干貨,各種框架都有整理,送給每一位前端小伙伴,想要獲取的可以關(guān)注我的頭條號并在后臺私信我:前端,即可免費(fèi)獲取。

          storage特性

          該類型只能存儲字符串,非字符串的數(shù)據(jù)會在存儲之前轉(zhuǎn)換成字符串。

          sessionStorage對象

          sessionStorage對象存儲是web stroage機(jī)制的一種,該對象會給每一個給定的源維持一個獨(dú)立的存儲區(qū)域,該區(qū)域在頁面會話期間可用(即只要瀏覽器處于打開狀態(tài),包括頁面重新加載和恢復(fù)) sessionStorage對象是綁定在服務(wù)器會話的,所以當(dāng)文件在本地運(yùn)行的時候是不能跨頁面訪問的,使用服務(wù)器地址訪問時才能多頁面訪問。


          在另一個頁面打開是找不到這個值的:


          開啟服務(wù)器訪問:



          再次使用服務(wù)器地址訪問的時候:



          localStorage對象

          localStorage也是web Stroage存儲機(jī)制的一種,localStorage對象是根據(jù)glocalStorage對象設(shè)計出來的簡化,glocalStorage的使用是要指定哪些域名可以訪問該數(shù)據(jù),是通過方括號標(biāo)記使用該屬性來實(shí)現(xiàn)的:

          //存數(shù)據(jù)
          glocalStorage["baidu.com"].name="劉德華"
          //取數(shù)據(jù)
          var name=glocalStorage["baidu.com"].name
          

          聽網(wǎng)上說是為了安全性然后在HTML5的規(guī)范中用localStorage取代了glocalStorage不能自己設(shè)置域名,值使用localStorage對象時就將當(dāng)前域名加入。規(guī)則設(shè)計出來就是方便我們的使用的,顯然localStorage比之前的使用起來更簡單了。

          我自己的理解根據(jù)該用戶不刪除數(shù)據(jù),數(shù)據(jù)將會一直保存在磁盤里的屬性,可以設(shè)置收藏列表,用戶愛好等等。

          //存數(shù)據(jù)
          glocalStorage.setItem('content', '劉德華');
          //取數(shù)據(jù)
          glocalStorage.getItem('content');

          兩者的比較

          不同點(diǎn)

          數(shù)據(jù)存儲時長

          sessionStorage:數(shù)據(jù)只保持在瀏覽器關(guān)閉

          localStorage:用戶不刪除數(shù)據(jù),數(shù)據(jù)將會一直保存在磁盤里

          訪問規(guī)則不同

          localStorage:只要在相同的協(xié)議、相同的主機(jī)名、相同的端口下,就能讀取和修改到同一份 localStorage 存儲的數(shù)據(jù)。

          sessionStorage:除了協(xié)議、主機(jī)名、端口外,還要求在同一窗口下。

          相同點(diǎn)

          1. 存儲大小都不能超過5M。

          Http緩存

          Last-Modified與if-Modified-Since

          Last-Modified:響應(yīng)頭,資源最新修改時間,由服務(wù)器告訴瀏覽器。 if-Modified-Since:請求頭,資源最新修改時間,由瀏覽器告訴服務(wù)器(其實(shí)就是上次服務(wù)器給的Last-Modified,請求又還給服務(wù)器對比),和Last-Modified是一對,它兩會在服務(wù)器端進(jìn)行對比。

          請求過程

          第一次請求數(shù)據(jù)

          瀏覽器:服務(wù)器服務(wù)器,我現(xiàn)在需要一個users.json的文件,你找到了給我,順便給我文件修改時間!

          服務(wù)器:行,文件修改時間我給你,到時候文件過期了咱兩核對文件修改時間,對得上我就不找了,返回a.txt+Last-Modified。

          第二次請求數(shù)據(jù) 瀏覽器:服務(wù)器服務(wù)器,我還需要一個users.json的文件,我把這個文件上次修改時間發(fā)你if-Modified-Since,你對比一下文件最近有不有修改!

          服務(wù)器:ok,我?guī)湍阏乙幌?。服?wù)器將if-Modified-Since與Last-Modified做了個對比。

          if-Modified-Since 與Last-Modified不相等,服務(wù)器查找了最新的users.json,同時再次返回全新的Last-Modified。

          if-Modified-Since 與Last-Modified相等,服務(wù)器返回狀態(tài)碼304,文件沒修改過,你還是用你的本地緩存。

          Etag與If-None-Match

          請求過程

          Etag:響應(yīng)頭,由服務(wù)器設(shè)置告訴瀏覽器。

          if-None-Match:請求頭,由瀏覽器告訴服務(wù)器(其實(shí)就是上次服務(wù)器給的Etag),和Etag是一對,它兩也會在服務(wù)器端進(jìn)行對比。

          瀏覽器:服務(wù)器服務(wù)器,我現(xiàn)在需要一個users.json的文件,你找到了給我!

          服務(wù)器:你又來了,文件我找給你,我給你一個Etag(文件版本號),到時候你再來找這個資源的時候把這個帶上,省的我再找給你,返回a.txt+Etag。

          第二次請求數(shù)據(jù) 瀏覽器:服務(wù)器服務(wù)器,我又需要users.json文件了,我把你發(fā)給我的Etag放在If-None-Match里面了,你對比一下最近這個文件有不有修改!

          服務(wù)器:ok,我?guī)湍阏乙幌?。服?wù)器將Etag與If-None-Match做了個對比。 Etag與If-None-Match不相等,服務(wù)器查找了最新的users.json,同時再次返回全新的Etag(文件版本號)。

          Etag與If-None-Match相等,服務(wù)器返回狀態(tài)碼304,文件沒修改過,你還是用你的本地緩存。

          Http頭部緩存和web緩存例子

          模擬前端請求顯示數(shù)據(jù)

           <ul id="users"></ul>
              <script>
                  window.onload = function() {
                      const users = JSON.parse(localStorage.getItem('usersdata'));
                      console.log("users", users)
                      const uluser = document.querySelector('#users');
                      if (users) {
                          uluser.innerHTML = users.map(user =>
                                  `
                              <li>
                                  ${user.username}-${user.place}
                              </li>
                              `
                              )
                      }
                      fetch('/users')
                          .then(data => data.json())
                          .then(data => {
                              localStorage.setItem('usersdata', JSON.stringify(data)) //需要存儲字符串
                              console.log("users請求")
                              uluser.innerHTML = data.map(user =>
                                  `
                                  <li>
                                      ${user.username}-${user.place}
                                  </li>
                                  `
                              )
                          })
                  }
              </script>

          模擬服務(wù)器接收與發(fā)送

          const http = require('http');
          const fs = require('fs');
          const users = require('./users.json')
          http.createServer((req, res) => {
              let status = fs.statSync('users.json');//獲取文件的狀態(tài)
              if (req.url == '/') {
                  res.writeHead(200, { "Content-Type": "text/html;charset=utf8" })
                   fs.createReadStream('./users.html')pipe(res);//通過流返回顯示頁面
              } else if (req.url == '/users') {
                  console.log("用戶數(shù)據(jù)請求")
                  if (req.headers['if-modified-since']) {
                      if (req.headers['if-modified-since'] == status.mtime) {//判斷請求中的修改時間與文件的修改時間是否相同
                          console.log("未修改文件返回304")
                          res.writeHead(304, { "Content-Type": "text/html;charset=utf8" });//304 Not Modified
                          res.end();
                          return;
                      } else {
                          res.setHeader('Last-Modified', status.mtime);
                          res.writeHead(200, { "Content-Type": "text/html;charset=utf8" });
                          console.log('修改了返回修改數(shù)據(jù)')
                          res.end(JSON.stringify(users))
                          return;
                      }
                  }
                  console.log("用戶數(shù)據(jù)第一次")
                  res.setHeader('Last-Modified', status.mtime);
                  res.writeHead(200, { "Content-Type": "text/html;charset=utf8" });
                  res.end(JSON.stringify(users))
              } else {
                  res.end("null")
              }
          }).listen('8088', () => {
              console.log('監(jiān)聽8088')
          })

          結(jié)果分析

          第一次請求數(shù)據(jù)服務(wù)器給我們返回200狀態(tài)碼和Last-Modified,代碼中提示的地方提示出用戶為第一次數(shù)據(jù)請求。



          文件未修改,第二次請求數(shù)據(jù)服務(wù)端判斷出文件未修改,則返回304狀態(tài)碼官方解釋304:The HTTP 304 Not Modified client redirection response code indicates that there is no need to retransmit the requested resources.



          文件修改,再次訪問,服務(wù)端檢測文件修改時間不一樣,則重新返回文件內(nèi)容




          結(jié)尾

          緩存數(shù)據(jù)可以節(jié)省許多資源,對于以上例子還有些缺陷,對于高頻訪問情況還是不能滿足需求,技術(shù)就是需要不斷的探索。有什么建議或者錯誤,歡迎指出!


          作者:有酒有故事
          鏈接:https://juejin.im/post/6844904164540022797


          主站蜘蛛池模板: 一区二区三区免费在线视频 | 国模无码一区二区三区| 久久精品国产一区| 日韩精品国产一区| 日本免费一区二区在线观看| 精品日本一区二区三区在线观看| 国产一区二区三区小说| 国产丝袜视频一区二区三区| 美女视频黄a视频全免费网站一区| 99久久精品日本一区二区免费| 国产一区二区精品| 国产午夜精品一区二区三区小说| 国产一区韩国女主播| 亚洲性无码一区二区三区| 性色av一区二区三区夜夜嗨| 亚洲国产综合无码一区| 亚洲福利视频一区二区| 国产成人精品a视频一区| 亚洲AV成人精品日韩一区18p| 国产成人精品一区二三区| 国产伦精品一区二区三区精品 | 午夜精品一区二区三区免费视频| 日本高清无卡码一区二区久久| 波多野结衣中文字幕一区| 在线播放精品一区二区啪视频| 午夜一区二区在线观看| 国模大胆一区二区三区| 国产精品免费视频一区| 日韩精品人妻av一区二区三区| 免费人妻精品一区二区三区| 亚洲性日韩精品国产一区二区| 一区二区三区影院| 亚洲欧美日韩国产精品一区| 亚洲AV日韩综合一区尤物| 精品人妻少妇一区二区三区不卡| 欧美日韩综合一区二区三区| 国产伦精品一区二区三区免费下载| 国产精品资源一区二区| 一区二区三区免费电影| 国产精品亚洲一区二区三区在线| 青青青国产精品一区二区|