深刻node.js-瀏覽器緩存機制

瀏覽器緩存

瀏覽器緩存(Browser Caching)是爲了節約網絡的資源加速瀏覽,瀏覽器在用戶磁盤上對最近請求過的文檔進行存儲,當訪問者再次請求這個頁面時,瀏覽器就能夠從本地磁盤顯示文檔,這樣就能夠加速頁面的閱覽。

瀏覽器緩存的使用是提升用戶體驗的一個重要途徑,一般也是優化前端的一種重要方式。利用好了緩存能夠加快頁面的瀏覽,下降服務器的壓力,減小網絡損耗等功能。圖片描述前端

瀏覽器緩存分類

  • 協商緩存
  • 強制緩存

協商緩存

經過上圖分析:node

  • 客戶端向服務器請求資源
  • 驗證標識,若是標識經過了驗證,則會響應304,告知瀏覽器讀取緩存
  • 若是沒有標識,或驗證沒有經過,則返回請求的資源

看到這裏可能有人會有問題,標識是什麼?
標識主要是用來標識請求的資源是否被修改或更新過,經過請求頭髮送給服務器進行驗證。瀏覽器

協商緩存的標識有兩種:緩存

  • ETag
  • Last-Modified

下面咱們來說講這二者的區別以及用法服務器

Last-Modified

last-modified 根據詞義就能夠知道表示該資源的最後修改時間。微信

  • 客戶端第一次請求服務器,服務器會把該資源的最後修改時間經過響應頭返回給客戶端
  • 客戶端再次請求服務器的時候,若是在響應頭中有Last-Modified字段,瀏覽器就會在請求頭中加上if-Modified-Since字段給服務器。
  • 服務器拿到該字段的值,與該資源的最後修改時間進行對比,若是相等則說明資源沒有被修改,向客戶端返回304。
  • 瀏覽器看到304就會去讀取緩存信息並呈現。

下面根據以上的幾個點,來看看代碼怎麼實現:網絡

const http = require('http');
    const url = require('url');
    const path = require('path');
    const fs = require('fs');
    const mime = require('mime');
    
    const server = http.createServer((req, res) => {
      // 獲取文件名
      const { pathname } = url.parse(req.url, true);
      // 獲取文件路徑
      const filepath = path.join(__dirname, pathname);
    
      /**
       * 判斷文件是否存在
       */
      fs.stat(filepath, (err, stat) => {
        if (err) {
          res.end('not found');
        } else {
          // 獲取if-modified-since這個請求頭
          const ifModifiedSince = req.headers['if-modified-since'];
          // 獲取資源最後修改時間
          let lastModified = stat.ctime.toGMTString();
          // 驗證資源是否被修改過,若是相同則返回304讓瀏覽器讀取緩存
          if (ifModifiedSince === lastModified) {
            res.writeHead(304);
            res.end();
          }
          // 緩存沒有經過則返回資源,並加上 last-modified響應頭,下次瀏覽器就會在請求頭中帶着 if-modified-since
          else {
            res.setHeader('Content-Type', mime.getType(filepath));
            res.setHeader('Last-Modified', stat.ctime.toGMTString());
            fs.createReadStream(filepath).pipe(res);
          }
        }
      });
    });
    
    server.listen(8000, () => {
      console.log('listen to 8000 port');
    });

ETag

  • ETag它的流程和last-modified是同樣的,僅僅只是驗證方式不一樣,last-modified是取的當前請求資源的最後修改時間來做爲驗證,而ETag則是對當前請求的資源作一個惟一的標識。
  • 標識能夠是一個字符串,文件的size,hash等等,只要可以合理標識資源的惟一性並能驗證是否修改過就能夠了。好比讀取文件內容,將文件內容轉換成一個hash值,每次接收到客戶端發送過來的時候,從新讀取文件轉成hash值,與以前的作對比,看資源是否修改過。
  • 和Last-Modify相同,服務器在響應頭返回一個ETag字段,那麼請求的時候就會在請求頭中加入if-none-match

