前端项目优化-加载时优化

前端项目性能优化

性能优化大体可以分为两个方面

  • 加载时优化

  • 运行时优化

加载时优化

浏览器输入网址之后发生了什么

  1. DNS域名解析

    客户端收到域名地址后,首先去找本地的hosts文件,检查在该文件中是否有相应的域名、IP对应关系,如果有,则向其IP地址发送请求,如果没有,再去找DNS服务器。DNS采用的是UDP协议

  2. 建立TCP连接

    三次握手:请求连接(SYN数据包),确认信息(SYN/ACK数据包),握手结束(ACK数据包)

  3. 发送HTTP请求

    与服务器建立了连接后,就可以向服务器发起请求了。

  4. 服务器处理请求

    服务器端收到请求后的由web服务器(准确说应该是http服务器)处理请求。

  5. 返回响应结果

    在http里,有请求就会有响应,哪怕是错误信息。

    在响应结果中都会有个一个http状态码,如200、301、404、500等。通过这个状态码可以知道服务器端的处理是否正常,并能了解具体的错误。

  6. 关闭TCP连接

    为了避免服务器与客户端双方的资源占用和损耗,当双方没有请求或响应传递时,任意一方都可以发起关闭请求。四次挥手。

  7. 浏览器解析HTML

    浏览器通过解析HTML,生成DOM树,解析CSS,生成CSS规则树,然后通过DOM树和CSS规则树生成渲染树。

  8. 浏览器布局渲染

    根据渲染树布局,计算CSS样式,即每个节点在页面中的大小和位置等几何信息。

从中看到那些可以使用前端知识做优化的。

网络优化

http

超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上。也就是说发起http请求前,都要连接上TCP,进行TCP3次握手,结束后要进行TCP的4次挥手。一个完整的 HTTP 请求需要经历 DNS 查找,TCP 握手,浏览器发出 HTTP 请求,服务器接收请求,服务器处理请求并发回响应,浏览器接收响应等过程。接下来看一个具体的例子帮助理解 HTTP :

image-20220404223036148

这是一个 HTTP 请求,请求的文件大小为42.5KB。

名词解释:

  • Queueing: 在请求队列中的时间。
  • Stalled: 从TCP 连接建立完成,到真正可以传输数据之间的时间差,此时间包括代理协商时间。
  • Proxy negotiation: 与代理服务器连接进行协商所花费的时间。
  • DNS Lookup: 执行DNS查找所花费的时间,页面上的每个不同的域都需要进行DNS查找。
  • Initial Connection / Connecting: 建立连接所花费的时间,包括TCP握手/重试和协商SSL。
  • SSL: 完成SSL握手所花费的时间。
  • Request sent: 发出网络请求所花费的时间,通常为一毫秒的时间。
  • Waiting(TFFB): TFFB 是发出页面请求到接收到应答数据第一个字节的时间。
  • Content Download: 接收响应数据所花费的时间。

http1.0,http1.1以及http2.0的区别

  • http1.0,每次http请求都要建立一个TCP

  • http1.1,可以在一个TCP链接上可以传送多个http请求和响应,这样就不用多次建立和关闭TCP连接。但是多个http请求只能按顺序请求。

  • http2.0

    • 多路复用:一个连接上可以有多个http请求,且可以随机的混在一起

      20220404

    • header压缩: http1.x中的header需要携带大量信息.而且每次都要重复发送.http2.0使用encode来减少传输的header大小.而且客户端和服务端可以各自缓存(cache)一份header filed表,避免了header的重复传输,还可以减少传输的大小.

接下来对比一下http1.1和http2的

http1.1

image-20220405131235513

http2

image-20220405131516572

使用cdn

① 当用户点击应用程序上的内容时,应用程序将根据URL地址到本地 (域名解析系统)寻求IP地址解析。

② 本地DNS系统将域名解析给CDN专用DNS服务器。

③ CDN专用DNS服务器向用户返回CDN全局负载平衡设备的IP地址。

