浅谈前端的性能优化| 青训营

83 阅读10分钟

前言

    随着互联网的快速发展,用户对于网页加载速度和交互体验的要求也越来越高。我们作为前端开发者,必须时刻关注并努力提升前端页面的性能。

    性能优化不仅可以显著提升用户体验,还可以减少服务器负载、节省带宽成本,甚至对SEO(搜索引擎优化)也有积极的影响。一个高性能的网站能够吸引更多的用户,并提升用户留存率和转化率。
接下来就一起来深入了解一下前端的性能优化叭。

1.常见的性能优化的手段

  1. 减少请求
  2. 压缩资源
  3. 优化网络连接
  4. 优化资源加载
  5. 减少重绘回流
  6. 性能更好的API
  7. webpack性能优化
  8. 首屏加载优化

2.解析具体实现方法

2-1 减少请求数量

图片资源处理

  • 雪碧图
    使用场景:页面存在较多的小图标
    原理:将一堆小的图标放在一张大图片上,请求时只需要请求一张图片即可
  • Base64
    使用场景:图片较小且数量有限 / 邮件中图片
    原理:将图片转化为Base64格式编码,嵌入页面中,以减少网络请求
  • ICON 字体图标
    使用场景:非定制化的图标
    原理:icon字体图标一般都是TTF这种字体的格式,相较于正常图片较小,因此可以减少加载时间
  • webp 格式的图片
    原理:webp 是一种新的图片文件格式,在相同图片质量下,webp 的体积比 png 和 jpg 更小。

减少页面重定向

原理:页面发生重定向时,会延迟整个HTML文档的传输,导致出现白屏,影响用户体验
建议:如果真的需要重定向,尽量使用永久重定向301,这样可以减少重定向的次数,优化体验

使用页面缓存

使用场景

  • 静态内容:例如网站的logo、静态图片、CSS样式文件等
  • 频繁访问的页面:例如首页、商品列表页等,使用页面缓存来减少数据库查询和动态生成页面的次数
  • 缓慢变化的内容:例如文章内容、博客页面等,只有当内容更新或过期时,才重新生成并更新缓存。

原理:请求过的资源就先缓存下来,用的时候直接用,就不用再次请求,以减轻服务器的压力
建议:对于用户信息等,就不宜进行缓存了,以免造成用户信息的泄露,并且要适当设置缓存的过期时间,防止用户看不到新的内容

减少使用css外部导入

原理:使用css@import会造成额外的请求

避免使用空的src和href

原理:a和href标签设置为空,会重定向到当前页面的地址

2-2 减少资源大小

  • html压缩
    html代码压缩就是压缩在文本文件中有意义,但是在html中不显示的字符,包括空格,制表符
  • css压缩
    css压缩包括无效代码删除与css语义合并
  • js压缩与混乱
    js压缩与混乱包括无效字符及注释的删除、代码语义的缩减和优化、降低代码的可读性、实现代码的保护
  • 图片压缩
    100% 的质量和 90% 质量的通常看不出来区别,因此可以在保证清晰度的情况下压缩一下,以减小内存占用

2-3 优化网络连接

  • 使用CDN
    使用户可以就近的取得所需内容,解决网络拥挤的状况,提高网站的响应速度
  • 使用DNS预解析
    当浏览器访问某域名时,需要解析DNS,获得对应域名的ip地址,在解析过程中,会逐步读取缓存,直到拿到ip地址
  • 持久连接
    使用keep-alive或者persistent来建立持久连接,降低了延时和连接建立的开销

2-4 优化资源加载

  • 资源加载位置
    原理:通过优化资源加载位置,使页面快速加载出来(不阻塞页面)
    建议
  1. 引用资源时,先考虑外链,再考虑本地
  2. css文件放在head中,目的:早点渲染出页面样式
  3. js文件放在body底部,目的:防止js阻塞页面结构的渲染
  4. 处理页面相关布局的js文件放在head中,如flexible.js,目的:早点执行
  5. 合理的使用script标签的属性,如defer延迟加载,async异步加载,目的:防止JS脚本阻塞页面加载
  • 资源加载时机

1、script标签
defer:延迟加载,在html解析完成后再执行JS脚本,一定不会阻塞页面
async:异步加载,JS加载完成后立即执行脚本,有可能会阻塞页面