下面來看看代碼,代碼中我都會加入詳細的註釋:優化

const http = require('http');
const url = require('url');
const path = require('path');
const fs = require('fs');
const mime = require('mime');
const crypto = require('crypto');

const server = http.createServer(function(req, res) {
  // 獲取請求的資源名稱
  let { pathname } = url.parse(req.url, true);
  // 獲取文件路徑
  let filepath = path.join(__dirname, pathname);

  /**
   * 判斷文件是否存在
   */
  fs.stat(filepath, (err, stat) => {
    if (err) {
      return sendError(req, res);
    } else {
      let ifNoneMatch = req.headers['if-none-match'];
      let readStream = fs.createReadStream(filepath);
      let md5 = crypto.createHash('md5');

      // 經過流的方式讀取文件而且經過md5進行加密,至關於轉成一個hash字符串做爲etag的值
      readStream.on('data', function(data) {
        md5.update(data);
      });
      readStream.on('end', function() {
        let etag = md5.digest('hex');
        // 驗證etag,判斷資源是否被修改過,若是沒有則返回304
        if (ifNoneMatch === etag) {
          res.writeHead(304);
          res.end();
        } else {
          res.setHeader('Content-Type', mime.getType(filepath));
          // 第一次服務器返回的時候,會把文件的內容算出來一個標識,發給客戶端
          fs.readFile(filepath, (err, content) => {
            // 客戶端看到etag以後,也會把此標識保存在客戶端,下次再訪問服務器的時候,發給服務器
            res.setHeader('Etag', etag);
            fs.createReadStream(filepath).pipe(res);
          });
        }
      });
    }
  });
});
server.listen(8000, () => {
  console.log('listen to 8000 port');
});

強制緩存

圖片描述

經過上圖分析:ui

  • 強制緩存經過Cache-Control這個響應頭中的max-age:60(緩存60s)來判斷緩存是否過時
  • 若是過時了則從新向服務器請求資源
  • 若是沒有過時,則不通過服務器,直接讀取資源

強制緩存比較簡單,直接看一下代碼的實現加密

const http = require('http');
    const url = require('url');
    const path = require('path');
    const fs = require('fs');
    const mime = require('mime');
    
    const server = http.createServer(function(req, res) {
      let { pathname } = url.parse(req.url, true);
      let filepath = path.join(__dirname, pathname);
      fs.stat(filepath, (err, stat) => {
        if (err) {
          res.setHeader('Content-Type', mime.getType(filepath));
          // 設置緩存過時時間
          res.setHeader('Cache-Control', 'max-age=100');
          fs.createReadStream(filepath).pipe(res);
        } else {
          return send(req, res, filepath);
        }
      });
    });
    server.listen(8000, () => {
      console.log('listen to port 8000');
    });

強制緩存就是向瀏覽器設置一個過時時間例如cache-control:max-age=60表示這是一個60秒的過時時間,60秒之內瀏覽器都會從緩存讀取該資源,超過60秒則訪問服務器

cache-control還有另外幾個值能夠設置

  • private 客戶端能夠緩存
  • public 客戶端和代理服務器均可以緩存
  • max-age=60 緩存內容將在60秒後失效
  • no-cache 須要使用對比緩存驗證數據,強制向源服務器再次驗證
  • no-store 全部內容都不會緩存,強制緩存和對比緩存都不會觸發

總結

理解緩存對前端開發來講十分的重要,這也是爲什麼把這篇文章寫出來的緣由,後續會繼續爲你們帶來node相關的文章,若是寫錯或很差的地方但願你們指出來,若是以爲寫的還行,麻煩點個贊哈!

如下個人新我的微信公衆號,也會爲你們持續提供原創文章,歡迎你們關注,若是用戶量足夠,會在裏面爲你們提供一些項目類的視頻教程,謝謝
圖片描述

相關文章
相關標籤/搜索