前端性能优化(图文并茂,通俗易懂)

一、静态和动态导入

默认情况下,我们静态导入的所有模块都会添加到初始捆绑包中。使用默认 ES2015 导入语法 导入的模块将静态导入。
import module from 'module'

image.png

实际上,我们可以而把那些在需要时展示的组件,通过动态导入来实现

import("b.vue").then()  //动态导入,在打包时会自动chunk

image.png

如上图,在点击时 emoji 时,才去加载 emoji-picker 组件。

动态路由和动态组件会自动拆分bundle。

二、可见性导入

除了用户交互之外,我们通常还有在初始页面上不可见的组件。一个很好的例子是延迟加载图像,这些图像在视口中不直接可见,但只有在用户向下滚动后才会加载。

image.png

三、交互时导入(懒惰式加载)

当用户与需要它的 UI 交互时,延迟加载非关键资源

您的页面可能包含不是立即必需的组件或资源的代码或数据。例如,用户看不到用户界面的一部分,除非他们单击或滚动页面的某些部分。例如模态框、列表的详情页、视频播放器或聊天小部件、第三方资源等等

与其立即加载这些资源,不如在更合适的时刻加载它们,例如:

  • 当用户首次单击以与该组件进行交互时
  • 将组件滚动到视图中
  • 或延迟该组件的加载,直到浏览器空闲(通过 requestIdleCallback API)。

加载资源的不同方法概括如下:

  • 立即加载资源(加载脚本的正常方式)
  • Lazy (路由)- 当用户导航到路由或组件时加载
  • Lazy (交互时) - 当用户单击 UI 时加载(例如显示聊天)
  • Lazy (在视口中)- 当用户滚动到组件时加载
  • Prefetch - 预拉取
  • Preload - 预加载

如: 第三方 UI
image.png

如: 视频播放器嵌入
image.png

如: 认证
image.png

如:聊天小部件
image.png

如: 帮助页
image.png

四、基于路由的拆分

根据当前路由动态加载组件

{
    path: "/home",
    component: () => import(/* webpackChunkName: "home"*/  "@/components/layout/Index.vue"),
    children: [
      //todo     
    ],
},

image.png

五、捆绑拆分

将代码拆分为可重用的小块

构建现代 Web 应用程序时,打包器例如,Webpack或rollup获取应用程序的源代码,并将其打包到一个或多个bundle中,
当用户访问网站时, bundle请求并加载,以便将数据显示到用户的屏幕。

bundle越大,则浏览器引擎到达进行第一次呈现调用的行所需的时间越长。在那之前,用户必须盯着空白屏幕相当长一段时间,这可能是.非常令人沮丧!

我们希望尽快向用户显示数据。更大的bundle导致加载时间、处理时间和执行时间增加.如果我们能减小这个捆绑包的大小,以便加快速度,那就太好了。

image.png

六、断续器模式

通过预缓存、延迟加载和最小化往返来优化初始负载

当我们想要访问一个网站时,我们首先必须向服务器发出请求才能获得这些资源。入口点指向的文件从服务器返回,这通常是我们应用程序的初始HTML文件!浏览器的 HTML 解析器在开始从服务器接收此数据后立即开始解析此数据。如果解析器发现需要更多资源(如样式表或脚本),则会向服务器发送另一个 HTTP 请求以获取这些资源!

image.png

断续器模式侧重于四个主要的性能注意事项:

  • 有效地推送关键资源,从而最大限度地减少到服务器的往返量并减少加载时间。
  • 尽快渲染初始路由,提升用户体验
  • 在后台为经常访问的路由预缓存资源,以最大限度地减少对服务器的请求量并实现更好的脱机体验
  • 懒惰地加载不经常请求的路线或资源

image.png

七、Tree Shaking

通过消除死代码来减小捆绑包大小

我们可能会将代码添加到我们的捆绑包中,而这些代码在应用程序中的任何地方都没有使用。这段死代码可以消除,以减小捆包的尺寸,并防止不必要地加载更多数据!消除死代码的过程在将其添加到我们的捆绑包之前,称为tree-shaking

