前端面试:腾讯营304

668 阅读5分钟

1. 引言

今天刚刚学习到了有关网络状态码,了解到了一个腾讯前端的面试题,关于304及其实现。趁热打铁,写文章以记录。可供参考。

2. 304介绍

  • 大白话说:你第二次向网站请求的系列小电影并没有更新,网站返回它告诉你没有更新,只能继续回顾经典
  • 304 Not Modified:客户端有缓冲的文件并发出了一个条件性的请求(一般是提供If-Modified-Since头表示客户只想比指定日期更新的文档)。服务器告诉客户,原来缓冲的文档还可以继续使用。
  • 如果客户端在请求一个文件的时候,发现自己缓存的文件有 Last Modified(最近修改时间) ,那么在请求中会包含 If Modified Since ,这个时间就是缓存文件的 Last Modified 。因此,如果请求中包含 If Modified Since,就说明已经有缓存在客户端。只要判断这个时间和当前请求的文件的修改时间就可以确定是返回 304 还是 200 。对于静态文件,例如:CSS、图片,服务器会自动完成 Last Modified 和 If Modified Since 的比较,完成缓存或者更新。但是对于动态页面,就是动态产生的页面,往往没有包含 Last Modified 信息,这样浏览器、网关等都不会做缓存,也就是在每次请求的时候都完成一个 200 的请求。
  • 一般的大的站点的图片服务器都有实现HTTP 304 缓存功能。
  • 这个 304 状态一般主要在用户刷新页面(F5键)的时候触发,当用户在刷新页面的时候,因为原来的页面里的很多资源已经缓存过,客户端的浏览器已经记录了资源的最后更新时间(Last Mod),所以在用户刷新页面的时候,会向服务器提交一个字段:If-Modified-Since: Wed,
  • 这个时候,服务器端的程序先取得这个字段的值,然后与服务器上的图片最后修改时间对比,如果相同,就直接返回 304 Not Modified ,然后停止。 设计的原因:
  1. 一切缓存皆为优化,提升体验。减少不必要的资源传输可以提升页面加载速度,
  2. 减少资源传输达到节省带宽的目的。 第一次:

image.png

第二次: image.png

3. 场景设计 本文采用两种方式实现

(一)lastModified方式

  1. 运行环境:vs code+npm 7.0.8 +node 15.2.0
  2. 代码文件与static同级
  3. 根目录运行npm init -y初始化项目,npm i express安装expres服务框架。假设有一个静态资源demo.js在 src/static目录下
  • 基础服务配置
const express=require('express')//框架引入
const app=express()//设置服务对象
const fs=require('fs')//引入fs node.js的fs模块获取文件状态信息
const port=3000;//3000端口启动
const path=require('path')//引入path模块配置资源路径
const md5=require('md5')//md5加密方式完成304设计
  • 取资源和资源信息
    const jsPath = path.resolve(__dirname, './static/js/demo.js');
    //resolve方法将绝对路径和目标路径串接 并且解析成路径树,让机器理解
    
    let status = fs.statSync(jsPath);//statSync可以获取路径文件的各种信息
    console.log(status, '--------------');
    let lastModified = status.mtime.toUTCString();//status里的metime属性表示最后修改时间,
                                                 //用toUTCString来将世界时转换成字符串,便于比对
    let count = fs.readFileSync(jsPath);//同步读取路径里的文件内容
    console.log(lastModified,'+++++')

image.png

image.png

  • 比对修改字段,设置返回头,状态码
     if(lastModified==req.headers('if-modified-since')){//当前资源的修改时间和请求头的修改时间对比
       res.writeHead(304,'NO Modified')
       res.end();//为空 不需要有响应 。
       return;
   }
   
    res.setHeader('Cache-Control','public,max-age=30')//设置浏览器缓存,
                                    //public属性表示任何情况下都应该缓存该资源,设置了最长缓存时间
    res.setHeader('Last-Modified',lastModified)//响应头携带文件修改时间给到用户端
    res.writeHeader(200,'OK')//第一次请求成功状态码是200
    res.end(count)//返回文件内容

关于cache-control :blog.csdn.net/u012375924/…

  • 启动监听
    app.listen(port,()=>{
    console.log('listen ')
})

(二)md5 ETag方式

  • npm i md5 引入MD5加密
  • md5将文件内容编成32位的码,内容的改变会引起编码的改变
  • 在原代码基础上新增 Etag,Etag是 Entity tag的缩写,可以理解为“被请求变量的实体值”,Etag是服务端的一个资源的标识.通过If-None-Match请求头带上了之前服务端返回的Etag的值。服务端收到第二次请求的时候,发现携带了If-None-Match字段,就重新计算服务器对应资源的Etag,如果二者匹配了,就认为资源没有发生变化,直接给客户端相应304,让客户端读取缓存中的数据。
let etag=md5(count);

  if(req.header['if-none-match']==etag){
        res.setHeader('ETag',etag)
        res.writeHead(304,'NOt Modified')
        res.end()
        return;
    }

图中两个标志字段一致,状态码是304

image.png

image.png

关于Etag:www.cnblogs.com/happy4java/…

  • 源代码
const express=require('express')
const app=express()
const fs=require('fs')
const port=3000;
const path=require('path')
const md5=require('md5')
//请求demo.js
//路由是/demo.js
// fs把它读取并发送流
app.get('/', (req, res) => {
    res.send(`<!DOCTYPE html>
    <html lang="en">//字符串模板设计了网页
      <head>
      <title>Document</title>
      </head>
      <body>
      demo1
      <script src="/demo.js">//在src里请求资源 /demo.js
      </script>
      </body>
    </html>
    `)
  })
app.get('/demo.js',(req,res)=>{
    const jsPath = path.resolve(__dirname, './static/js/demo.js');
    let status = fs.statSync(jsPath);
    let lastModified = status.mtime.toUTCString();
    let count = fs.readFileSync(jsPath);
    let etag=md5(count);
    console.log(lastModified,'+++++')
    if(req.header['if-none-match']==etag){
        res.setHeader('ETag',etag)
        res.writeHead(304,'NOt Modified')
        res.end()
        return;
    }
      // if(lastModified==req.headers('if-modified-since')){
    //     res.writeHead(304,'NO Modified')
    //     res.end();//为空 不需要有响应 。
    //     return;
    // } lastModified方法
    res.setHeader('Cache-Control','public,max-age=30')
    res.setHeader('Last-Modified',lastModified)
    res.setHeader('ETag',etag)
    res.writeHeader(200,'OK')
    res.end(count)
})
app.listen(port,()=>{
    console.log('listen ')
})

小结

  • 状态码出现频率高,但数量多,需要挑重点,联系应用场景
  • 请求头和响应头的几个常考属性可以多加了解,设置的方法应多多联系
  • 网页启动一个node服务的流程要熟悉

*本人大三,正寻实习,与君共勉。寥有拙作,万望指正。*