前端知识点之性能优化

87 阅读9分钟

性能优化

在谈性能优化的时候,首先要知道项目出现了什么问题,有什么样的解决方法。性能优化可以分为以下几个方面。

一 减少打包文件的体积

  1. Tree Shaking:使用Tree Shaking的方式引入外部第三方的模块或者自定义功能函数,使用哪一部分的代码就引入哪一部分的方法。举个例子🎂:
export const sum = (num1, num2) => {
    return num1 + num2;
};
export const sub = (num1, num2) => {
    return num1 - num2;
};

如果只引用了sum函数,那么在打包时只会将sum函数进行打包。

  1. CDN引入:也可以使用CDN的方式引入第三方模块,CDN其实就是一个内容分发网络,可以快速的获取所要加载的资源。
  2. 进行图片压缩:小的图片可以使用base64的格式减少请求的数量,也可以使用精灵图或者WebP格式的图片减少图片的体积,或使用字体图标,图片的懒加载。
  3. 组件的懒加载与路由的懒加载:对于某些组件,采用动态引入的方式,按需加载,当需要加载的时候再加载,提高首屏的渲染效率。
  4. build时删除source-map文件,这个文件建立了原代码和打包代码之间的一种映射,方便调试代码,但是生产环境下并不需要。如果需要定位问题,可以采用从CDN引入的方式导入source-map文件。
  5. 删除log和注释。
  6. 在打包时进行代码拆包,提取公共模块。而不是将所有的文件都打包进一个js文件中,这样会导致js文件过大,增加首屏渲染时的白屏时间。
  7. 采用gzip压缩:gzip是一种高效的压缩算法,可以减少打包代码的体积。
  8. 抽离css,采用link标签引入的方式加载css。对css,html,js代码进行压缩。

二 从网络层面

  1. 减少请求的次数:对于某些图片可以做成精灵图,对与某些频繁的并且数据不咋发送变化的请求采用本地缓存。采用节流/防抖减少向服务器发送请求的次数。
  2. 采用HTTP2:HTTP2的新特性,包括头部压缩,多路复用,服务器端推送等新特性可以提高网络传输效率,提高页面的加载速率。
  3. 采用浏览器缓存。
  4. 对于某些即将使用的文件可以使用预加载,在浏览器空闲时加载该资源。
  5. 采用服务器端渲染:对于某些频繁更新的页面,可以采用服务器端渲染的方式提高页面渲染性能。

三 Dom层面

核心就是减少Dom的操作次数,最小化页面的回流和重绘。在这个方面,vue3已经优化的比较彻底了,包括diff算法,异步更新队列等。浏览器也对回流和重绘进行了一定的优化,浏览器有一个渲染队列,每隔一段时间或者队列的长度到达了一定的数量就取出队列中的部分操作进行页面的渲染,将多次的回流,重绘操作变成一次回流,重绘操作。但是还是有一些优化方法:

  1. 采用DocumentFragement:当涉及多次Dom操作时,可以采用DocumentFragement减少页面的回流次数。DocumentFragement也相当于一个标签,但它是在内存中的对页面的结构不会产生影响。当页面插入DocumentFragement元素时会把所有的子标签插入到页面中,将多次的Dom操作转化为一次的Dom操作。
  2. 对于某些动画,会改变元素的大小位置,可以使其脱离文档流。
  3. 使用事件委托:利用事件冒泡的原理将事件定义在父元素中,减少定义事件的数量,减轻内存压力。

四 项目中

在项目中就是一些比较小的优化项了(Vue):

  1. v-for中的key的使用:注意不要将数组的下标作为key。
  2. v-if和v-for的合理使用:在vue2中v-for的优先级高于v-if,可以在同一个标签上定义,但是不建议这么做,会造成大量的性能消耗。在vue3中,v-if的优先级高于v-for,不能在同一个标签上使用,如果有这种v-if和v-for的需求,可以再定义个父标签写v-for或者将判断的逻辑写道函数或计算属性中(推荐)。
  3. keep-alive缓存组件。
  4. 骨架屏:主要用于首页渲染优化,通过提供一个占位动画缓解用户等待页面加载的心理,目前再app上应用的比较多。
  5. 虚拟列表:当页面一次要加载100000条数据时,可以采用虚拟列表的方式,将所有要加载的数据保存在一个数组中,通过可视区域的变化再决定去渲染哪部分数据。还有一种方法是使用requestAnimationFragement(浏览器每刷新一次就会执行一次)+ DocumentFragement进行定时,在每个tick中只进行一部分的数据操作。
  6. 合理使用计算属性,计算属性与普通函数的区别是计算属性具有缓存。
  7. 如果一个页面有上百个请求,如何优化?在浏览器中,同一个域名最多一次性可以发送68个请求,可以使用一个缓冲队列(长度为68),当队列满时堵塞请求的发送,否则则发送请求。
  8. 使用webworker对长任务进行优化(长任务定义:执行时间超过50ms的任务),因为js解析线程与gui渲染线程是相互独立的,在进行js解析时会阻塞页面的渲染。举个例子🎂:
