前端优化之路必不可少的知识点。
- 浏览器输入url到页面的展现,具体发生了些什么,前端能做哪些优化
- DNS 递归查询解析 —— DNS优化prefetch;
- TCP 三次握手、四次挥手 —— http1.0/1.1/2.0 的区别,http/s的区别;
- http 缓存 —— 304 与 CDN;
- 浏览器渲染机制 —— CSS、JS顺序的重要性,@import的损耗,防抖、节流、重排、重绘,GPU加速等;
- 如何优化JS主线程 —— web worker,任务分片
- ...
- 图片你优化了吗,雪碧图、webp、svg;
- webpack 等打包优化
- 运维的基本知识 nginx
本文按一定顺序总结与前端性能优化的基本点,大家可以按步骤逐一检查自己的项目,找出性能的瓶颈。如有错误遗漏欢迎补充纠正。
文章有些原理细节都在参考文章中,价值较高建议读一读。
webpack
默认的 webpack4
很多优化内部已经做到很好了,但无法满足所有的业务场景, 如果发现开发时打包慢、打包体积太大,这是你就要审视下配置了。
代码分块分析插件 webpack-bundle-analyzer
npm i webpack-bundle-analyzer -D
-
修改
webpack.config.js
// 在头部添加 const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); // 在plugins: [] 中新增配置如下 new BundleAnalyzerPlugin({ analyzerMode: 'server', analyzerHost: '127.0.0.1', analyzerPort: 8000, reportFilename: 'report.html', defaultSizes: 'parsed', openAnalyzer: true, generateStatsFile: false, statsFilename: 'stats.json', statsOptions: null, logLevel: 'info' })
-
启动本地开发服务,浏览器中打开
http://127.0.0.1:8000
-
webpack4 默认代码分割策略
- 新的 chunk 是否被共享或者是来自 node_modules 的模块
- 新的 chunk 体积在压缩之前是否大于 30kb
- 按需加载 chunk 的并发请求数量小于等于 5 个
- 页面初始加载时的并发请求数量小于等于 3 个
比如,由于业务中频繁 antd
中的UI组件,但他们都小于 30kb
不会被独立打包,导致过多重复的代码打入不同 chunk
中。 这时根据实际业务情况,默认的配置就不满足需求了。修改策略:
-
react 全家桶和状态管理一个
vendor
包 -
antd 相关的一个
lib
包 -
node_modules 里的打成
common
包// 默认配置 splitChunks: { chunks: 'all', name: false, } // 修改后的配置 splitChunks: { chunks: 'all', name: false, cacheGroups: { vendor: { name: 'vendor', test: module = >/(react|react-dom|react-router-dom|mobx|mobx-react)/.test(module.context), chunks: 'initial', priority: 11 }, libs: { name: 'libs', test: (module) = >/(moment|antd|lodash)/.test(module.context), chunks: 'all', priority: 14 }, common: { chunks: "async", test: /[\\/] node_modules[\\ / ] / , name: "common", minChunks: 3, maxAsyncRequests: 5, maxSize: 1000000, priority: 10 } } }
结论:
- 优化前体积为56MB(去除sourceMap)
- 优化后体积为36MB(保留sourceMap,去除sourceMap大约在7.625MB)
glob 和 purgecss-webpack-plugin 去除无用CSS
npm i glob purgecss-webpack-plugin -D
// 在webpack.config.js中的plugins: []中添加.
// 需要注意的是paths一定要是绝对路径,比如antd的样式不在src目录下就需要写成一个数组,要不然antd的样式就会不见了
new purgecssWebpackPlugin({
paths: glob.sync(`${paths.appSrc}/**/*`, { nodir: true })
})
结论:CSS
资源减小很多
一番操作下来
- 目前资源已经减小到7.25M左右;
- 打包速度由7.5分钟减少到2.5分钟,效率极大提升。
图片
webp
webp
是一种新式图片格式,在保证品质的同时提供无损和有损压缩。 webp
对于图片较多的站点是必不可少的优化手段,一般 CDN
都有提供转换服务。
-
某宝大规模使用
-
优点:在同等品质下,无损图片比
png
减少26%
的大小,有损下比jpeg
小25-34%
。 -
缺点:有的浏览器不兼容,需要做兼容,以下是官方提供
// check_webp_feature: // 'feature' can be one of 'lossy', 'lossless', 'alpha' or 'animation'. // 'callback(feature, result)' will be passed back the detection result (in an asynchronous way!) function check_webp_feature(feature, callback) { var kTestImages = { lossy: "UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA", lossless: "UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==", alpha: "UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==", animation: "UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA" }; var img = new Image(); img.onload = function () { var result = (img.width > 0) && (img.height > 0); callback(feature, result); }; img.onerror = function () { callback(feature, false); }; img.src = "data:image/webp;base64," + kTestImages[feature]; }
雪碧图
多个图片拼成一个图片,在利用 background-position
定位。 减少 http 请求。
HTTP2 可以解决线头阻塞问题。
iconfont
svg 版雪碧图
base64
// 字符转
window.btoa('str');
// canvas 转
canvas.toDataURL();
// 图片转
const reader = new FileReader();
const imgUrlBase64 = reader.readAsDataURL(file);
// webpack 打包转
// url-loader
- 优点:便于存储在html、js中,减少http请求个数。
- 缺点:文件尺寸增大
30%
左右。
适用于少量小图的场景。
缓存
DNS缓存
查找过程
- 先浏览器 DNS 缓存
- 查找
hosts
文件域名IP
映射(你知道背墙DNS污染但没封的IP,可以设置hosts文件访问) - 查找本地
DNS解析器
(路由) 缓存 - 根DNS服务器 -> 顶级.com -> 二级域名服务器xx.com -> 主机 www.xx.com
-
可以看出某些
DNS
解析占大头时间,优化还是很有必要的 -
dns-prefetch
,例如访问某宝首页,猜测你接下来要访问某些域名,提前去解析。以节省下个页面的DNS
查询。 不过大量不必要的预获取,对公共网络资源造成较大浪费。 -
优化后
http 缓存
简单提下,对于现在 SPA
项目,一般静态资源放在 CDN 上, 对经常变动入口文件index.html
设置强制检验过期 Cache-Control: no-cahce
或直接不缓存。 其他 hash
命名的资源直接设置长缓存(max-age: 一年半载)。
具体详情已在另一篇文字阐释,文末链接。
CDN(Content Delivery Network) 内容分发网络
优点:
- 资源文件多处备份,就近原则,网络离用户最近的服务器提供服务,速度快、安全性高;
- 带宽贵啊,大量的用户访问,不上
CDN
,网站很卡或崩溃。
本地缓存 localStorage、sessionStorage、cookie
- storage
- localStorage 一直存在浏览器中,要么用户删除或浏览器缓存策略剔除
- sessionStorage 页面关闭消失
优点:可以存储较大的数据 Chrome 5M
- cookie
相比 storage
:
- 优点:可以设置失效时间
- 缺点:存储量较小,
http1.x
每次会上报给服务器,造成网络浪费。
建议:对服务器安全数据设置 http-only
,能少用尽量少用,只用来与服务器进行状态维护和用户识别。
浏览器渲染
CSS
- 减少
@import
的使用,浏览器解析html
会优化嗅探获并发获取文件,如果使用@import
需要下载解析了当前CSS
文件,才能下载。 - CSS 权重较高,应该优先下载解析。
脚本 defer、async
只对外联脚本有效。众所周知,脚本解析会阻塞 DOM
解析,这两个属性就是为了解决这个问题。
-
defer
下载时不阻塞 HTML 解析成 DOM,下载完成会等待DOM
构建完毕且在DOMContentLoaded
事件触发之前执行,多个defer
脚本保证脚本执行顺序。 -
async
下载时不阻塞 HTML 解析成 DOM,下载完毕后尽量安排JS执行。意思说执行时间不确定,早下载早执行。如果DOM
未构建完,脚本可能会阻塞DOM构建。 -
例如某宝大量在头部使用
async
-
但不是很理解是,按原理
async
应该用在对DOM构建
和脚本顺序无依赖的场景,而且下载太快还可能阻塞DOM构建
。感觉defer
更合适。
防抖、节流
在其他文章有详细说明,在此不再赘述,请看参考索引。
防止强制布局
-
主要是在避免在循环中又读又写样式
GPU 加速
will-change: transform
transform: translateZ(0)
会单独把元素提升层级交给 GPU
渲染,适合一些 Animation
动画。
requestAnimation, requestIdelCallback
-
google
文档上有很多探讨,检测计算长任务的新api
进展。 -
Facebook
最新react
中的fiber
调度,就使用了requestAnimation
,requestIdelCallback
来 进行长任务的时间切片,避免以前深DOM
树更新产生长耗时甚至抖动。
web worker
对于需要大量计算会占用渲染主线程,适合放到 web worker
来执行。
服务器
http2
http1.1 对比 http 1.0 主要进步有
- 缓存处理的增强,如
Etag
- 加入
range
头,响应码206(Partial Content)
支持断点续传 - 加入
host
头,多个域名可以绑定一个IP
- 响应头
Connection: keep-alive
,长连接,客户端与同一个主机通信不必多次三次握手
https 与 http
- https 需要申请
CA
证书,要钱;https 在 http 上多了层安全协议SSL/TLS
; - 客户端进行
CA
认证,连接需要非对称加密(耗时),传输数据使用对称加密; - 防止运营商 http 劫持,插广告等。http 端口
80
,https443
。
http2 对比 http1.x
- header压缩,例如后者每次传输数据都得带很多相同的头部信息,http2 会压缩头部且避免重复头部重传;
- 新的二进制格式,后者是基于文本协议解析,前者基于01串,方便且健壮;
- 多路复用,注意与
keep-alive
区分。前者是连接共享,每个请求有个唯一id
来确认归属,多个请求可以同时相互混杂。 而后者减少了握手保持长联,会影响服务器性能,先进先出需要等前一个请求发完才能进行下一个,造成线头阻塞。客户端一般会限制一个页面与不同服务器同时http连接上限; - 服务器推送,http1.x服务器只能被动接收请求发送资源,HTTP2可以主动推送。
http2 可以提升传输效率。nginx 有必要做好 http2 的升级和降级处理。
gzip
服务器开启压缩,文本类型的文件能够减少网络传输。 特别是文件较大且重复率高的文本压缩效果更明显。
-
如图
index.html
文件压缩(383-230)/383=39.95%
。
- Chrome request headers 告诉服务器支持的压缩算法:
Accept-Encoding: gzip, deflate, br
- 服务器响应使用的压缩算法 response headers:
Content-Encoding: gzip
-
nginx 开启
gzip on; // 不压缩临界值,大于1K的才压缩,一般不用改 gzip_min_length 1k; // 压缩级别,1-10,数字越大压缩的越细,时间也越长 gzip_comp_level 2; // 进行压缩的文件类型 gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png; // ie兼容性不好所以放弃 gzip_disable "MSIE [1-6]\.";
compression-webpack-plugin 配合 gzip
npm i compression-webpack-plugin -D
const CompressionWebpackPlugin = require('compression-webpack-plugin');
// 在webpack.config.js中的plugins: []中添加.
new CompressionWebpackPlugin({
asset: '[path].gz[query]', // 目标文件名
algorithm: 'gzip', // 使用gzip压缩
test: /\.(js|css)$/, // 压缩 js 与 css
threshold: 10240, // 资源文件大于10240B=10kB时会被压缩
minRatio: 0.8, // 最小压缩比达到0.8时才会被压缩
})
- 优点
nginx
开启gzip
,发现有压缩好的gzip
压缩文件,就会直接使用,减少服务器cpu
的资源的使用。 - 缺点 打包时间就会拉长。现在静态资源一般会上
CDN
,gzip
是CDN服务器基本的服务, 需要去节省本就花了钱买的服务器资源,而增加打包的时间吗?
es6、动态使用POLYFILL
webpack
默认支持 es6
的新特性 tree-shaking
,可以摇掉不用的代码,且新api的性能很高。 推荐全面使用。
// 会根据ua返回不同的内容
https://polyfill.io/v3/polyfill.min.js
方案
优点
缺点
babel-polyfill
React 官方推荐
体积200kb
babel-plugin-transform-runtime
体积小
不能poyfill原型上的方法
polyfill-service
动态根据ui加载
兼容性,国内部分浏览器有问题
结论:可以减少资源大小,但依赖外部服务,自建麻烦的话,放弃。
来自山西太原的用户 3天前
贼详细,收藏了
来自辽宁沈阳的用户 7天前
老衲喜欢这篇文章
来自北京朝阳的用户 8天前
小有所得,不错
来自河北石家庄的用户 22天前
满满的干货,受益匪浅!如果排版再弄好看点,可能点赞数更高
来自福建福州的用户 27天前
为何你如此秀
来自海南海口的用户 28天前
涨知识了
来自北京朝阳的用户 29天前
加油~付出就会有收获
来自上海徐汇的用户 29天前
这个要记录下来,每天打卡。跟着大佬学习
来自海南海口的用户 30天前
优秀,加油!