Skip to content

前端性能优化主要是为了提高页面的加载速度,优化用户的访问体验。我认为可以从这些方面来进行优化。 第一个方面是页面的内容方面 (1)通过文件合并、css 雪碧图、使用 base64 等方式来减少 HTTP 请求数,避免过多的请求造成等待的情况。

(2)通过 DNS 缓存等机制来减少 DNS 的查询次数。

(3)通过设置缓存策略,对常用不变的资源进行缓存。

(4)使用延迟加载的方式,来减少页面首屏加载时需要请求的资源。延迟加载的资源当用户需要访问时,再去请求加载。

(5)通过用户行为,对某些资源使用预加载的方式,来提高用户需要访问资源时的响应速度。

第二个方面是服务器方面 (1)使用 CDN 服务,来提高用户对于资源请求时的响应速度。

(2)服务器端启用 Gzip、Deflate 等方式对于传输的资源进行压缩,减小文件的体积。

(3)尽可能减小 cookie 的大小,并且通过将静态资源分配到其他域名下,来避免对静态资源请求时携带不必要的 cookie

第三个方面是 CSS 和 JavaScript 方面 (1)把样式表放在页面的 head 标签中,减少页面的首次渲染的时间。

(2)避免使用 @import 标签。

(3)尽量把 js 脚本放在页面底部或者使用 defer 或 async 属性,避免脚本的加载和执行阻塞页面的渲染。

(4)通过对 JavaScript 和 CSS 的文件进行压缩,来减小文件的体积。

加载阶段

首页加载图片过多的问题,可以通过以下几种方法解决:

  1. 通过懒加载的方式处理非首屏的图片
  2. 对于小图标可以采用 iconfont 的方式解决
  3. 对于小图片可以采用雪碧图的方式解决

首页的请求量过多,可以通过一些手段来减少资源的请求量,比如:

  1. 通过 nginx 服务器来做资源文件的合并或者通过 Webpack 等打包工具进行物理打包
  2. 在代码层面,对于需要引入一些大型第三方库的时候,可以通过特定的 Babel 插件来进行按需加载
  3. 还有可以使用前端路由懒加载,从而可以减少首页的 JS 和 CSS 的大小

优化图片的做法

熊猫站:智能压缩 PNG 和 JPG 的一个网站

图片的优化,也是从两个方面来考虑:太多 和 太大。

  • 可以通过懒加载减少图片的请求,或者通过雪碧图来合并图片,以及将小图转化成 base64 的格式,来解决多的问题。
  • 图片大的问题,可以通过自动化压缩工具来压缩图片,如熊猫站。或者使用 WebP 格式的图片。

Webpack 打包优化,也是从两个方面来考虑:太多 和 太大。

  • 可以通过设置 mode = production 来默认实现 Webpack 对代码的混淆和压缩,从而最大程度的减少代码体积
  • 使用 Webpack + dynamic import 并结合路由的入口文件做拆包处理。
  • 并且可以设定一些打包策略,并配合网络缓存做最终的加载性能优化。

实现 CDN 加速

CDN 服务器主要是用来放静态资源的服务器,可以用来加速静态资源的下载 CDN 之所以能够加速,是因为会在很多地方都部署 CDN 服务器,如果用户需要下载静态资源,会自动选择最近的节点下载 同时由于 CDN 服务器的地址一般都跟主服务器的地址不同,所以可以破除浏览器对同一个域名发送请求的限制

运行阶段

渲染十万条数据如何不造成卡顿

  1. 无论是浏览器中的 DOM 和 BOM,还是 NodeJS,它们都是基于 JavaScript 引擎之上开发出来的
  2. DOM 和 BOM 的处理最终都是要被转换成 JavaScript 引擎能够处理的数据
  3. 这个转换的过程很耗时
  4. 所以在浏览器中最消耗性能的就是操作 DOM

优化渲染很多数据的情况:尽可能的减少 DOM 的操作

eg:假如有一个需求,我们要在一个页面中 ul 标签里渲染 十万 个 li 标签。

jsx
// 插入十万条数据
const total = 100000;
let ul = document.querySelector('ul'); // 拿到 ul

// 懒加载的思路 -- 分段渲染
// 1. 一次渲染一屏的量
const once = 20;
// 2. 全部渲染完需要多少次,循环的时候要用
const loopCount = total / once;
// 3. 已经渲染了多少次
let countHasRender = 0;

