前端优化之路必不可少的知识点。
- 浏览器输入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加载
兼容性,国内部分浏览器有问题
结论:可以减少资源大小,但依赖外部服务,自建麻烦的话,放弃。
来自河北沧州的用户 4天前
感谢分享,希望作者继续出文章,多交流分享。
来自辽宁沈阳的用户 5天前
优秀,先收藏了。
来自河南郑州的用户 12天前
非常感谢,眼界开阔了
来自湖南长沙的用户 20天前
好文章,值得分享
来自山东济南的用户 20天前
可以说是相当详细了
来自海南海口的用户 21天前
信息量好大
来自湖南长沙的用户 24天前
比我还好 一年的数据库 一年的后端 一点的前端 现在搞得什么都没有学号学精
来自北京昌平的用户 26天前
可以的,老铁,点个赞
来自浙江杭州的用户 26天前
贼详细,收藏了