image.png

八、Preload

在发现关键资源之前通知浏览器(指关键资源加载完后,会接着加载preload的资源。)

<link rel="preload"> 方式
image.png

webpackPreload: true 方式(Webpack 4.6.0+)

const EmojiPicker = import(/* webpackPreload: true */ "./EmojiPicker");

image.png

九、Prefetch

获取和缓存可能很快请求的资源(指关键资源加载完后,不一定会加载prefetch的资源,可能等到具体需要时才加载)

<link rel="prefetch">方式
image.png

webpackPrefetch: true方式

const EmojiPicker = import(/* webpackPrefetch: true */ "./EmojiPicker");

image.png

十、虚拟列表

这是在动态列表中仅呈现可见内容行而不是整个列表的想法。呈现的行只是完整列表的一小部分,当用户滚动时,可见的内容(窗口)会移动。这可以提高渲染性能。

image.png

十一、压缩脚本

减少通过网络传输脚本所需的时间

  • 1、Gzip 和 Brotli 是两种最常用的被现在浏览器所支持的 压缩js 方式(需要和nginx配置使用)
  • 2、Brotli 在相似的压缩级别下提供了更好的压缩比(但是需要https,而gzip在http下也可以)
  • 3、如果你使用 webpack来打包你得代码,你可以使用 CompressionPlugin 压缩为gzip 或者 使用 BrotliWebpackPlugin 压缩为brotli,如果使用rollup,则使用rollup-plugin-gzip插件
  • 4、Gzip压缩切换到Brotli压缩,文件体积大小会降低15%-25%左右。
  • 5、compress(a + b) <= compress(a) + compress(b) - 单个大捆绑包将比多个较小的捆绑包提供更好的压缩

HTTP 数据压缩可以以不同的方式进行分类。其中之一是有损与无损。
有损压缩意味着压缩-解压缩循环会导致文档略有更改,同时保持其可用性。最终用户大多无法察觉到这种变化。有损压缩最常见的示例是图像的 JPEG 压缩。
使用无损压缩,压缩和后续解压缩后恢复的数据将与原始数据精确匹配。PNG 图像是无损压缩的一个示例。无损压缩与文本传输相关,应应用于基于文本的格式,如 HTML、CSS 和 JavaScript。

有多个工具可用于缩小 HTML、CSS 和 JS 资源。Terser 是 ES6+ 中流行的 JavaScript 压缩工具,默认情况下,Webpack 包含一个用于此库的插件,用于创建缩小的构建文件。
也可以使用esbuild压缩,它是一种比terser速度还快的压缩方式。能快10-100倍

静态与动态压缩

缩小有助于显著减小文件大小,但压缩 JS 可以提供更显著的增益。可以通过两种方式实现服务器端压缩。
静态压缩:可以使用静态压缩来预压缩资源,并在生成过程中提前保存它们。常用于打包时压缩。比如webpack或者rollup在打包时,就提前压缩为gzip,然后放到 nginx上,配置 gzip_static:on;这样在http请求时,会获取 gzip的文件,然后浏览器解压缩,显示页面。
动态压缩:通过此过程,当浏览器请求资源时,压缩会动态进行。常用于接口中的数据压缩,在请求接口时,服务器动态压缩数据,然后返回到浏览器。

Gzip 和Brotli算法

Gzip压缩格式已经存在了近30年,是一种基于Deflate算法的无损算法。Deflate算法本身使用LZ77算法和霍夫曼编码的组合。
LZ77 算法标识重复的字符串,并将它们替换为反向引用,反向引用是指向它以前出现的位置的指针,后跟字符串的长度。随后,霍夫曼编码识别常用的引用,并用具有较短位序列的引用替换它们。较长的位序列用于表示不经常使用的引用。

image.png

所有主流浏览器都支持 Gzip。