function add() {
  // 创建虚拟节点,(使用 createDocumentFragment 不会触发渲染)
  const fragment = document.createDocumentFragment();
  // 循环 20 次
  for (let i = 0; i < once; i++) {
    const li = document.createElement('li');
    li.innerText = Math.floor(Math.random() * total);
    fragment.appendChild(li);
  }
  // 最后把虚拟节点 append 到 ul 上
  ul.appendChild(fragment);
  // 4. 已渲染的次数 + 1
  countHasRender += 1;
  loop();
}

// 最重要的部分来了
function loop() {
  // 5. 如果还没渲染完,那么就使用 requestAnimationFrame 来继续渲染
  if (countHasRender < loopCount) {
    // requestAnimationFrame 叫做逐帧渲染
    // 类似于 setTimeout(add, 16);
    // 帧:一秒钟播放多少张图片,一秒钟播放的图片越多,动画就约流畅
    // 1000/60 = 16
    window.requestAnimationFrame(add);
  }
}
loop();
// 插入十万条数据
const total = 100000;
let ul = document.querySelector('ul'); // 拿到 ul

// 懒加载的思路 -- 分段渲染
// 1. 一次渲染一屏的量
const once = 20;
// 2. 全部渲染完需要多少次,循环的时候要用
const loopCount = total / once;
// 3. 已经渲染了多少次
let countHasRender = 0;

function add() {
  // 创建虚拟节点,(使用 createDocumentFragment 不会触发渲染)
  const fragment = document.createDocumentFragment();
  // 循环 20 次
  for (let i = 0; i < once; i++) {
    const li = document.createElement('li');
    li.innerText = Math.floor(Math.random() * total);
    fragment.appendChild(li);
  }
  // 最后把虚拟节点 append 到 ul 上
  ul.appendChild(fragment);
  // 4. 已渲染的次数 + 1
  countHasRender += 1;
  loop();
}

// 最重要的部分来了
function loop() {
  // 5. 如果还没渲染完,那么就使用 requestAnimationFrame 来继续渲染
  if (countHasRender < loopCount) {
    // requestAnimationFrame 叫做逐帧渲染
    // 类似于 setTimeout(add, 16);
    // 帧:一秒钟播放多少张图片,一秒钟播放的图片越多,动画就约流畅
    // 1000/60 = 16
    window.requestAnimationFrame(add);
  }
}
loop();

结论:

  1. 可以使用 document.createDocumentFragment 创建虚拟节点,从而避免引起没有必要的渲染
  2. 当所有的 li 都创建完毕后,一次性把虚拟节点里的 li 标签全部渲染出来
  3. 可以采取分段渲染的方式,比如一次只渲染一屏的数据
  4. 最后使用 window.requestAnimationFrame 来逐帧渲染

导致浏览器卡顿的原因一般都是操作 DOM 的次数太频繁。 如果想要渲染很多条数据不造成卡顿,那么就一定要尽可能的减少操作 DOM 的次数。 比方说 React 的虚拟 DOM,本质上就是用 JS 数据来模拟真实 DOM 树,从而大大减少了操作真是 DOM 的次数。 还有在渲染的时候,可以使用 document.createDocumentFragment 创建虚拟节点,从而避免引起没有必要的渲染 也可以采取分段渲染的方式,最后使用 window.requestAnimationFrame 来逐帧渲染

渲染优化

  • 禁止使用 iframe(阻塞父文档 onload 事件)
  • 使用 CSS3 代码代替 JS 动画(尽可能避免重绘重排以及回流)
  • 对于一些小图标,可以使用 base64 位编码,以减少网络请求。但不建议大图使用,比较耗费 CPU
  • 小图标优势在于
    • 减少 HTTP 请求
    • 避免文件跨域
    • 修改及时生效
  • innerHTML 代替 DOM 操作,减少 DOM 操作次数,优化 javascript 性能
  • 当需要设置的样式很多时设置 className 而不是直接操作 style
  • 图片预加载,将样式表放在顶部,将脚本放在底部 加上时间戳

性能优化总结

在前端中性能优化的点主要分为两个阶段:

  1. 初始阶段,主要就是加载方面优化的问题。所有问题的指导原则就两点:
    • 尽可能的减少前端资源的数量
    • 尽可能的减小前端资源的大小
  2. 运行阶段,主要就是渲染方面优化的问题。只要是在浏览器中,所有的问题的指导原则就是:
    • 尽可能的减少操作 DOM
AI助手