2、按需加载

    • 路由懒加载
      背景:项目build打包后,会将所有内容放到一起,因此在访问首页时,会加载所有的内容,导致加载慢,出现白屏等问题
      理解:按需加载,用到某个路由的页面时,再加载这个路由,相当于将一个总块分成若干个小块
      原理:先把代码在一些逻辑断点处分离开,一些代码块完成某些操作后,立即引用另外一些新的代码块。这样就加快了应用的初始加载速度,减轻了它的总体体积
      常见路由懒加载写法
      1. Vue异步组件
      2. ES6标准语法import()
      3. webpack的require,ensure()

Vue异步组件

<template>
  <div>
    <button @click="loadComponent">加载组件</button>
    <div v-if="showComponent">
      <async-component></async-component>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      showComponent: false
    };
  },
  methods: {
    loadComponent() {
      // 异步加载组件
      import('./AsyncComponent.vue')
        .then(component => {
          // 注册异步组件
          this.$options.components['AsyncComponent'] = component.default;
          this.showComponent = true;
        })
        .catch(error => {
          console.error('无法加载组件:', error);
        });
    }
  }
};
</script>

ES6标准语法import(推荐)

  route中配置
  {
      path: '/chat',
      name: 'chat',
      component: resolve => require(['../views/chat], resolve)
    }

webpack的require,ensure()

import Vue from 'vue';
import Router from 'vue-router';
const HelloWorld=resolve=>{
		require.ensure(['@/components/HelloWorld'],()=>{
			resolve(require('@/components/HelloWorld'))
		})
	}
Vue.use('Router')
export default new Router({
	routes:[{
	{path:'./',
	name:'HelloWorld',
	component:HelloWorld
	}
	}]
})
    • 图片懒加载
      背景:对于页面加载速度影响最大的因素之一就是图片资源,如果一个页面图片太多,光是大小就几十上百兆,用户要是加载都要好久,体验极差
      理解:在页面打开的时候,不要一次性全部显示页面所有的图片,而是只显示当前视口内的图片,按需加载,加载速度就会大大提升,用户体验也会更好
      原理:由于浏览器会自动对页面中的img标签的src属性发送请求并下载图片。
      因此,通过html5自定义属性 data-xxx先暂存src的值,然后在需要显示的时候,再将 data-xxx的值重新赋值到img的src属性即可。
      常见图片懒加载写法
      1. Intersection Observer实现
      2. Scroll 事件监听

Intersection Observer实现
原理:检测元素是否进入了浏览器视窗范围内。当图片元素进入可见区域时,再进行图片资源加载

<template>
  <div>
    <img
      v-if="isVisible"
      :src="imageSrc"
      @load="onImageLoad"
      alt="Lazy-loaded Image"
    >
  </div>
</template>

<script>
export default {
  data() {
    return {
      imageSrc: '', // 图片地址
      isVisible: false, // 图片是否可见
    };
  },
  mounted() {
    const observer = new IntersectionObserver((entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          this.isVisible = true;
          observer.unobserve(entry.target);
        }
      });
    });

    observer.observe(this.$el);
  },
  methods: {
    onImageLoad() {
      console.log('Image loaded');
    },
  },
};
</script>

Scroll 事件监听
原理:监听页面的滚动事件,在滚动过程中判断图片是否进入可见区域,并进行相应的加载处理

<template>
  <div>
    <img
      v-if="isVisible"
      :src="imageSrc"
      @load="onImageLoad"
      alt="Lazy-loaded Image"
    >
  </div>
</template>

<script>
export default {
  data() {
    return {
      imageSrc: '', // 图片地址
      isVisible: false, // 图片是否可见
    };
  },
  mounted() {
    this.addScrollListener();
  },
  methods: {
    addScrollListener() {
      window.addEventListener('scroll', this.checkVisibility);
    },
    checkVisibility() {
      const rect = this.$el.getBoundingClientRect();
      const windowHeight =
        window.innerHeight || document.documentElement.clientHeight;
  
      if (rect.top >= 0 && rect.bottom <= windowHeight) {
        this.isVisible = true;
        window.removeEventListener('scroll', this.checkVisibility);
      }
    },
    onImageLoad() {
      console.log('Image loaded');
    },
  },
};
</script>

3、使用预加载preload和预读取prefetch
preload让浏览器提前加载指定资源,需要执行时候再执行,可以加快当前页面的加载速度
prefetch告诉浏览器加载下一个页面可能会用到的资源,可以加速下一个页面的加载速度

