14

前端极致性能优化手册大全

 4 years ago
source link: https://juejin.im/post/5e22a5c15188254dc132fab4
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

原文链接

前端优化之路必不可少的知识点。

  • 浏览器输入url到页面的展现,具体发生了些什么,前端能做哪些优化
  1. DNS 递归查询解析 —— DNS优化prefetch;
  2. TCP 三次握手、四次挥手 —— http1.0/1.1/2.0 的区别,http/s的区别;
  3. http 缓存 —— 304 与 CDN;
  4. 浏览器渲染机制 —— CSS、JS顺序的重要性,@import的损耗,防抖、节流、重排、重绘,GPU加速等;
  5. 如何优化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

    webpack-bundle-analyzer
  • webpack4 默认代码分割策略

  1. 新的 chunk 是否被共享或者是来自 node_modules 的模块
  2. 新的 chunk 体积在压缩之前是否大于 30kb
  3. 按需加载 chunk 的并发请求数量小于等于 5 个
  4. 页面初始加载时的并发请求数量小于等于 3 个

比如,由于业务中频繁 antd 中的UI组件,但他们都小于 30kb 不会被独立打包,导致过多重复的代码打入不同 chunk 中。 这时根据实际业务情况,默认的配置就不满足需求了。修改策略:

  1. react 全家桶和状态管理一个 vendor
  2. antd 相关的一个 lib
  3. 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 对于图片较多的站点是必不可少的优化手段,一般 CDN 都有提供转换服务。

  • 某宝大规模使用

    webp
  • 优点:在同等品质下,无损图片比 png 减少 26% 的大小,有损下比 jpeg25-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];
}
复制代码
  • 根据客户端上报的请求头 accept: image/webp,服务端进行判断,支持则返回 webp 不支持则原图。

多个图片拼成一个图片,在利用 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缓存

  1. 先浏览器 DNS 缓存
  2. 查找 hosts 文件域名 IP 映射(你知道背墙DNS污染但没封的IP,可以设置hosts文件访问)
  3. 查找本地 DNS解析器(路由) 缓存
  4. 根DNS服务器 -> 顶级.com -> 二级域名服务器xx.com -> 主机 www.xx.com
  • 可以看出某些 DNS 解析占大头时间,优化还是很有必要的

    DNS
  • dns-prefetch,例如访问某宝首页,猜测你接下来要访问某些域名,提前去解析。以节省下个页面的 DNS 查询。 不过大量不必要的预获取,对公共网络资源造成较大浪费。

    dns-prefetch
  • dns-better

http 缓存

http 缓存

简单提下,对于现在 SPA 项目,一般静态资源放在 CDN 上, 对经常变动入口文件index.html 设置强制检验过期 Cache-Control: no-cahce 或直接不缓存。 其他 hash 命名的资源直接设置长缓存(max-age: 一年半载)。

具体详情已在另一篇文字阐释,文末链接。

CDN(Content Delivery Network) 内容分发网络

  1. 资源文件多处备份,就近原则,网络离用户最近的服务器提供服务,速度快、安全性高;
  2. 带宽贵啊,大量的用户访问,不上 CDN ,网站很卡或崩溃。

本地缓存 localStorage、sessionStorage、cookie

  • storage
  1. localStorage 一直存在浏览器中,要么用户删除或浏览器缓存策略剔除
  2. sessionStorage 页面关闭消失

优点:可以存储较大的数据 Chrome 5M

  • cookie

相比 storage

  1. 优点:可以设置失效时间
  2. 缺点:存储量较小,http1.x 每次会上报给服务器,造成网络浪费。

建议:对服务器安全数据设置 http-only,能少用尽量少用,只用来与服务器进行状态维护和用户识别。

浏览器渲染

  • 减少 @import 的使用,浏览器解析 html 会优化嗅探获并发获取文件,如果使用 @import 需要下载解析了当前 CSS 文件,才能下载。
  • CSS 权重较高,应该优先下载解析。

