手机版 欢迎访问某某自媒体运营网(www.baidu.com)网站

当前位置: 主页 > 分析

前端性能优化之网络优化

时间:2024-05-06 04:53|来源:网络|作者:佚名|点击:

上篇文章提到了性能监控,那么有了监控之后就可以谈谈如何进行优化了。

前端的优化可谓是种类繁多,很难权衡。其主要包含三个方面的优化:

网络优化(对加载时所消耗的网络资源优化)。

  1. 加载顺序
  2. CDN优化
  3. 服务端渲染
  4. 懒加载
  5. PWA

代码优化(资源加载完后,脚本解释执行的速度)。

  1. 业务逻辑
  2. 代码逻辑
  3. CSS

框架优化(选择性能较好的框架,比如benchmark)。

这些优化技巧中,较为容易掌握,也较为容易产生效果的就是对网络的优化。相较之下,对代码的优化较为困难,也难以立竿见影。切换框架就更不必说了,想必很少有团队会因为性能切换技术栈。

本篇文章将聚焦于网络层的优化,并介绍其原则和方法。

首先,我们需要明确一个问题,网络优化到底优化的是什么?是用户的网络带宽?是查询DNS的速度?对于前端开发来说,都不是。对于上面的两个问题,前端开发是无法解决的。我们关注的是能够解决的问题——优化资源在浏览器中加载的时序。

对于不同的前端页面而言,资源加载的时序可能大不相同。大家可以使用chrome的开发栏->audit->generate report来测一下。这里举个例子:

页面加载时序图

上面这个是一个性能比较好的网页,可以看到这个时序图也非常的整齐。那么,如何根据这个时序图分析一个网页可能存在性能问题呢?一般而言,存在性能问题的页面存在几个典型特征:

  1. 并行性差,同时加载的资源较少,很多资源是一个串行的方式进行加载。
  2. 某些资源加载时间超长,远远超出其它资源的加载时长。
  3. 单一堵塞,存在某个资源堵塞其他资源加载的情况。

如果你们项目的时序图出现了这一种或几种特征,那么,你或许可以开始考虑优化一下了。下面,我将提供一些技巧,来优化这个问题。

浏览器一般会在解析完HTML时开始加载script和style,其顺序取决于资源在HTML中引用的顺序及方式(大体可分为同步或异步)。对于chrome来说,它可以在初始的时候同时加载6个相同域名的资源,所以,对于绝大多数页面,我们可以这样分配:

  • 1个css样式资源(或2个)
  • 1个vendor脚本资源(或拆分2个)
  • 1个公有脚本资源(对于多页面应用而言)
  • 1个当前页面入口所需的脚本资源

为了达到这样的效果,我们可以使用webpack的splitChunks的功能。具体配置项可以参考文档,这里给出一个创建vendor和common资源的样例:

{
  chunks: 'all',
  cacheGroups: {
    commons: {
      name: 'common',
      chunks: 'all',
      priority: 10,
      minChunks: 3
    },
    vendors: {
      name: 'vendor',
      chunks: 'all',
      test: /node_modules/,
      priority: 20,
      minChunks: 2,
      enforce: true
    },
    default: false
  }
}

在webpack中,一个chunk基本上可以理解为一个被import进来的代码块。那上面的这些配置,是用来告诉webpack,不要使用默认的分组机制(default: false)。首先(根据priority来定),抽取vendor组,其规则是,路径中包含node_modules,并且至少被引入2次及以上。其次,抽取common组,其规则是,所有代码中被引入3次及以上。

这些数值并没有一个特别好的选择,这就跟机器学习的调参一样,需要多尝试,选择一个比较均衡的值。这个值可以让vendor、common和入口js这三者达到一个比较平均的状态,这样,浏览器在加载资源的时候可以达到最优。

有的读者可能要问了,我们项目还有一些统计相关的代码,这些代码需要在一开始就被浏览器加载,并执行一些统计相关的工作,这个该怎么优化呢?下面,我将在代码内联一节给出解决办法。在此之前,我们先看一下如何优化不同域名下的网络连接性能。

如今的前端项目会访问很多资源,这些资源通常不在同一域名下,比如:

  1. 公有CDN资源,比如unpkg
  2. 统计资源,比如ga等。
  3. 广告资源,比如google adv等。
  4. 静态图片服务器,比如各种static content service。