Brotli
2015年,谷歌推出了Brotli算法和Brotli压缩数据格式。与GZip一样,Brotli也是一种基于LZ77算法和霍夫曼编码的无损算法。此外,它还使用二阶上下文建模以类似的速度产生更密集的压缩。上下文建模是一项功能,它允许在同一块中对同一字母表使用多个霍夫曼树。Brotli 还支持更大的窗口大小用于反向引用,并具有静态字典。这些功能有助于提高其作为压缩算法的效率。
Brotli目前受到所有主要服务器和浏览器的支持,并且越来越受欢迎。

比较gzip和brotli

下表显示了不同压缩级别的 Brotli 和 Gzip 压缩率和速度的基准比较。
image.png

启用压缩

webpack配置

module.exports = {
 //...
 plugins: [
   //...
   new CompressionPlugin()
 ]
}

Nginx 这样的 HTTP 代理上启用它

server {
        listen 8080;
        server_name localhost;

        # 需要http_gzip_static_module 模块
        gzip_static on;  
        
        location / {
            alias html
            index index.html index.htm;
            try_files $uri $uri/ /html/index.html;
        }
}

浏览器通过请求中的 Accept-Encoding HTTP 标头传达它支持的压缩算法,这表明浏览器支持 Gzip 和brotli
Accept-Encoding: gzip, br

服务器将返回 Content-Encoding HTTP 响应标头,以指示响应中使用的压缩算法。例如,
Content-Encoding: br

image.png


小李子的前端
热爱前端的菜鸟,怀揣梦想的小白

人生短短急个球!

2.3k 声望
108 粉丝
0 条评论
推荐阅读
单文件组件下的vue,可以擦出怎样的火花
与时俱进吧,看着 vue3 和 vite,虽然不会用,但还是心痒痒,然后就把原先基于 vue@2 的实现做了重构。不周之处,大家见谅!下面关于过期的内容,我就用删除线标记了。

leftstick64阅读 45.2k评论 18

从零搭建 Node.js 企业级 Web 服务器(零):静态服务
过去 5 年,我前后在菜鸟网络和蚂蚁金服做开发工作,一方面支撑业务团队开发各类业务系统,另一方面在自己的技术团队做基础技术建设。期间借着 Node.js 的锋芒做了不少 Web 系统,有的至今生气蓬勃、有的早已夭折...

乌柏木150阅读 12.3k评论 10

正则表达式实例
收集在业务中经常使用的正则表达式实例,方便以后进行查找,减少工作量。常用正则表达式实例1. 校验基本日期格式 {代码...} {代码...} 2. 校验密码强度密码的强度必须是包含大小写字母和数字的组合,不能使用特殊...

寒青56阅读 7.9k评论 11

JavaScript有用的代码片段和trick
平时工作过程中可以用到的实用代码集棉。判断对象否为空 {代码...} 浮点数取整 {代码...} 注意:前三种方法只适用于32个位整数,对于负数的处理上和Math.floor是不同的。 {代码...} 生成6位数字验证码 {代码...} ...

jenemy46阅读 6k评论 12

从零搭建 Node.js 企业级 Web 服务器(十五):总结与展望
总结截止到本章 “从零搭建 Node.js 企业级 Web 服务器” 主题共计 16 章内容就更新完毕了,回顾第零章曾写道:搭建一个 Node.js 企业级 Web 服务器并非难事,只是必须做好几个关键事项这几件必须做好的关键事项就...

乌柏木66阅读 6.2k评论 16

再也不学AJAX了!(二)使用AJAX ① XMLHttpRequest
「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,希望读者通过阅读本系列文章,能够对 AJAX 技术有更加深入的认识和理解,从此能够再也不用专门学习 AJAX。本篇文章为该系列的第二篇,最近更新于 2023 年 1...

libinfs39阅读 6.3k评论 12

封面图
从零搭建 Node.js 企业级 Web 服务器(一):接口与分层
分层规范从本章起,正式进入企业级 Web 服务器核心内容。通常,一块完整的业务逻辑是由视图层、控制层、服务层、模型层共同定义与实现的,如下图:从上至下,抽象层次逐渐加深。从下至上,业务细节逐渐清晰。视图...

乌柏木44阅读 7.4k评论 6

人生短短急个球!

2.3k 声望
108 粉丝
宣传栏