# 前端如何优化网站性能

# 常规优化

  1. 减少HTTP请求数量
  • 使用CSS雪碧图,减少图片总字节数
  • 合并CSSJS文件
  • 采用懒加载
  1. 利用浏览器缓存
  2. 减少重排

重排是DOM的变化影响到了元素的几何属性(宽和高),浏览器会重新计算元素的几何属性,会使渲染树中受到影响的部分失效,浏览器会验证 DOM 树上的所有其它结点的visibility属性,这也是重排低效的原因。如果重排的过于频繁,CPU使用率就会急剧上升。减少重排,如果需要在DOM操作时添加样式,尽量使用增加class属性,而不是通过style操作样式。

  1. 减少Dom操作
  2. 图标使用IconFont替换
  3. 避免head标签JS堵塞

所有放在head标签里的cssjs都会堵塞渲染。如果这些CSSJS需要加载和解析很久的话,那么页面就空白了。

  1. 减少head标签里的CSS资源

由于CSS必须要放在head标签里面,如果放在body里面,一旦加载好之后,又会对layout好的dom进行重排,样式可能又会发生闪烁。但是一旦放在head标签里面又会堵塞页面渲染,若要加载很久,页面就会保持空白状态。所以要尽可能地减少CSS的代码量。

TIP

有两种解决办法,第一种是把script放到body后面,这也是很多网站采取的方法。第二种是给scriptdefer的属性,deferhtml5新增的属性。一旦scriptdefer延迟的,那么这个script将会异步加载,但不会马上执行,会在readystatechange变为Interactive后按顺序依次执行

  1. 不要放太多base64CSS里面

放太多base64放在CSS里面,会导致CSS极速膨胀,把一张3k的图片转成base64,体积将会变成4k

  1. 自适应DPR加载图片

在高分辨率显示屏如 2x 上,在页面中使用二倍图可以保证清晰度,但是当此页面在低 DPR 设备打开时,我们只需要 50% 长宽的图片就能保证显示效果,而此时带宽开销却是一样的。所以为了节约传输流量,我们需要告诉浏览器,根于不同的 DPR加载不同尺寸的图片,通常有以下三种方法:

<!-- 第一种 -->
<picture>  
  <source srcset="photo@3x.jpg" media="(min-width: 800px)">  
  <source srcset="photo@2x.jpg" media="(min-width: 600px)">  
  <img srcset="photo.jpg">  
</picture>

<!-- 第二种 -->
<img src="photo.png" srcset="photo@2x.png 2x, photo@3x.png 3x" alt="photo" />
1
2
3
4
5
6
7
8
9
/* 第三种 */
background-image: image-set("photo.png" 1x,
                            "photo@2x.png" 2x,
                            "photo@3x.png" 3x);
1
2
3
4

TIP

像素相关概念:

  • DP 设备像素,又名物理像素px,即设备屏幕上真实的物理像素,以矩阵的形式排列,如 iphone X 屏幕分辨率为 2436*1125,即屏幕每行包含 1125 个物理像素,每列包含 2436 个物理像素。
  • DIP 设备无关像素,是一种基于屏幕坐标的抽象像素,应用程序以抽象像素为单位,如我们 CSS 中使用的 px,实际渲染时通过底层程序转换为物理像素。
  • DPR 设备像素比,设备像素 / 设备无关像素的值即为设备像素比,在 Javascript 中可以通过 window.devicePixelRatio 来获取。

# vue项目中的性能优化

  1. v-if/v-show

v-if 是 真正 的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。v-show 就简单得多, 不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSSdisplay 属性进行切换。所以,v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show 则适用于需要非常频繁切换条件的场景。

  1. v-for避免使用v-if

v-forv-if 优先级高,如果每一次都需要遍历整个数组,将会影响速度,尤其是当之需要渲染很小一部分的时候,必要情况下应该替换成 computed 属性。

  1. 长列表性能优化