脚本 defer、async

只对外联脚本有效。众所周知,脚本解析会阻塞 DOM 解析,这两个属性就是为了解决这个问题。

  • defer 下载时不阻塞 HTML 解析成 DOM,下载完成会等待 DOM 构建完毕且在 DOMContentLoaded 事件触发之前执行,多个 defer 脚本保证脚本执行顺序。

  • async 下载时不阻塞 HTML 解析成 DOM,下载完毕后尽量安排JS执行。意思说执行时间不确定,早下载早执行。如果 DOM 未构建完,脚本可能会阻塞DOM构建。

  • 例如某宝大量在头部使用 async

    async
  • 但不是很理解是,按原理 async 应该用在对 DOM构建 和脚本顺序无依赖的场景,而且下载太快还可能阻塞 DOM构建。感觉 defer 更合适。

防抖、节流

在其他文章有详细说明,在此不再赘述,请看参考索引。

防止强制布局

  • 主要是在避免在循环中又读又写样式
    FSL

GPU 加速

  1. will-change: transform
  2. transform: translateZ(0)

会单独把元素提升层级交给 GPU 渲染,适合一些 Animation 动画。

requestAnimation, requestIdelCallback

  • google 文档上有很多探讨,检测计算长任务的新 api 进展。
  • Facebook 最新 react 中的 fiber 调度,就使用了 requestAnimation, requestIdelCallback 来 进行长任务的时间切片,避免以前深 DOM 树更新产生长耗时甚至抖动。
    main-thread

web worker

对于需要大量计算会占用渲染主线程,适合放到 web worker 来执行。

http2

http1.1 对比 http 1.0 主要进步有

  1. 缓存处理的增强,如 Etag
  2. 加入 range头,响应码206(Partial Content) 支持断点续传
  3. 加入 host 头,多个域名可以绑定一个 IP
  4. 响应头 Connection: keep-alive,长连接,客户端与同一个主机通信不必多次 三次握手

https 与 http

  1. https 需要申请 CA 证书,要钱;https 在 http 上多了层安全协议 SSL/TLS
  2. 客户端进行 CA 认证,连接需要非对称加密(耗时),传输数据使用对称加密;
  3. 防止运营商 http 劫持,插广告等。http 端口 80,https 443

http2 对比 http1.x

  1. header压缩,例如后者每次传输数据都得带很多相同的头部信息,http2 会压缩头部且避免重复头部重传;
  2. 新的二进制格式,后者是基于文本协议解析,前者基于01串,方便且健壮;
  3. 多路复用,注意与 keep-alive 区分。前者是连接共享,每个请求有个唯一 id 来确认归属,多个请求可以同时相互混杂。 而后者减少了握手保持长联,会影响服务器性能,先进先出需要等前一个请求发完才能进行下一个,造成线头阻塞。客户端一般会限制一个页面与不同服务器同时http连接上限;
  4. 服务器推送,http1.x服务器只能被动接收请求发送资源,HTTP2可以主动推送。

http2 可以提升传输效率。nginx 有必要做好 http2 的升级和降级处理。

服务器开启压缩,文本类型的文件能够减少网络传输。 特别是文件较大且重复率高的文本压缩效果更明显。

  • 如图 index.html 文件压缩 (383-230)/383=39.95%
    gzip
  1. Chrome request headers 告诉服务器支持的压缩算法:Accept-Encoding: gzip, deflate, br
  2. 服务器响应使用的压缩算法 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 的资源的使用。
  • 缺点 打包时间就会拉长。现在静态资源一般会上CDNgzip 是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加载 兼容性,国内部分浏览器有问题

结论:可以减少资源大小,但依赖外部服务,自建麻烦的话,放弃。

  1. 从优化到面试装逼指南——网络系列

浏览器渲染

webpack

  1. 手摸手,带你用合理的姿势使用webpack4(下)

http相关

iconfont


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK