前端性能优化

1.图片优化

  1. 选择合适的图片格式压缩大图,可从根源上截图大图加载过慢的问题。
  2. 使用雪碧图iconfontbase64css 代替图片等可减少图片 http 请求,提高页面加载速度。
  3. 使用 CDN 图片可达到分流的效果,减少服务券压力。
  4. 图片懒加载预加载渐进式图片等可不同程度减少白屏时间,提高产品体验。

1.1 图片懒加载

  • 减少资源的加载,页面启动只加载首屏的图片,这样能明显减少了服务器的压力和流量,也能够减小浏览器的负担。
  • 防止并发加载的资源过多而阻塞 js 的加载,影响整个网站的启动,影响用户体验
  • 浪费用户的流量,有些用户并不想全部看完,全部加载会耗费大量流量。

原理:图片懒加载的原理就是暂时不设置图片的 src 属性,而是将图片的 url 隐藏起来(比如先写在 data-src 里面),等当前图片是否到了可视区域再将图片真实的 url 放进 src 属性里面,从而实现图片的延迟加载。

function lazyload() {
  let viewHeight = document.body.clientHeight //获取可视区高度
  let imgs = document.querySelectorAll('img[data-src]')
  imgs.forEach((item, index) => {
    if (item.dataset.src === '') return

    // 用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置
    let rect = item.getBoundingClientRect()
    if (rect.bottom >= 0 && rect.top < viewHeight) {
      item.src = item.dataset.src
      item.removeAttribute('data-src')
    }
  })
}

// 可以使用节流优化一下
window.addEventListener('scroll', lazyload)

通过上面例子的实现,我们要实现懒加载都需要去监听 scroll 事件,使用IntersectionObserver可以不需要频繁地调用scroll 事件

IntersectionObserver接口 (从属于Intersection Observer API) 提供了一种异步观察目标元素与其祖先元素或顶级文档视窗(viewport)交叉状态的方法。

const imgs = document.querySelectorAll('img[data-src]')
const config = {
  rootMargin: '0px',
  threshold: 0,
}
let observer = new IntersectionObserver((entries, self) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      let img = entry.target
      let src = img.dataset.src
      if (src) {
        img.src = src
        img.removeAttribute('data-src')
      }
      // 解除观察
      self.unobserve(entry.target)
    }
  })
}, config)

imgs.forEach((image) => {
  // 开始简体一个元素
  observer.observe(image)
})

1.2 图片预加载

图片预加载,是指在一些需要展示大量图片的网站,将图片提前加载到本地缓存中,从而提升用户体验。
常用的方式有两种:

  1. 隐藏在 cssbackgroundurl 属性里面,

    function preloader() {
      if (document.getElementById) {
        document.getElementById('preload-01').style.background =
          'url(http://domain.tld/image-01.png) no-repeat -9999px -9999px'
        document.getElementById('preload-02').style.background =
          'url(http://domain.tld/image-02.png) no-repeat -9999px -9999px'
        document.getElementById('preload-03').style.background =
          'url(http://domain.tld/image-03.png) no-repeat -9999px -9999px'
      }
    }
    function addLoadEvent(func) {
      var oldonload = window.onload
      if (typeof window.onload != 'function') {
        window.onload = func
      } else {
        window.onload = function () {
          if (oldonload) {
            oldonload()
          }
          func()
        }
      }
    }
    addLoadEvent(preloader)
    
  2. 通过 javascriptImage 对象设置实例对象的 src 属性实现图片的预加载。

    function preloader() {
      if (document.images) {
        var img1 = new Image()
        var img2 = new Image()
        var img3 = new Image()
        img1.src = 'http://domain.tld/path/to/image-001.gif'
        img2.src = 'http://domain.tld/path/to/image-002.gif'
        img3.src = 'http://domain.tld/path/to/image-003.gif'
      }
    }
    function addLoadEvent(func) {
      var oldonload = window.onload
      if (typeof window.onload != 'function') {
        window.onload = func
      } else {
        window.onload = function () {
          if (oldonload) {
            oldonload()
          }
          func()
        }
      }
    }
    addLoadEvent(preloader)
    

1.3 响应式图片加载

  • 在不同分辨率的设备上显示不同尺寸的图片,避免资源的浪费

  • 常用的方法就是 css3媒体查询(media query)。

@media  screen and (min-width: 1200px) {
  img {
    background-image: url('1.png');
  }
}
@media  screen and (min-width: 992px) {
  img {
    background-image: url('2.png');
  }
}
@media  screen and (min-width: 768px) {
  img {
    background-image: url('3.png');
  }
}
@media screen and (min-width: 480px) {
  img {
    background-image: url('4.png');
  }
}
  • 此外,还可以使用 HTML5picture 属性进行响应式处理。方法如下:
<picture>
  <source srcset="src/img/l.png" media="(min-width: 1200px)" />
  <source srcset="src/img/2.png" media="(min-width: 992px)" />
  <source srcset="src/img/4.png" media="(min-width: 768px)" />
  <img src="src/img/4.png" />
</picture>

1.4 渐进式图片

  • 渐进式图片的意思是在高画质图像加载完之前会先显示低画质版本。
  • 低画质版本由于画质低、压缩率高,尺寸很小,加载很快。

在这里插入图片描述

1.5 图片压缩

  • 有损压缩:有损压缩指在压缩文件大小的过程中,损失了一部分图片的信息,也即降低了图片的质量。例如 jpg 格式的图片使用的就是有损压缩。
  • 无损压缩:无损压缩指的是在压缩图片的过程中,图片的质量没有任何损耗。例如 pnggif 使用的就是无损压缩。

1.5.1 工具压缩

1.5.2 webpack 压缩

工程化的项目可以在 webpack 里面配置 image-webpack-loader 进行图片压缩

1.安装依赖 npm install --save-dev image-webpack-loader
2.配置 webpack

module.exports = {
...
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name].[hash:7].[ext]'
            },
          },
          {
            loader: 'image-webpack-loader',
            options: {
              mozjpeg: {
                progressive: true,
                quality: 50,
              },
              optipng: {
                enabled: true,
              },
              pngquant: {
                quality: [0.5, 0.65],
                speed: 4,
              },
              gifsicle: {
                interlaced: false,
              },
              webp: { // 不支持WEBP就不要写这一项
                quality: 75
              },
            },
          },
        ],
      },
    ],
  },
}

1.6 使用雪碧图

  • 雪碧图(CSS Sprites),是一种 CSS 图像合成技术,主要用于小图片显示。
  • 把 10 个小图片合并为一张大图片的画,那么只用一次请求即可拉取下来 10 个小图片的资源。
  • 减少服务器压力,减少并发,减少请求次数。
  • 把诸多小图片合成一张大图,利用 backround-position 属性值来确定图片呈现的位置

1.6.1 webpack 合成雪碧图

在 webpack 中,有相应的插件提供了自动合成雪碧图的功能并且可以自动生成对应的样式文件— webpack-spritesmith,使用方法如下

var path = require('path')
var SpritesmithPlugin = require('webpack-spritesmith')

module.exports = {
  // ...
  plugins: [
    new SpritesmithPlugin({
      src: {
        cwd: path.resolve(__dirname, 'src/ico'),
        glob: '*.png',
      },
      target: {
        image: path.resolve(__dirname, 'src/spritesmith-generated/sprite.png'),
        css: path.resolve(__dirname, 'src/spritesmith-generated/sprite.style'),
      },
      apiOptions: {
        cssImageRef: '~sprite.png',
      },
    }),
  ],
}

通过上面配置就能将 src/ico 目录下的所有 png 文件合成雪碧图,并且输出到对应目录

1.7 使用 iconfont

iconfont(字体图标),即通过字体的方式展示图标,多用于渲染图标、简单图形、特殊字体等。

优点:

  • 像使用字体一样,设置大小、颜色及其他样式,不失真
  • 轻量,易修改
  • 有效减少 HTTP 请求次数

1.8 使用 base64 格式

将图片转换为 base64 编码字符串 inline 到页面或 css 中。

优点:

  • 提升性能: 网页上的每一个图片,都是需要消耗一个 http 请求下载而来的, 图片的下载始终都要向服务器发出请求,要是图片的下载不用向服务器发出请求,base64 可以随着 HTML 的下载同时下载到本地.减少 https 请求。
  • 加密: 让用户一眼看不出图片内容 , 只能看到编码。
  • 方便引用: 在多个文件同时使用某些图片时, 可以把图片转为 base64 格式的文件, 把样式放在全局中, 比如 common.css, 以后在用的时候就可以直接加类名, 二不需要多层找文件路径, 会提升效率

但需要注意的是:如果图片较大,图片的色彩层次比较丰富,则不适合使用这种方式,因为该图片经过 base64 编码后的字符串非常大,会明显增大 HTML 页面的大小,从而影响加载速度。

base64 化最常见的就是在 url-loader 中使用:

module.exports = {
...
  module: {
    rules: [
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10240,
          name: utils.assetsPath('img/[name].[hash:7].[ext]'),
        }
      },
    ],
  },
}

这样就能将项目中小于 10kb 的图片转化为 base64 应用到页面中

1.9 图片格式

1.9.1 JPEG

  • JPEG 格式的图片可以呈现数百万种颜色。所以每当网站需要呈现色彩丰富的图片,JPEG 总是最佳选择。
  • 有损压缩,你可以通过压缩大大的减少图片的体积,一般图片用 60%级别比较合适,如果选择大于 75%的压缩等级,则会使图片有明显的质量下降。
  • 无兼容性问题,所以开发者可以放心随意使用。

使用场景:

  • JPG 适用于呈现色彩丰富的图片,在我们日常开发中,JPEG 图片经常作为大的背景图轮播图Banner 图出现。
    但是有损压缩后的图片确实很难露出马脚,当它处理矢量图形和 Logo 等线条感较强、颜色对比强烈的图像时,人为压缩的图片模糊会相当明显。
  • JPEG 图像不支持透明度处理,透明图片可选择使用 PNG。

1.9.2 PNG

  • PNG(可移植网络图形格式)是由 W3C 开发的图片格式,是一种无损压缩高保真的图片格式。
  • PNG 图片具有比 JPEG 更强的色彩表现力,对线条的处理更加细腻,对透明度有良好的支持。唯一的缺点就是体积太大

应用场景:

  • PNG 在处理线条和颜色对比度方面的优势,我们主要用它来呈现小的 Logo颜色简单且对比强烈的图片或背景等。
  • 支持透明度处理,透明图片可选择使用 PNG

1.9.3 GIF

  • GIF 是一种最多支持 256 种颜色8 位无损图片格式。
  • 支持 256 中颜色,文件体积通常都很小
  • 支持透明

应用场景:

  • 支持动画,适合去展示一些无限循环的动画,比如图标、表情、广告栏等。
  • 对于一些只有简单色彩的图片非常合适。

1.9.4 WebP

  • WebP 是一种同时提供了有损压缩无损压缩(可逆压缩)的图片文件格式,派生自影像编码格式 VP8。
  • 它像 JPEG 一样对细节丰富的图片信手拈来,像 PNG 一样支持透明,像 GIF 一样可以显示动态图片,集多种图片文件格式的优点于一身。
  • 虽然 webP 有诸多优点,但是它不能完全替代 JPEG 和 PNG,因为浏览器对 WebP 支持并不普遍。特别是移动端 IOS 系统基本不支持

2. HTTP请求优化

2.1 减少HTTP请求次数

  • 一个完整的 HTTP 请求需要经历 DNS 查找,TCP 握手,浏览器发出 HTTP 请求,服务器接收请求,服务器处理请求并发回响应,浏览器接收响应等过程。
  • 减少HTTP请求次数,可以减少HTTP请求多余的浪费

2.2 使用 HTTP2

  • 解析速度快:服务器解析 HTTP1.1 的请求时,必须不断地读入字节,直到遇到分隔符 CRLF 为止。而解析 HTTP2 的请求就不用这么麻烦,因为 HTTP2 是基于帧的协议,每个帧都有表示帧长度的字段。
  • 多路复用:在 HTTP2 上,多个请求可以共用一个 TCP 连接,这称为多路复用。
  • 首部压缩:HTTP/2 在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键-值对,对于相同的数据,不再通过每次请求和响应发送。
  • 优先级处理:HTTP2 可以对比较紧急的请求设置一个较高的优先级,服务器在收到这样的请求后,可以优先处理。
  • 流量控制:由于一个 TCP 连接流量带宽(根据客户端到服务器的网络带宽而定)是固定的,当有多个请求并发时,一个请求占的流量多,另一个请求占的流量就会少。流量控制可以对不同的流的流量进行精确控制。
  • 服务器推送:HTTP2 新增的一个强大的新功能,就是服务器可以对一个客户端请求发送多个响应。

2.3 使用服务端渲染

服务端渲染:服务端返回 HTML 文件,客户端只需解析 HTML。

优点:首屏渲染快,SEO 好。
缺点:配置麻烦,增加了服务器的计算压力。

3. 减少重绘重排

3.1 浏览器渲染过程

  1. 解析HTML生成DOM树
  2. 解析CSS生成CSSOM规则树
  3. 解析JS,操作 DOM 树CSSOM 规则树
  4. 将DOM树与CSSOM规则树合并在一起生成渲染树
  5. 遍历渲染树开始布局,计算每个节点的位置大小信息。
  6. 浏览器将所有图层的数据发送给GPU,GPU将图层合成并显示在屏幕上。
    在这里插入图片描述

3.2 重绘重排

  • 重排:当改变 DOM 元素位置或大小时,会导致浏览器重新生成渲染树,这个过程叫重排
  • 重绘:当重新生成渲染树后,就要将渲染树每个节点绘制到屏幕,这个过程叫重绘。
  • 不是所有的动作都会导致重排,例如改变字体颜色,只会导致重绘。记住,重排会导致重绘,重绘不会导致重排
  • 重排和重绘这两个操作都是非常昂贵的,因为 JavaScript 引擎线程与 GUI 渲染线程是互斥,它们同时只能一个在工作

什么操作会导致重排?

  • 添加或删除可见的 DOM 元素
  • 元素位置改变
  • 元素尺寸改变
  • 内容改变
  • 浏览器窗口尺寸改变

如何减少重排重绘?

  • JavaScript 修改样式时,最好不要直接写样式,而是替换 class 来改变样式。
  • 如果要对 DOM 元素执行一系列操作,可以将 DOM 元素脱离文档流,修改完成后,再将它带回文档。推荐使用隐藏元素(display:none)或文档碎片(DocumentFragement),都能很好的实现这个方案。

4. 使用事件委托

事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。所有用到按钮的事件(多数鼠标事件和键盘事件)都适合采用事件委托技术, 使用事件委托可以节省内存。

<ul>
  <li>苹果</li>
  <li>香蕉</li>
  <li>凤梨</li>
</ul>

// good
document.querySelector('ul').onclick = (event) => {
  const target = event.target
  if (target.nodeName === 'LI') {
    console.log(target.innerHTML)
  }
}

// bad
document.querySelectorAll('li').forEach((e) => {
  e.onclick = function() {
    console.log(this.innerHTML)
  }
}) 

5 CDN优化加载速度

CDN (Content Delivery Network),即内容分发网络。CDN 是构建在网络之上的内容分发网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。CDN 的关键技术主要有内容存储分发技术

前端项目中使用CDN好处:

  1. 减少应用打包出来的包体积
  2. 加快静态资源的访问-cdn服务器集群-就近返回
  3. 利用浏览器缓存,不会变动的文件长期缓存

使用CDN可以适当提高第三方插件的链接速度, 但是开发的时候我们还是可以在本地用npm下载, 但是上线后要配置cdn地址, 用户的浏览器默认还有缓存功能

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值