• 浏览器缓存


    之前对于浏览器的缓存一直是一知半解,可能这块前端涉入较少,主要是由后端在控制。这两天看Nodejs,于是重点研究了下,在此做个总结。

    缓存必要性:

      浏览器打开一个网页,通常需要加载HTML、Javascript、CSS 等够成网页界面和逻辑的文件,加载这些东西是非常耗时的。而且这些内容在大部分情况下不会经常变更。因此浏览器提供了一种本地缓存机制,用户再次进入这个页面时,不需要再去向服务器端请求这些文件,而是直接从本地读取。这种机制减少了带宽浪费,同时也大大提高了用户体验。作为前端人员,研究下它的工作原理,还是很有必要的。

    缓存实现方案:

      方案一: If-Modified-Since 和 Last-Modified

        浏览器第一次打开某个网址,必然是会请求服务器端的内容,并将返回的内容缓存在本地。文件返回头部都会带有一个 Last-Modified 字段,意思是最近一次的修改时间。如下所示:

          Last-Modified:Wed, 18 Dec 2013 00:32:38 GMT

        当浏览器再次访问该网址时,它会对本地文件进行检查,如果不能确定这份文件是否可以直接使用(如Expires过期), 将会发送一份请求给服务端,并且在请求头上带上If-Modified-Since字段,字段值为该文件的Last-Modified值:

          If-Modified-Since:Wed, 18 Dec 2013 00:32:38 GMT

        请求发送到服务器后,服务器端根据 If-Modified-Since 值是和文件的Last-Modified 值判断文件是否有更新。如果没有更新,返回状态码304,不需要返回文件内容,浏览器直接使用本地文件。否则,则返回200,并将新的内容返回给浏览器。

    fs.stat(filename, function (err, stat) {
      var lastModified = stat.mtime.toUTCString();


      if(lastModified === req.headers['if-modified-since']) {
        res.writeHead(304, 'Not Modified');
        res.end()
      } else {
        fs.readFile(filename, function (err, file) {
          var lastModified = stat.mtime.toUTCString();
          res.setHeader('Last-Modified', lastModified);
          res.writeHead(200, 'OK');
          res.end();
        });
      }
    });

          <nodejs 实现>

      方案二: If-None-Match 和 ETag

        方案一采用的是时间戳的方式实现,但是时间戳会有一些缺陷存在:

          文件的时间戳改动但内容不一定改动

          时间戳只能精确到秒级别,频繁的内容更新将无法生效

        因此在HTTP 1.1 中引入了ETag。ETag 是由服务器端根据文件内容生成的一串散列值,散列值随着文件内容改变而改变,且不会存在上述的问题。判断原理和方案一差不多,只不过将判断文件的修改时间改成判断文件散列值是否相同。

    fs.readFile(filename, function (err, file) {
      var hash = getHash(file),
        nonMatch = req['if-none-match'];


      if(hash === nonMatch) {
        res.writeHead(304, 'Not Modified');
        res.end();
      } else {
        res.setHeader('ETag', hash);
        res.writeHead(200, 'OK');
        res.end();
      }
    });

        <nodejs 实现>

      方案三:设置 Expires

        方案一和方案二尽管在文件内容没有改动时可以节省带宽,但依然会消耗一个HTTP请求。想要浏览器在加载文件时不发送请求,而是直接去读本地文件,可以在请求返回头设置expires属性。

        expires 是一个GTM格式的时间字符串。服务器可以通过它设置文件在用户本地存储的过期时间。只要本地还存在这个缓存文件,在到期时间之前都不会再发送请求。如将Expires 设置为一个月以后:

          Expires:Sat, 15 Feb 2014 11:12:03 GMT

        在地址框中重新输入地址(注意不能按F5 或 Ctrl+R 刷新),查看chrome 控制台显示的请求信息如下:

        

        状态信息显示的是200 OK (from cache)。在firebug 中这个请求直接会不可见。

      方案四:设置Cache-Control

        Cache-Control 功能更强大,它有一个 max-age 值,意思是文档被访问后的存活时间,单位为秒。这个时间是个相对值,相对的是文档第一次被请求时服务器记录的请求时间。max-age 能够避免Expires 带来的浏览器和服务前端时间不同步的问题。不过  HTTP 1.0不支持max-age,而且如果浏览器中max-age 和Expires 同时存在,且都被支持,max-age 会覆盖Expires。Cache-Control 由后台设置,在请求的返回头信息中出现。如下设置文档在客户端存活时间为2592000秒。

        Cache-Control:max-age=2592000

    强制不读本地缓存:

      有些时候我们不希望浏览器去读去缓存数据,而是强制它去读去服务器端最新的数据。这种情况在ajax请求动态json数据的时候经常出现。解决方案如下:

      1)在URL search 部分加入随机数,如 http://example.com/data?123456

      2)设置请求头部的If-Modified-Since 属性为'0'。

    清除缓存:

      当版本号更新的时候,我们希望浏览器读取我们最新版本的文件,而不是旧版本的文件。这种情况,比较普遍的解决办法是在URL中加入版本号,让浏览器发起新的URL请求,避免读去到本地缓存或是CDN缓存内容。如下面URL, 版本号为1.0.1:

        http://static.lanhuedu.com/kutianshi/1.0.1/assets/css/common.css

     

    参考资料:《深入浅出Nodejs》

  • 相关阅读:
    LNMP
    Unable to guess the mime type as no guessers are available 2 9
    django--模型字段引用
    no python application found, check your startup logs for errors
    uWSGI+django+nginx的工作原理流程与部署历程
    进程管理supervisor的简单说明
    Django 部署(Nginx)
    MyBatis学习教程
    Spring教程
    互联网的寒冬该如何度过
  • 原文地址:https://www.cnblogs.com/beyondcheng/p/3523102.html
Copyright © 2020-2023  润新知