HTTP 为什么重要
- HTTP 是前后端合作的重要方式
99%的需求都是通过 HTTP 做到(如登录注册,增删改查,文件下载等),小部分需求可以通过 WebSockets 做到。 - HTTP 能帮你从本质上理解 HTML、CSS、JS、图片、JSON、JSONP 等不同形式的资源
- Web 性能优化基本等价于对 HTTP 传输效率优化
- 前端工程化需要你对 HTTP 缓存有深入了解
什么是 HTTP
四个概念
- server:JS运行在服务器上,可以监听80端口或者88端口的软件,可以接收响应,发送响应
- client:JS运行在客户端上
- request
- response
Unix和Linux系统中,80端口是无法被普通用户使用的,一般使用1024以上的端口,windows没有这个限制
用简单的工具了解这四个概念:
-
curl 作为 client(curl本质上跟浏览器没有区别)
- curl 网址:可以返回response
- curl -v 网址:会有请求和响应信息出现
server.js 作为 server
curl 发送一个 request 到 server.js
server.js 返回一个 response 给 curl
client
- 用户用来浏览网页的软件(GUI(图形界面) 或命令行均可)
- 后端开发者的爬虫
- 搜索引擎的爬虫
client 也可以叫做用户代理,因为它帮用户发送请求。
什么是代理?
「用户代理」的英文是 user agent,「代理」的英文是 proxy,所以并不完全一样。
通俗来说,proxy 是为了满足特定功能的 agent,这些功能包括加速访问、内容缓存、内容过滤、信息加密等;而 agent 则功能较广泛,如 Chrome 就是一个 user agent,能满足广泛的上网需求,curl 和爬虫也是 user agent,一般不能叫做 proxy。
server
server是用于提供响应的软件,一般来说一个硬件上面可以有多个软件服务器
你可以通过很多方式搭建一个 server
- Apache + PHP/Java/Python/Node.js
- Nginx + PHP/Java/Python/Node.js
- 只用 Node.js
正式搭建的时候使用前两种,最后那个比较脆弱
为了便于理解精髓,我们只用一个 server.js 就可以搭建一个 server,源码我已经写好了
我们用 curl 来向这个 server 发一个请求。
curl -v http://127.0.0.1
如果你不想用 IP,可以设置一下 hosts
127.0.0.1 wtf.com
curl -v http://wtf.com
请求
请求包括四部分
- 第一部分 - 动词 路径 协议/版本号
- 第二部分 - 一堆 key: value,用回车分割
- 第三部分 - 回车,作用只有一个:分隔开第二部分和第四部分
- 第四部分 - 随便什么内容都可以,内容的格式必须要第二部分里用 Content-Type 说明
内容格式Connection-Type: application/x-www-form-urlencoded
- x表示没有写在规范里,这个只是临时实验使用的,没有具体含义。
- www表示是万维网
- form代表表单格式
- urlencoded表示是encoded过的
- 形如xxx=yyy就是www-form-urlencoded的全部内容,多个值就是xxx=yyy&xxx2=yyy2这样,其中都是经过unicode化,是utf编码的16进制
- 使用key=value
- 多个key,value使用&链接
- 特殊字符转换成百分开头的文字,如&转成%26
一般来说get请求没有第四部分,这是get和post的一个区别
curl -v 可以查看请求
Chrome 开发者工具也可以
响应
响应包括四部分
- 第一部分 - 协议/版本号 状态码 状态信息
- 第二部分 - 一堆 key: value,用回车分割
- 第三部分 - 回车,作用只有一个:分隔开第二部分和第四部分
- 第四部分 - 随便什么内容都可以,内容的格式必须要第二部分里用 Content-Type 说明
响应出现乱码的原因可能是因为没有在Content-Type中说明,并设置编码就好了
状态码一般是200-500之间的20多个,需要记忆
curl -v 可以查看响应
Chrome 开发者工具也可以
《Http权威指南》
HTML、CSS、JS 的本质
本质就是字符串,只不过 Content-Type 不同。
注意 url 里的后缀是废话,毫无意义。
- 浏览器通过地址栏、iframe 来请求 HTML
- 浏览器通过 link 标签获取 CSS,然后渲染
- 浏览器通过 script 标签获取 JS,然后执行
- 浏览器通过 image 标签获取图片,然后展示
JSON 的本质
本质就是字符串,只不过 Content-Type 为 application/json。
注意 url 里的后缀是废话,毫无意义。
请求JSON的方式:
1. 通过JS请求JSON
请求每一种资源都需要使用特定的方法。
type和Content-type
使用JSON.parse可以将字符串变为对象
JSON是一个有格式的字符串,是一种语法
JSONP 的本质
本质就是字符串,只不过
- Content-Type 为application/javascript 或者 text/javascript
- 内容格式为 functionName( {"format": "JSON"} )
缓存
使用 Cache-Control 缓存是常用的缓存方式
-
想要更新缓存只需稍微变更一下 url
- app-2131312362387123.js
- app.js?v=1
- app.js?t=201801012334
- 不要缓存入口页面,这是你更新资源的唯一入口
-
前端性能优化的一个切入点就是缓存:response.setHeader('Cache-Control', 'max-age=3600')
我们可以看到当我们在server.js那边设置了cache-control,在后面的前端响应那边会带上这个信息
-
当我们再次请求这个style的时候,status的200变成灰色,size变成了(from disk cache),这个的意思就是没有去读服务器,而是从本地浏览器的缓存中读取,我们可以看到本地文件的时间没有很大差异,但是当在远程服务器上面的时候,时间差异会很大。其他没有这种cache-control设置的就得从服务器下载。我们看服务器在二次刷新的时候没有发送style文件
缓存其实很简单,就在server.js上加了一句代码。所有的网站都是这种效果
在浏览器的开发者工具里面,点击disable cache就可以不使用之前的缓存。缓存在网络快的情况下,效果非常显著。
设置缓存的时间,时间过了之后才会再次请求服务器。
-
如何强制抛弃之前的缓存?可以更改之前请求的url,如在url后面写一个随机数,当这个路径跟之前的路径不一样,就会重新请求
-
我们发现有的时候缓存放在内存(memory)中,有的是放在硬盘(disk)中的。具体浏览器缓存到哪里是无法控制的
实际过程中,百度图片默认缓存10年,意味着10年内不需要更新,在这个过程中,如果需要变化,只需要更改图片的后缀就好了,请求的url变化了,一定会再次更新;做法就是尽量设置最长的内存时间,需要更改就更改一下请求的url
-
重新请求的方法以及问题
- 我们可以在请求的文件后面添加时间,如/style?t=20200424164455
- 问题1:有可能会在1s中改了两次文件,那么前半秒下载文件的用户得不到后半秒更改之后的文件
- 问题2:当有多个文件的时候,手动更改的时候容易出错
- 现在一般不使用时间戳
- 之前没有node.js,一般更新缓存的时候会在访问路径后面添加时间戳或者更新版本号
- 有了node.js之后,我们一般这样写/style-md5,文件后面加一个md5,
- md5是对文件内容去一个特殊值
- md5很难重复,现在一般的做法就是后面添加一个md5,有的会加一个md5的前八位
- 这样就不需要程序员自己算需要加什么样的值。
- 只要文件变化,md5就可以变化
- webpack内部封装了md5变化,通过设置,只要文件变化后,打包的时候就会在后面添加一个md5
- 一般入口文件不加缓存,因为入口文件需要记录其余文件的变化,一旦这个入口文件不变了,就意味着其余文件即便变化了我们也不知道。
- 我们可以在请求的文件后面添加时间,如/style?t=20200424164455
并不是缓存
- 使用 Etag 和 304 可以避免下载过程
- 这并不是严格意义的缓存
-
设置ETag的方式如下所示,使用response.setHeader('Etag', 'frank'):
-
设置好Etag之后,第一次请求之后,response里面会有一个Etag,其值就是我们设置好的值
-
第二次访问的时候,浏览器会带着这个Etag的值进行请求,即If-None-Match: frank
-
这是方便服务器去读取的,服务器如果拿到这个这个If-None-Match,可以拿着个跟之前设置的Etag做对比,相等就代表内容一致,不需要进行传递,就不返回内容就好
-
Etag还是会请求服务器,但是如果值相同就不下载,如果文件变化之后,更改一下Etag就好,我们可以让Etag就等于Md5
Cookie
Cookie 用于识别用户
注意:
- 现代前端几乎不会去碰 Cookie
- 如果需要多页面共享信息,请用 localStorage 或 sessionStorage
- Cookie 的所有知识都可以用维基百科查到
- 我们使用简单的node.js代码就可以实现Cookie。HTTP是一种无状态协议,使用Cookie就可以让HTTP有状态
-
我们在首页的响应头里面写一个Cookie,就可以看到在响应的时候会有这样的值,这个cookie其实只要关闭页面就过期了
-
我们发现我们只设置了入口文件的Cookie,并且入口文件的response里面带着的参数是set-cookie,即设置了cookie,一旦设置了cookie,后面所有文件的请求都会带着这个参数,即cookie参数,也就是请求的时候会将cookie返回给服务器
-
cookie就好像一个访问的权限,带着cookie可以访问一些内容,但是没有的话就不准访问,服务器可以读取这个Cookie,判断值到底对不对。
- 网页上填的东西都是以明文的形式传递的,没有任何安全性可言
- Cookie可以识别用户有没有曾经做过一件事情,如果做过,就种一个Cookie。我们一般将设置Cookie称为种Cookie,就是服务器的响应里面有一个set-cookie的头
-
登出一般是后端程序员做的事情,需要清除Cookie,一般是通过设置过期时间来的。
Session
Session 就是 server 为用户分配的一小段内存,用户的识别依据可以用 Cookie 保存(也可以不用 Cookie)
- Cookie的优点是可以记住用户的状态,缺点是Cookie容易被伪造
- 我们的目标是技能标识用户,也能防止用户随意更改?这就是Session,Session是Cookie的升级版
- 我们可以定义一个session,session相当于一个账本,给浏览器的是票据,这个票据对应着用户信息,这样就不会将用户信息暴露出去了。伪造票据的几率很低的。
- session可以每天重新分派,里面的账户信息是可以对应上的。猜对的概率很低很低。
- 使用随机的32位字符作为ID的一个好处是
- 用户不知道自己什么信息,无法自己更改
- 想随便更改碰对的几率太低了。
- 真正使用Cookie的方法是将Session的ID放入Cookie中,用户每次来都通过CookieID去读取Session。Session可以放到数据库中,可以放到内存中,可以放到文件中。Session就相当于一个内部的账单
- 登录是一个简单的东西,但是想做好登录是比较难的,因为涉及安全性,便捷性等各种各样的问题。有的登录还可以使用JWT来做
-
使用简单的方法模拟Session,随机数可能不是真正的随机,如Random,这是一个假随机,很可能被黑客猜到,不建议使用random,session要存在文件或者服务器中,否则刷新后就会消失
- Session和Cookie有什么区别:
- 目的:需要标识用户之间的不同使用Cookie,JWT是另一种技术;想记录某个用户信息的时候,使用Session,Session不是用来区分用户的,而是用来记录用户的敏感信息的
- 实现原理:Cookie在响应的时候,写一个Set-Cookie这个头就好了,Cookie是HTTP协议层里面的东西。Session跟HTTP没有关系,Session在不同的后台框架里面有不同的实现方式,存在文件,数据库里面。而Cookie是存在浏览器中的,浏览器每次请求都会将Cookie带到服务器中
- Session一般基于Cookie,但也是可以不基于Cookie的,可以返给LocalStorage,LocalStorage不会每次都随浏览器复制到请求中,我们可以使用JS去读取这个LocalStorage,读取之后就可以放到Header中,所以不用Cookie一样可以拿到Session;Session还可以放到url参数中如/login?sessionid=0.11111
反向代理是什么
server 根据请求中的信息,将请求分发到不同的地方,此 server 即为反向代理。
- 以 Nginx 配置多域名网站为例
- 以 server.js 转发请求为例
- 正向代理代理客户端,反向代理代理服务
-
FQ一般需要开个代理,这个代理其实是给客户端提供服务;
- 用户发请求进来会有一个HOST,如果这个HOST是baidu就发送到baidu服务器上,这个就是Ngix反向代理,配置文件可以实现这种代理