// 如果把该代码方式主线程中,会阻塞页面的渲染,但是如果放到webworker中单独开启一个线程,那么不会阻塞页面的渲染:
let count = 0;
for (let i = 0; i < Infinity; i++) {
    conut += Math.random();
}
console.log(count);

9.进行优先级渲染,像用户进行点击操作加载大型列表,可以使用setTimeout将加载数据操作放到下一个tick中,防止页面卡顿。

五 百万数据实时渲染

针对列表类型展示的数据:

  • 虚拟列表
  • requestAnimationFrame + DocumentFragment(虚拟文档片段)

针对图表类型的数据

  • web worker + 降采样
  • 增量渲染

六 Webpack基本配置及性能优化

(1)webpack基本配置

webpack本身的功能是很少的,在开发模式下,只能处理js的es module语法(因为webpack是基于Node的)。在生产模式下,可以处理js的es module语法以及对js代码进行压缩。

webpack的配置主要有五个部分:

  • entry:编译开始的入口文件;
  • output:配置出口文件目录;
  • module:配置loader;
  • plugins:配置插件;
  • mode:设置开发模式还是生产模式;

可以配置loader增加webpack对多种资源文件解析的支持:包括css,less,sass以及图片视频等资源;而plugins是用来扩展webpack的功能的。此外,在开发模式下还可以配置服务器以及反向代理服务器。

(2)webpack性能优化

webpack的性能优化主要包含两部分:一个就是提升打包的构建速度,一个就是减少打包代码的体积,优化代码的运行性能。

提升打包的构建速度:

  • 使用多进程打包,但是在代码体积小的时候不是很好,因为开启一个进程也需要额外的时间开销;
  • 使用HMR热模块更新,正常情况下,当一个文件改变了,webpack会对所有的文件重新进行打包编译,这样就太消耗时间了,开启hmr功能后只会对改变的文件进行编译而不编译所有的文件;
  • 使用缓存:像eslint,babel每次文件改变都是需要重新进行编译的,可以开启eslint和babel的缓存来保存eslint和babel的编译结果;
  • 对于引入的第三方模块是不需要进行重新编译的,可以使用exclude将第三方模块排除在外;

减少代码的体积,优化代码的运行性能:

  • 对image图片进行压缩,对于比较小的图片,可以将其转为base64的格式;
  • 对html(html-webpack-plugin),css进行压缩,对于css可以使用link引入的方式解决闪屏现象;
  • tree shaking:对于第三方的模块,可以使用按需引入的方式;
  • 使用gzip压缩;
  • 在生产模式下,删除log,注释以及source-map文件;
  • 使用路由懒加载的方式,用到哪个路由就去加载哪个路由;
  • 将代码进行分包,提取公共模块;
  • 浏览器缓存的问题:因为浏览器默认是开启缓存的,如果前后两次打包生成的文件名称相同,很可能走浏览器缓存而不去加载新的文件,这个时候就需要去配置contenthash(根据文件内容生成的hash值,每个文件都不同),此外,还有fullhash(所有的文件公用一个hash)和chunkhash(根据不同的打包入口生成不同的hash);

七 埋点数据上发方案及原因

方案:使用.gif图片(长宽为1*1)+携带参数的形式向后台发送埋点数据。 原因:

  • 跨域问题:埋点系统需要接收来自各个域名的请求,使用.gif图片的方式天然的解决了跨域问题(image,script,以及link标签支持天然的跨域)。
  • 防止阻塞页面的渲染:image标签只需要new一个image对象就可以发起请求,而不需要将image标签插入到Dom中,防止阻塞页面的渲染。
  • gif图片格式相比于jpg/png的体积较小,减少了流量的消耗。