2-5 减少重绘回流

  • 使用 CSS3 动画代替 JavaScript 动画,例如使用 transform 和 opacity 属性来实现动画效果;
  • 将频繁操作的元素从文档流中脱离,使用绝对定位或固定定位;
  • 批量修改样式,将样式操作集中到一起,避免多次修改;
  • 使用虚拟滚动来优化大量数据的列表展示,避免渲染过多的 DOM 元素;
  • 使用事件委托来减少事件监听器的数量。

2-6 性能更好的API

  1. Intersection Observer API:通过监听元素是否进入视窗区域,可以延迟加载图片、懒加载无限滚动列表等,避免不必要的资源加载。
  2. requestAnimationFrame API:该 API 可以优化动画效果,比使用 setTimeoutsetInterval 更加高效,它可以在浏览器下一次重绘之前执行动画更新操作,确保在刷新频率内实现平滑的动画效果。
  3. Performance API:提供了一系列方法和属性,用于测量页面的性能指标,如页面加载时间、资源加载时间等,可以用来分析和优化页面性能。
  4. Web Worker API:Web Worker 是在后台运行的 JavaScript 线程,可以优化大量计算密集型的任务,避免阻塞页面主线程,提升页面的响应性能。
  5. Cache API:可以利用浏览器缓存机制,将静态资源缓存起来,减少网络请求,提高页面加载速度。可结合 Service Worker 使用,实现离线访问和更高级的缓存策略。
  6. localStorage 和 sessionStorage:这两个 API 提供了在浏览器端存储数据的能力,可以用于缓存一些频繁使用的数据,减少网络请求。
  7. IndexedDB:一个浏览器内置的客户端数据库,可以在浏览器中存储大量结构化数据,用于高效地管理和操作大型数据集,适用于离线应用、缓存数据等场景。

2-7 webpack性能优化

  • 打包公共代码

使用optimization.splitChunks 和 optimization.runtimeChunk插件,将公共模块拆出来,最终合成的文件能够在最开始的时候加载一次,便存到缓存区中供后续使用,可以提升性能。

  • 删除死代码tree shaking

移除Javascript上下文中的未引用代码
JS主要通过uglify.js来实现
css主要通过purify.css来实现

  • 公共代码内联

使用html-webpack-inline-chunk-plugin插件将manifest.js内联到html文件中

  • 长缓存优化
  1. 设置 HTTP 缓存头:控制浏览器对资源的缓存行为
  2. 文件指纹:给文件名添加 hash 或版本号,使浏览器判定资源已更改,强制重新下载,避免使用旧版本的缓存文件
  3. 版本号管理:使用带有版本号的 URL 来加载静态资源,在资源更新时更新版本号,从而使浏览器获取到最新的版本,如 app.js?v=1.0.0

2-8 首屏加载优化

使用骨架屏或者动画优化用户体验

3.总结

    随着互联网的快速发展,用户对于网页加载速度和交互体验的要求也越来越高。我们作为前端开发者,必须时刻关注并努力提升前端页面的性能。我们要确保网页加载速度快,以提供更好的用户体验。平时我们在编程时就要有性能优化的思想在脑海中,这样就可以将这些思想不知不觉的融入我们的代码中。
下面梳理一下我们性能优化的思路:
    1.优化网络请求,合并和压缩 CSS 和 JavaScript 文件,采用雪碧图或者 SVG 图标,减少请求数量和资源大小。
    2.使用缓存来减少服务器请求,设置适当的缓存头和过期时间,确保浏览器可以缓存资源,并在需要时直接使用缓存,不用再向服务器发出请求。
    3.使用延迟加载和懒加载技术,在初次加载时,只加载可视区域内的内容,而不是一次性加载整个页面。这对于长页面或多媒体内容尤其有用,因为它不仅可以减少初始加载时间,还能提高用户对网站的滚动响应速度。
    4.使用异步加载脚本和样式表,将不影响页面显示的 JavaScript 代码放在 asyncdefer 属性中,来确保它们不会阻塞页面加载。对于样式表,我们也要将其放在 <link> 标签中,并使用 preloadprefetch 属性来提前获取资源。
    5.进行定期的性能测试和监测,帮助我们了解网站在不同情况下的性能表现,并找出需要改进的地方。


本文学习借鉴自CSDN博主「马小兜-」的原创文章
原文地址为 https://blog.csdn.net/weixin_43514149/article/details/107834647