④ 用户向CDN负载平衡设备发起内容URL访问请求。

⑤ CDN负载平衡设备基于用户的IP地址和用户请求的内容URL在用户所属的区域中选择高速缓存服务器。

⑥ 负载平衡设备告诉用户缓存服务器的IP地址,并允许用户向选定的缓存服务器发起请求。

⑦ 用户向缓存服务器发起请求,缓存服务器响应用户的请求,并将用户所需的内容发送到用户终端。

⑧ 如果此缓存服务器没有用户需要的内容,则此缓存服务器将从网站的 请求内容。

⑨ 源服务器将内容返回到缓存服务器,缓存服务器发送给用户,并根据用户定义的缓存策略,确定是否在缓存服务器上缓存内容。

简单的说,就是DNS服务器给你分配一个最近最快的服务器IP地址,让你去获取资源

开启gzip

看一下开启前后的对比:

开启前

image-20220405142134701

开启后

image-20220405142013001

提高浏览器并发连接数

不同的浏览器对单个域名的最大并发TCP连接数有一定的限制(一般限制在4个左右,不同浏览器内核限制数量不一样),如果浏览器同时对某一域名发起多个请求,超过了限制就会出现等待。那么为了解决阻挡这一问题,可以对某些URL的域名分散处理。将不同的资源划分到不同的域名。

渲染优化

关键渲染路径优化

从收到 HTML、CSS 和 JavaScript 字节到对其进行必需的处理,从而将它们转变成渲染的像素这一过程中有一些中间步骤,优化性能其实就是了解这些步骤中发生了什么, 即关键渲染路径优化关键渲染路径是指优先显示与当前用户操作有关的内容。

做关键渲染路径优化需要掌握的知识点

  • 一个网页渲染的步骤

    1. 处理 HTML 标记并构建 DOM 树。
    2. 处理 CSS 标记并构建 CSSOM 树。
    3. 将 DOM 与 CSSOM 合并成一个渲染树。
    4. 根据渲染树来布局(Layout),以计算每个节点的几何信息。
    5. 将各个节点绘制到屏幕上。
  • preload和prefetch

    • preload提供了一种声明式的命令,让浏览器提前加载指定资源(加载后并不执行),需要执行时再执行

      好处在于:

      1、将加载和执行分离开,不阻塞渲染和document的onload事件

      2、提前加载指定资源,不再出现依赖的font字体隔了一段时间才刷出的情况

    • prefetch告诉浏览器加载下一页面可能会用到的资源,注意,是下一页面,而不是当前页面。因此该方法的加载优先级非常低,也就是说该方式的作用是加速下一个页面的加载速度。

      prefetch可以做DNS预解析:从域名查询IP的过程,这个过程一般都很快的,但也会引起延迟。一般浏览器会适当的对解析结果缓存,并对页面中出现的新域名进行预解析,但并不是所有的浏览器都会这么做,为了帮助其它浏览器对某些域名进行预解析,你可以在页面的html标签中添加dns-prefetch告诉浏览器对指定域名预解析

      <link rel="dns-prefetch" href="//domain.com">
      
  • async和defer

    • defer:延迟脚本。立即下载,但延迟执行(延迟到整个页面都解析完毕后再运行),按照脚本出现的先后顺序执行。
    • async:异步脚本。下载完立即执行,但不保证按照脚本出现的先后顺序执行。
  • 回流与重绘

    当渲染树中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候。在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树。完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程成为重绘

关键渲染路径性能分析

描述关键渲染路径的词汇:

  • 关键资源: 可能阻止网页首次渲染的资源。
  • 关键路径长度: 获取所有关键资源所需的往返次数或总时间。
  • 关键字节: 实现网页首次渲染所需的总字节数,它是所有关键资源传送文件大小的总和。我们包含单个 HTML 页面的第一个示例包含一项关键资源(HTML 文档);关键路径长度也与 1 次网络往返相等(假设文件较小),而总关键字节数正好是 HTML 文档本身的传送大小。

图片等资源的影响

<html>
<head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Critical Path: No Style</title>
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="123.webp"></div>
</body>
</html>

image-20220405153939757

DOMContentLoaded 事件的时间(5 毫秒),该时间同样与蓝色垂直线相符。HTML 下载结束与蓝色垂直线 (DOMContentLoaded) 之间的间隔是浏览器构建 DOM 树所花费的时间。

请注意,图片并没有阻止domContentLoaded 事件。这证明,并非所有资源都对首次绘制具有关键作用。事实上,关键渲染路径通常谈论的是 HTML 标记、CSS 和 JavaScript。

image

只需要DOM构建完成,就需要构建渲染树。

使用外部 CSS 文件

<html>
<head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Critical Path: No Style</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="123.webp"></div>
</body>
</html>

image-20220405163234426

html解析到link标签,所以style.css在蓝色线前就开始请求

image

这里需要HTML 和 CSS 来构建渲染树,所以渲染树构建在style.css请求返回并完成构建CSSOM后才开始。

使用外部的 Javascript 文件及 CSS 文件

<html>
<head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Critical Path: No Style</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
<p>Hello <span>web performance</span> students!</p>
<div><img src="123.webp"></div>
<script src="app.js"></script>
</body>
</html>

添加了 app.js,它既是网页上的外部 JavaScript 资源,又是一种解析器阻止(即关键)资源。更糟糕的是,为了执行 JavaScript 文件,我们还需要进行阻止并等待 CSSOM

image-20220405164413187

image

构建渲染树,需要等CSSOM构建完成后,同步的JS运行完毕后才开始构建。

优化方法

  • 灵活使用async和defer延迟解析 JavaScript

  • CSS 标记为非关键资源

    CSS 是构建渲染树的必备元素,首次构建网页时,JavaScript 常常受阻于 CSS。确保将任何非必需的 CSS 都标记为非关键资源(例如打印和其他媒体查询),并应确保尽可能减少关键 CSS 的数量,以及尽可能缩短传送时间。

  • 将 CSS 置于文档 head 标签内

    尽早在 HTML 文档内指定所有 CSS 资源,以便浏览器尽早发现 <link> 标记并尽早发出 CSS 请求。

image

webpack的分包策略、提高构建速度

分包策略

webpack 4默认使用optimization.splitChunks分包。默认的分割策略:

  • 新的 chunk 是否被共享或者是来自 node_modules 的模块
  • 新的 chunk 体积在压缩之前是否大于 30kb
  • 按需加载 chunk 的并发请求数量小于等于 5 个
  • 页面初始加载时的并发请求数量小于等于 3 个

默认的策略不能满足我们的需求,我们需要再进行个性化的优化。

寻找需要分割的包

vue-cli-service build  --report

使用上面命令可以生成打包后的模块依赖及文件大小,确定优化的方向在哪。

image-20220405172706678

抽离大文件
chainWebpack: (config) => {
    config.optimization.splitChunks({
      chunks: 'all',
      cacheGroups: {
        vendors: {
          name: 'chunk-vendors',
          test: /[\\/]node_modules[\\/]/,
          priority: 10,
          chunks: 'initial'
        },
        design: {
          name: 'chunk-design',
          priority: 20,
          test: /[\\/]node_modules[\\/]_?ant-design(.*)/
        },
        easemob: {
          name: 'chunk-easemob',
          priority: 20,
          test: /[\\/]node_modules[\\/]_?easemob(.*)/
        },
        commons: {
          name: 'chunk-commons',
          minChunks: 2,
          priority: 5,
          chunks: 'initial',
          reuseExistingChunk: true
        }
      }
    })
  },

抽离后的效果

image-20220405174619366

CDN 方式

https://www.bootcdn.cn/查找第三分cdn对接包的版本路径

  1. index.html 引入相应 cdn 链接

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <!-- <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests"> -->
      <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
      <meta name="referrer" content="never">
      <meta name="renderer" content="webkit" />
      <!-- <meta name="viewport" content="width=device-width,initial-scale=1.0"> -->
      <link rel="icon" href="<%= BASE_URL %>logo.png">
      <title>熊猫进厂-专注普工招聘平台</title>
    </head>
    <body>
    <div id="app">
    </div>
    <script src="./js/echarts.common.js"></script>
    <script src="./js/browser.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.runtime.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/ali-oss/6.15.2/aliyun-oss-sdk.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
    <!-- built files will be auto injected -->
    </body>
    </html>
    
    
  2. vue.config.js 配置 externals

    configureWebpack: config => {
        return {
          externals: {
            'echarts': 'echarts',
            'vue': 'Vue',
            'ali-oss': 'ali-oss'
          }
        }
      },
    

打包后的情况

image-20220405181725416

推荐使用CDN的方式

  • 使用第三方CDN,有可能用户访问过其他网站使用了相同的库,就会有缓存,此时直接读缓存,
  • 第三方CDN,和我们网站的域名不一样,浏览器可以创建新的连接过去文件
提高构建速度

使用speed-measure-webpack-plugin分析打包时间的耗时主要是在哪里。配置

const path = require('path')
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasurePlugin()

function resolve (dir) {
  return path.join(__dirname, dir)
}

// vue.config.js
const vueConfig = {
  configureWebpack: config => {
    // const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
    return smp.wrap({
      externals: {
        'echarts': 'echarts',
        'vue': 'Vue',
        'ali-oss': 'ali-oss'
      }
    })
  },
  ...
}

module.exports = vueConfig

image-20220405185250110

可以看到本次打包用时20秒。

vuecli基本上没有什么优化的空间,如果自己配的webpack可以参考vuecli。

使用下面命令可以输出vuecli的配置

npx vue-cli-service inspect > output.js
  • 缩小文件查找和处理范围

    resolve: {
        alias: {
          '@': 'D:\\chitone\\panda-com-web\\src',
          vue$: 'vue/dist/vue.runtime.esm.js',
          '@$': 'D:\\chitone\\panda-com-web\\src'
        },
        modules: [
          'node_modules',
          'D:\\chitone\\panda-com-web\\node_modules',
          'D:\\chitone\\panda-com-web\\node_modules\\@vue\\cli-service\\node_modules'
        ]
    },
    module: {
        noParse: /^(vue|vue-router|vuex|vuex-router-sync)$/,
    }
    
    • 通过modules指定查找第三方模块的路径。
    • 通过alias指定第三方模块直接查找到打包构建好的压缩js文件。
    • 通过module指定noparse,对第三方模块不再进行分析依赖。
  • 开启多线程打包

    webpack4已经默认开启,4以下的版本可以使用thread-loader

  • 设置缓存:cache-loader

    module.exports = {
      module: {
        rules: [
          {
            test: /\.js$/,
            use: [
              'cache-loader',
              'babel-loader'
            ],
            include: path.resolve('src')
          }
        ]
      }
    }
    

    Webpack 5已经内建了编译缓存的能力,并且 cache-loader 已弃用

DllPlugin 和 DllReferencePlugin

DllPlugin 可以把我们需要打包的第三方库打包成一个 js 文件和一个 json 文件,这个 json 文件中会映射每个打包的模块地址和 id,DllReferencePlugin 通过读取这个json文件来使用打包的这些模块。

优点:项目第三方库的依赖一般不会变的,打包后的文件名hash一般不会变,可以用户浏览器缓存

缺点:将多个第三方库的依赖打包在一起,文件的体积一般会比较大,影响首屏的显示。所以一般将多个体积小的依赖打包在一起,减少http请求,但是要注意包与包之间的冲突,打包一起后会报错。

运行时优化

  • 虚拟长列表渲染
  • 图片懒加载
  • 使用事件委托
  • 28
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值