浏览器往往需要解释到对应代码或执行到相关逻辑的时候才会建立与这些资源的链接,而建立这些链接(握手)需要消耗几十或几百毫秒。为了优化这些性能,浏览器提供了resource hits相关的标签,这些标签可以告诉浏览器一些具体的页面使用资源的情况,以方便浏览器提前加载或连接这些资源,主要包括这5种:

  1. preload 预加载资源(会影响其他资源的加载)
  2. link prefetch 预加载资源(会在浏览器idle时进行)
  3. dns prefetch 提前进行DNS查询
  4. preconnect 提前建立连接,包括DNS查询、TLS交换、TCP握手
  5. prerender 提前渲染

其中,dns prefetch和preconnect比较常用。那我们怎样将其应用起来呢?读者可能会想到一个比较直接的方式,那就是直接写在html中。虽然这个方法可以,但它难以复制和维护。我的建议是将其定义在配置文件中,并通过webpack的define plugin将其注入至某个js中,并在适当的时机执行这段js以便获得理想的效果。

那么,这类js代码肯定是越早执行,浏览器就能越早地对网页的网络特征进行提前优化。所以,最好的方式是将这段js内联至html中。下面将简单介绍如何内联代码及其原则。

由于现在很多前端开发项目都会选用Vue或React等前端框架,这些框架使用后会导致JS膨胀,而html文件萎缩。因此,如果能够将某些js片段内联至html,页面的整体性能会有一定提升。

通常,我们会选择一些需要提前加载的js进行内联,这些代码包括:

  1. 统计相关代码,需要提前加载并开始统计页面性能。
  2. 优化相关代码,需要提前加载,以便及早开始优化。

对于这些代码,我们可将其单独放置在一个webpack的chunk中,并使用html-webpack-inline-source-plugin将其内联至HTML中。

这里需要用到两个webpack的插件:

  1. html-webpack-plugin 该插件负责将代码注入至html中,通常注入方式为script、link标签的src属性。
  2. html-webpack-inline-source-plugin 该插件负责将匹配到的代码直接注入至script或link标签的body中。

具体配置可查阅相关文档,这里不再赘述。下面,我们将简单介绍一下对样式(CSS)的处理方式。

要说明白提取,我们得先简单回顾一下现存的3种建立css和js桥梁的方式:

  1. 直接使用classname,最为原始,也最为有效,问题是难以避免命名重叠,容易引起命名焦虑。
  2. 使用css module,将css中的命名转为js的命名,且可以随机生成,避免命名重叠。
  3. 放弃命名,直接将css写在js中。

这3种方式中,1和2是可以提取的,3是无法提取的。其中,1在提取的过程中可能会产生顺序的问题,从而影响最终的页面呈现效果。而2,因为其能够避免命名重叠,所以,提取后不会破坏页面效果。

那么,为什么我们要纠结于提取css呢?主要有两点原因:

  1. 减少js的体积,并提高网络加载时的并发度,以优化性能。
  2. 提高浏览器渲染时的性能。

对于第1条,是无需多解释的。对于第二条,浏览器在读取到CSS后,就会开始构建样式树,当解析到对应DOM后,会根据提前构建好的样式树进行匹配,从而渲染视图。而css in js需要每次都刷新样式树,从而影响性能。这里有个benchmark,大家可以参考一下。据此,笔者大胆推断,未来前端的最佳实践应为2,也就是css module。

说完样式提取,我们将进入最后一个技巧。该技巧适用于一些资源比较紧张的公司,而对于一些可以财大气粗,可自行购买CDN服务的公司则不适用。

unpkg是一个提供npm包进行CDN加速的站点,因此,可以将一些比较固定了依赖写入html模版中,从而提高网页的性能。

首先,需要将这些依赖声明为external,以便webpack打包时不从node_modules中加载这些资源,配置如下:

externals: { 'react': 'React' }

其次,你需要将所依赖的资源写在html模版中,这一步需要用到html-webpack-plugin。下面是一段示例:

<% if (htmlWebpackPlugin.options.node_env === 'development') { %>
  <script src="https://unpkg.com/react@16.7.0/umd/react.development.js"></script>
<% } else { %>
  <script src="https://unpkg.com/react@16.7.0/umd/react.production.min.js"></script>
<% } %>

这段代码需要注入node_env,以便在开发的时候能够获得更友好的错误提示。当然,读者也可以选择一些比较自动的库,来帮助我们完成这个过程,比如webpack-cdn-plugin,或者dynamic-cdn-webpack-plugin

性能优化是个很深远的话题,这里只列举其中一小部分。某些优化技巧可能会导致一些复杂的代码,希望大家能够根据自己的团队,进行一些选择,在得到良好性能的同时,保持工程代码的简洁。

Copyright © 2002-2022 首页-雷神娱乐电商新闻发布站 版权所有

平台注册入口