启用压缩 Gzip
Gzip 是用来做网络资源压缩,帮助我们减少资源文件在网络传输大小的技术,可以高达 90%
如下是 MacOs 安装方法,Windows 安装方法及使用可以参考我这篇文章:项目技术架构-Nginx 服务器搭建
- 安装 homebrew:https://brew.sh/index_zh-cn
1 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" |
- 安装 nginx
1 | brew install nginx |
- 运行 ngix
1 | sudo brew services start nginx |
- 查看配置文件
1 | vim /usr/local/etc/nginx/nginx.conf |
修改为如下配置:
- 访问:
localhost:8090
即可(注意每行结尾都需要加;
,路径需要使用/
)
1 | server |
配置 gzip:
1 | http { |
启用 Keep Alive
这个技术可以帮助我们对 TCP 链接进行复用,也就是说当我们和一台服务器进行 TCP 建立连接之后,接下来的请求就都不需要重复建立链接。Nginx 默认开启 keep-alive
- 它是 HTTP 标准中的一部分,多数情况是有益无害的,所以在 HTTP1.1 以后,Keep Alive 默认开启
- Initial connection 为 TCP 链接的建立,后续资源加载就没有 Initial connection
可以在 Request Headers 中看到 keep-alive
参数
1 | http { |
HTTP 资源缓存
缓存资源
- 提高重复访问时资加载的速度
HTTP 缓存方案:
- Cache-Control/Expires
- Last-Modified + If-Modified-Since
- Etag + If-None-Match
Cache-Control/Expires
HTTP 1.0 中通过 Pragma 控制页面缓存,通常设置为
no-cache
并加上expires: 0
(立即过期,下次再用时去服务端拿)HTTP 1.1 中启用 Cache-Control 来控制页面是否缓存,常用参数:
no-cache
、public
、no-store
、must-revalidate
配置后两个主要是为了兼容性问题
因为 JS 和 CSS 在 Webpack 里都使用 Hash 命名放,这也可以保证 HTML 更新到最新,拿到的 JS 和 CSS 也是最新的
1 | server |
客户端第一次请求一个 URL,服务器返回状态是 200,同时有一个 Last-Modified
报头的属性标记
1 | Last-Modified:Tue, 24 Feb 2019 08:01:04 GMT |
客户端第二次请求此 URL,浏览器会向服务器传送 If-Modified-Since
报头,询问该时间是否被修改过。如果服务器资源没有变化,自动返回 304,内容为空,客户端直接从缓存中取内容即可;如果资源有变化,则向客户端发送最新资源
1 | If-Modified-Since:Tue, 24 Feb 2019 08:01:04 GMT |
Etag 同理,第一次请求会服务器会返回 Etag
报头
1 | Etag:“5d8c72a5edda8d6a:3239“ |
第二次请求会向服务器传送 If-None-Match
报头
1 | If-None-Match:“5d8c72a5edda8d6a:3239“ |
缓存网站参考
更多配置可以看:HTTP Headers
max-age
:设置缓存存储的周达周期,单位秒s-maxage
:只用于共享缓存,比如:CDN 缓存(s -> share)max-age
用于普通缓存,s-maxage
用于代理缓存它会跟服务器进行重新确认(携带
if-none-match
)去确认
public
:响应可以被任何对象(发送的客户端、代理服务器)缓存private
:响应只能被单个用户缓存,不能作为共享缓存(代理服务器不能缓存)no-store
:绝对禁止缓存no-cache
:资源不进行缓存,但是设置了这个不代表浏览器不缓存,而是缓存前要向服务器确认资源是否被更改,因为有时为了保险起见还会加上private
指令或将过期时间设为过去的时间
must-revalidate
:缓存必须在使用之前验证旧资源的状态,并且不可使用过期资源- 由于通过了 service worker,它并没有真正和服务器进行确认,可以直接去使用
Service Worker
加速重复访问
离线支持
用户在没有网络的情况下(offline)也可以让用户访问我们的网页
serviceWorker 也有自己的生命周期,首先要注册安装激活才能使用,打包后的目录里会生成 asset-manifest.json
里面定义了哪些资源要进行缓存、缓存文件的文件名、相关的版本信息会存在 precache-manifest
里,每个文件都有先关版本信息
- 需要使用两个插件生成 serviceWorker,一个叫 WorkboxWebpackPlugin,另一个叫 ManifestPlugin(生成
asset-manifest.json
)它会决定哪些资源进行缓存,通常会把所有静态资源 HTML、CSS、JS 都进行缓存,图片或视频资源一般不会缓存
1 | const SpeedMeasurePlugin = require('speed-measure-webpack-plugin') |
在入口文件里注册即可
1 | import * as serviceWorker from './serviceWorker' |
Service Worker 原理:
- 在客户端和服务端建立一个中间层,做了存储
Service Worker 注意:
延长了首屏时间,但页面总加载时间减少
兼容性
只能在 localhost 或者 https 下使用(为了保证安全性)
HTTP2 提升
HTTP2 优势
二进制传输
HTTP 1.1 基于文本传输,效率低且不安全
HTTP 2 基于二进制编码传输,安全且能进行很好的压缩,提高了传输效率
请求响应多路复用
HTTP 1.1 实现是基于请求-响应模型,同一个连接中 HTTP 完成一个事务才能处理下一个事务,如果响应迟迟不来,后续请求无法发送,造成了 对头阻塞 问题。如果并发多个请求就需要多个 TCP 连接,开启 keep-alive,虽然可以用多次,但是同一时刻只能有一个 HTTP 请求
HTTP 2 所有相同域名的请求都通过一个 TCP 连接并发完成,多个 Stream 复用一条 TCP 连接
Server push
HTTP 1.1 不支持服务器主动推送资源给客户端,都是客户端向服务器发起请求后,才能获取到服务器响应的资源
HTTP 2 服务器可以主动推送资源文件,减少消息传递次数。客户端发起请求,必须使用奇数号 Stream,服务器主动推送,使用偶数号 Stream(会先发送 PUSH_PROMISE 帧,告诉客户端接下来在哪个 Stream 发送资源)
头部压缩(HTTP 协议报文是有 Header + Body 构成)
HTTP 1.1 可以使用头字段(Content-Encoding)指定 Body 压缩方式(gzip),但是 Header 没有针对它的优化手段
HTTP 2 使用 HPACK 算法进行压缩,对于常见的头通过 静态表和 Huffman 编码 方式,后续请求头,可以建立 动态表
开启 HTTP2
- HTTPS
- 适合较高的请求量
1 | server |
自签名证书
- 执行最得到
server.crt
和server.key
,在工程目录下新建 ssl 文件夹,将其拷贝进去
1 | openssl genrsa -des3 -passout pass:x -out server.pass.key 2048 |
访问 https://localhost:843
,会出现如下图。因为我们使用的是自签名证书,直接在键盘输入 thisisunsafe
,页面就可以绕过证书的验证了
所有的网络资源都变成 http2 的协议了,还有 h3(这里 h3 是对 google 外部资源的请求)
HTTP 1.1 虽然可以用 keep-alive
复用同个 TCP 链接,但是资源还是有顺序的,会形成阻塞
HTTP 2 真正做到了异步或并发的对资源进行传输,同一个时刻可以发起多个资源请求,可以将不同资源信息同时通过网络传回浏览器
Server Push(服务器推送)
正常客户端拿到资源都是向服务器发起请求,服务器再把资源推送给客户端,这个来回是有消耗的(TTFB),如果能让服务器提前把这些东西推送到客户端,就能节约一定的网络开销
1 | server |
重启 nginx,可以发现图片没有了绿色部分(TTFB),少了请求返还回路的过程
- Initiator 图片为 Push,这种资源是通过 server push 提前推送到浏览器的
服务端渲染 SSR
SSR 好处:
- 加速首屏加载
- 更好的 SEO
基于 Next.js 实现SSR
1 | npm init -y |
添加 scripts 执行脚本:
1 | // package.json |
在 index.jsx
中添加如下内容,之后 npm run dev
即可
- 服务端渲染会把页面上显示的所有内容都放在 html 里
- next.js 已经帮我们把代码进行基于路由的代码拆分,里面提供了 Link 组件
1 | import React from 'react' |
是否使用 SSR
- 架构-大型,动态页面,面向公众用户(是否关心首屏速度)
- 搜索引擎排名很重要(前面的页面使用静态页面,后面页面使用 vue react 实现动态加载)