Vue 会通过 Object.defineProperty 对数据进行劫持,来实现视图响应数据的变化,然而有些时候我们的组件就是纯粹的数据展示,不会有任何改变,我们就不需要 Vue 来劫持我们的数据,在大量数据展示的情况下,这能够很明显的减少组件初始化的时间,那如何禁止 Vue 劫持我们的数据呢?可以通过 Object.freeze 方法来冻结一个对象,一旦被冻结的对象就再也不能被修改了。

  1. 事件的销毁

Vue 组件销毁时,会自动清理它与其它实例的连接,解绑它的全部指令及事件监听器,但是仅限于组件本身的事件。如果在 js 内使用 addEventListener 等方式是不会自动销毁的,我们需要在组件销毁时手动移除这些事件的监听,以免造成内存泄露

beforeDestroy() {
    removeEventListener('click', this.click, false)
}
1
2
3
  1. 图片懒加载

对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载。这样对于页面加载性能上会有很大的提升,也提高了用户体验。我们在项目中使用 Vuevue-lazyload 插件

  1. 路由懒加载

Vue 是单页面应用,可能会有很多的路由引入 ,这样使用 webpcak 打包后的文件很大,当进入首页时,加载的资源过多,页面会出现白屏的情况,不利于用户体验。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件,这样就更加高效了。这样会大大提高首屏显示的速度,但是可能其他的页面的速度就会降下来。

TIP

App.vue组件中使用公用头部,如果使用了懒加载,那么某个不需要该公用组件的路由,刷新就会短暂显示头部组件

  1. 第三方插件按需引入 babel-plugin-component

  2. 提取公共代码 如果项目中没有去将每个页面的第三方库和公共模块提取出来,则项目会存在以下问题:

  • 相同的资源被重复加载,浪费用户的流量和服务器的成本。
  • 每个页面需要加载的资源太大,导致网页首屏加载缓慢,影响用户体验。 所以我们需要将多个页面的公共代码抽离成单独的文件,来优化以上问题 。Webpack 内置了专门用于提取多个Chunk 中的公共部分的插件 CommonsChunkPlugin
  1. 去除多余css--库purgecss
  2. 动态组件记得使用keep-alive

# SPA首屏加载速度慢如何解决

img

  • 利用DOMContentLoad/performance计算首屏时间
// 方案一:
document.addEventListener('DOMContentLoaded', (event) => {
    console.log('first contentful painting');
});
// 方案二:
performance.getEntriesByName("first-contentful-paint")[0].startTime

// performance.getEntriesByName("first-contentful-paint")[0]
// 会返回一个 PerformancePaintTiming的实例,结构如下:
{
  name: "first-contentful-paint",
  entryType: "paint",
  startTime: 507.80000002123415,
  duration: 0,
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • 加载慢的原因

    • 网络延时问题
    • 资源文件体积是否过大
    • 资源是否重复发送请求去加载
    • 加载脚本的时候,渲染内容堵塞
  • 解决方案

    • 减小入口文件体积
    • 静态资源本地缓存
    • UI框架按需加载
    • 图片资源的压缩
    • 组件重复打包
    • 开启GZip压缩
    • 使用SSR

TIP

# 减少入口文件体积

常用的手段是路由懒加载,把不同路由对应的组件分割成不同的代码块,待路由被请求的时候会单独打包路由,使得入口文件变小,加载速度大大增加

# 静态资源本地缓存

后端返回资源:

  • 采用HTTP缓存,设置Cache-Control,Last-Modified,Etag等响应头
  • 采用Service Worker离线缓存

前端合理使用localStorage

# 插件按需加载

# 开启GZip压缩

npm i compression-webpack-plugin -D
1

vue.config.js中引入并修改webpack配置

const CompressionPlugin = require('compression-webpack-plugin')

configureWebpack: (config) => {
    if (process.env.NODE_ENV === 'production') {
    // 为生产环境修改配置...
        config.mode = 'production'
        return {
            plugins: [new CompressionPlugin({
                test: /\.js$|\.html$|\.css/, //匹配文件名
                threshold: 10240, //对超过10k的数据进行压缩
                deleteOriginalAssets: false //是否删除原文件
            })]
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

全面的首屏优化方案 img