当前位置: 首页 > news >正文

你的应用太慢了,给我司带来了巨额损失,该怎么办

记得很久之前看过谷歌官方有这么样的声明:如果一个页面的加载时间从 1 秒增加到3 秒,那么用户跳出的概率将增加 32%。

但是早在 2012 年,亚马逊就计算出了,页面加载速度一旦下降一秒钟,每年就会损失 16 亿美元的销售额。于是,这篇文章就想聊聊有没有方法来解决这种问题。

什么?没赚到钱,是我的锅?

那么,是什么影响了页面的加载速度,导致用户跳出?

其中有一个大的因素就是我们的应用用到了很多的第三方库。

那么,有没有一种一举两得的方法,我即可以保留使用的第三方脚本,又可以保证页面的加载速度?

其实,我们知道 JavaScript 本质上是一种单线程语言,只运行一个事件循环。这意味着一次只执行一条语句。由于这一限制,当试图运行自己的代码以及任何第三方脚本时,它们必须在同一线程中执行。这意味着由于处理能力的限制,它们会减慢主线程和彼此的速度,也会减慢彼此的速度。

根据谷歌的说法,添加第三方脚本后,有一些潜在的问题会产生,我列举了以下几点:

  • 会向多个服务器发出过多的网络请求。一个网站的请求越多,它的加载时间就越长。
  • 发送太多的 JavaScript 会让主线程很忙。过多的 JavaScript 会阻碍 DOM 的构建,延迟页面呈现的速度
  • cpu 密集型脚本解析和执行可能会延迟用户交互,并会导致电池电量消耗的更快。
  • 不小心加载的第三方脚本可能会产生单点故障(SPOF)。
  • HTTP 缓存不足,迫使经常直接从网络获取资源。
  • 脚本中使用遗留 api(例如 document.write() ),对用户体验是有害的。
  • 脚本中包含过多的 DOM 元素或昂贵的 CSS 选择器。
  • 包括多个第三方嵌入,可能导致多个框架和库被多次拉入,这加剧了性能问题。
  • 第三方脚本也经常使用嵌入技术导致阻塞 window.onload 的执行,例如使用 async或 defer

这些问题都可以通过谷歌浏览器的 Analytics 工具检测出来。

现在,有一个改善第三方脚本的工具,能帮助我们的应用减少大量的第三方脚本,也是本篇文章要说的主角—— Partytown

Partytown

Partytown 是一个 JavaScript 库,可以让你的第三方脚本交给 web worker 来处理,以消除他们可能对你的网站产生的性能影响。为了抵消上述第三方脚本的负面影响,Partytown 打算做以下事情:

  • 释放主线程资源,仅用于主要 web 应用程序的执行。
  • 将第三方脚本放到沙箱,允许或拒绝它们访问主线程 api。
  • 在 web worker 线程中隔离长时间运行的任务。
  • 通过将 DOM setter /getter 批处理到组更新中,减少来自第三方脚本的布局抖动。
  • 限制第三方脚本对主线程的访问。
  • 允许第三方脚本完全按照它们的编码方式运行,无需任何更改。
  • 在 web worker 中同步读写主线程 DOM 操作,允许在 web worker 中运行的脚本按预期执行。

Partywork 运行方式

简单地说,Partytown 添加了一个 worker 线程来允许在主线程和 worker 线程中执行

要理解 Partytown,首先要了解现代网络浏览器使用的一些技术:

  • Web Workers API: 这使得在与 Web 应用程序的主执行线程分离的后台线程中运行脚本操作成为可能。Partytown 的理念是主线程应该专门用于你的第一方代码任何不需要在关键路径上的脚本都应该移动到 web worker上。因为主线程的性能比 web worker 线程的性能更重要。
  • XMLHttpRequest (XHR): 对象用于与服务器交互。可以从URL检索数据,而不必进行整个页面刷新。这使得网页只更新页面的一部分,而不会中断用户正在做的事情。
  • Service Worker API: Service Worker 本质上充当了位于 web 应用程序、浏览器和网络之间的代理服务器。它们主要用于创建有效的脱机体验,拦截网络请求,并根据网络是否可用采取适当的操作,以及更新驻留在服务器上的资源。它们还允许访问推送通知和后台同步 api。
  • Javascript 代理:代理对象允许你创建一个可以用来代替原始对象的对象,但它可能会重新定义基本的对象操作,如 get、set 和 define 属性。代理对象通常用于记录属性访问、验证、格式化或清除输入等。

传统上,主线程和 worker 线程之间的通信必须是异步的:因为为了让两个线程通信,它们不能使用阻塞调用。Partytown 则不同。它允许从 web worker 执行的代码同步访问 DOM这样做的好处是第三方脚本可以继续按照它们的编码方式工作。

如下图所示,运行在代理全局变量的 web worker 中的代码使用同步 XHR 使异步操作同步化。这将被 service worker 拦截,主线程值将通过 postMessage 检索并发送回来。

在这里插入图片描述

如何集成 Partytown

你可以很容易地将它添加到任何站点,并使用 type="text/partytown" 标记任何你想要加载在 web worker 中的脚本。

需要注意的是,Partytown 并不会自动将所有脚本转移到 web worker上,而是采用了一种可选择的方法最好的情况是,开发人员可以准确地选择哪些脚本应该使用Partytown,而所有其他脚本将保持不变。

Partytown可以使用任何 HTML 页面,不需要特定的框架。然而,为了让它更容易在各种框架或服务中使用,可以为它们的生态系统制作插件/包装器。

只有当特定脚本具有 type="text/ Partytown " 属性时,才会启用 Partytown。

<script type="text/partytown" src="third-parth.js"></script>

上面这段脚本执行时会产生几个事件:

  • 通过使用 <script/>标签上的 type="text/partytown" 属性,脚本不能在主线程上运行。
  • Service worker 创建一个 “onfetch” 处理程序来拦截特定的请求。
  • Web worker 会处理在 worker 线程中执行的脚本。
  • Web worker 创建 JavaScript 代理来复制和转发对主线程 api 的调用(比如DOM操作)。
  • 任何对JavaScript 代理的调用都使用同步XHR请求。
  • Service worker 拦截请求,然后能够与主线程进行异步通信
  • 当 Service worker 从主线程接收到结果时,它会响应 web worker 的请求。
  • 从在 web worker 上执行代码的角度来看,一切都是同步的,对 document 的每次调用都是阻塞的。

执行步骤

任何你添加 type="text/partytown" 的脚本都会在默认情况下加载到 web worker 中,但是可以完全访问全局变量。' type="text/partytown" ' 属性做两件事:

  1. 通知浏览器不处理脚本。通过给脚本一个浏览器无法识别的 type 属性:“嵌入的内容被视为一个数据块,浏览器不会处理它。”
  2. 提供一个 query 选择器,这样 Partytown 就可以找到所有要在web worker中运行的脚本。当 document 准备好并且 Partytown 已经初始化时,Partytown 将查询所有脚本属性中含有 [type="text/ Partytown "] 属性的元素。你会注意到,当一个脚本在web worker中执行后,它会得到一个更新的 type 属性: type="text/partytown-x"
// run in the worker
fetch('/track', {body: JSON.stringify({url: window.location.href // run on the main thread})
})

我们使用 JavaScript Proxy 向 worker 线程提供主线程全局变量,拦截它们并转发给主线程:

self.window = new Proxy({get(key) {return getFromMainThread(key)}
})

这里是最好的部分是: 使用一个同步 XHR 请求来阻塞 worker 线程,并从主线程检索所需的值:

function getFromMainThread(prop) {request.open('POST', '/proxytown', false)request.send(null)request.send(JSON.stringify({prop}))return JSON.parse(request.reponseText)
}

现在我们可以使用 service worker 来拦截 /proxytown 请求,向主线程异步postMessage 以获取所需的值并返回它:

self.addEventListener('fetch', event=>{if(request.url === '/proxytown') {event.respondWith(new Promise(async resolve=>{resolve(await getFromMainThread(event.request.json()))}))}
})

好了!你现在可以无缝地将一系列第三方脚本放到 web worker 中运行,从而消除主线程的性能成本。如果感兴趣,可以用一用。

相关文章:

你的应用太慢了,给我司带来了巨额损失,该怎么办

记得很久之前看过谷歌官方有这么样的声明&#xff1a;如果一个页面的加载时间从 1 秒增加到3 秒&#xff0c;那么用户跳出的概率将增加 32%。 但是早在 2012 年&#xff0c;亚马逊就计算出了&#xff0c;页面加载速度一旦下降一秒钟&#xff0c;每年就会损失 16 亿美元的销售额…...

第十四届蓝桥杯三月真题刷题训练——第 22 天

目录 第 1 题&#xff1a;受伤的皇后_dfs 题目描述 输入描述 输出描述 输入输出样例 运行限制 代码&#xff1a; 思路&#xff1a; 第 2 题&#xff1a;完全平方数 问题描述 输入格式 输出格式 样例输入 1 样例输出 1 样例输入 2 样例输出 2 评测用例规模与约…...

机器学习:朴素贝叶斯模型算法原理(含实战案例)

机器学习&#xff1a;朴素贝叶斯模型算法原理 作者&#xff1a;i阿极 作者简介&#xff1a;Python领域新星作者、多项比赛获奖者&#xff1a;博主个人首页 &#x1f60a;&#x1f60a;&#x1f60a;如果觉得文章不错或能帮助到你学习&#xff0c;可以点赞&#x1f44d;收藏&…...

Linux 多线程:理解线程

目录一、理解线程的思想二、Linux中的线程与进程1.Linux中的进程2.Linux中的线程三、线程的工作方式四、线程的独有数据与共享数据1.独有数据2.共享数据一、理解线程的思想 线程就是把一个进程分成多个执行部分&#xff0c;一个部分就是一个线程&#xff0c;比如可以让一个线程…...

Web前端学习:章四 -- JavaScript初级(四)-- BOM

138&#xff1a;Object数据格式简介 1、object对象 JS中独有 的一种数据格式 名字可以随便取&#xff0c;值一般就那几种数据格式 139&#xff1a;BOM - JS跳转页面 BOM Browser Object Model&#xff1a;浏览器对象模型 使用JavaScript控制浏览器交互 控制浏览器里面的内…...

Lesson9.网络基础1

网络协议初识 所谓的协议就是人们为了通信的一种约定 操作系统要进行协议管理,必然会先描述,再组织协议本质就是软件,软件是可以"分层"协议在设计的时候,就是被层状的划分的, 为什么要划分成为层状结构 场景复杂功能解耦(便于人们进行各种维护)OSI七层模型 局域网中…...

这几个SQL语法的坑,你踩过吗

本文已经收录到Github仓库&#xff0c;该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点&#xff0c;欢迎star~ Github地址&#xff1a;https://github.com/…...

算法基础——复杂度

前言 算法是解决问题的一系列操作的集合。著名的计算机科学家Niklaus Wirth曾提出&#xff1a;算法数据结构程序&#xff0c;由此可见算法在编程中的重要地位。本篇主要讨论算法性能好坏的标准之一——复杂度。 1 复杂度概述 1.1 什么是复杂度 本文所讨论的复杂度是指通过事先…...

基类与派生类对象的关系 派生类的构造函数

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C &#x1f525;座右铭&#xff1a;“不要等到什么都没有了&#xff0c;才下…...

【算法】生成分布式 ID 的雪花算法

ID 是数据的唯一、不变且不重复的标识&#xff0c;在查询数据库的数据时必须通过 ID 查询&#xff0c;在分布式环境下生成全局唯一的 ID 是一个重要问题。 雪花算法&#xff08;snowflake&#xff09;是一种生成分布式环境下全局唯一 ID 的算法&#xff0c;该算法由 Twitter 发…...

Linux系统编程 - 基础IO(IO操作)

目录 预备知识 复习C文件IO相关操作 printf相关函数 fprintf snprintf 读取文件 系统文件IO操作 open函数 umask()函数 open函数返回值 预备知识 1.你真的理解文件原理和操作了吗&#xff1f;不是语言问题&#xff0c;是系统问题2.是不是只有C/C有文件操作呢&#x…...

基于 Avue 的 CRUD 表格组件封装

在 components 文件夹中,创建一个新的 .vue 文件,例如:AvueCrudTable.vue。 透传父组件传递的属性和事件 : 1、利用v-bind=“ a t t r s " 支持所有 a v u e 的使用方法并在其基础上进行封装 2 、使用 v − o n = " attrs"支持所有 avue 的使用方法并在其基…...

树莓派学习笔记(十三)基于框架编写驱动代码

文章目录一、代码分析&#xff1a;二、源码一、代码分析&#xff1a; 在内核中由于代码文件多&#xff0c;避免函数名重复&#xff0c;使用static将函数的作用域限制在该文件内 内核的打印函数printk和printf类似 file_operations结构体使用符号“ . ”指定参数&#xff0c;省…...

vue事件修饰符之.prevent

.prevent 事件修饰符只是阻止默认事件&#xff0c;不会自动触发任何事件处理函数。因此&#xff0c;在使用 .prevent 事件修饰符时&#xff0c;需要自己编写相应的事件处理函数来处理事件。 例如&#xff0c;在上面的例子中&#xff0c;我们通过在表单上绑定 submit.prevent&q…...

【SpringCloud AlibabaSentinel实现熔断与限流】

本笔记内容为尚硅谷SpringCloud AlibabaSentinel部分 目录 一、Sentinel 1、官网 2、Sentinel是什么 3、下载 4、特性 5、使用 二、安装Sentinel控制台 1、sentinel组件由2部分构成 2、安装步骤 1.下载 2.运行命令 3.访问sentinel管理界面 三、初始化演示工程 …...

类与对象-封装

一、封装的意义封装是C面向对象三大特性之一语法&#xff1a; class name { 访问权限:属性行为 };注意&#xff1a;类中的属性和行为 统称为成员属性 又称 成员属性 / 成员变量行为 又称 成员函数 / 成员方法封装将属性和行为作为一个整体&#xff0c;表现生活中的事物例①&…...

【回忆杀】2012年拥有第一台电脑【致逝去的青春】

高中说起 在2012年的时候吧&#xff0c;高考过后&#xff0c;那个时候一门心思的想当一名体育老师【现在居然还有这个想法&#xff0c;哈哈】&#xff0c;最后没有考上自己希望的大学我记得好像是2012年7月的时候就去重庆投靠朋友&#xff0c;他教我做模具&#xff0c;2012年做…...

PointNeXt: Revisiting PointNet++ with Improved Training and Scaling Strategies

Abstract PointNet 是点云理解领域最有影响力的神经网络架构之一。虽然近期出现了 PointMLP 和 Point Transformer 等新型网络&#xff0c;它们的精度已经大大超过了 PointNet&#xff0c;但我们发现大部分性能提升是由于改进的训练策略&#xff0c;例如数据增强和优化技术以及…...

打印九九乘法表-课后程序(JavaScript前端开发案例教程-黑马程序员编著-第2章-课后作业)

【案例2-9】打印九九乘法表 一、案例描述 考核知识点 for双重循环 练习目标 掌握for循环应用。实现九九乘法表。 需求分析 九九乘法表相信大家一点也不陌生&#xff0c;之前见到的乘法表是印刷在课程本之上的。而在本案例中我们将用JavaScript代码来实现九九乘法表。 案例分…...

【Linux】基于阻塞队列的生产者消费者模型

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《学会Linux》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;为何要使用…...

【kafka】Golang实现分布式Masscan任务调度系统

要求&#xff1a; 输出两个程序&#xff0c;一个命令行程序&#xff08;命令行参数用flag&#xff09;和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽&#xff0c;然后将消息推送到kafka里面。 服务端程序&#xff1a; 从kafka消费者接收…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

AI病理诊断七剑下天山,医疗未来触手可及

一、病理诊断困局&#xff1a;刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断"&#xff0c;医生需通过显微镜观察组织切片&#xff0c;在细胞迷宫中捕捉癌变信号。某省病理质控报告显示&#xff0c;基层医院误诊率达12%-15%&#xff0c;专家会诊…...

动态 Web 开发技术入门篇

一、HTTP 协议核心 1.1 HTTP 基础 协议全称 &#xff1a;HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09; 默认端口 &#xff1a;HTTP 使用 80 端口&#xff0c;HTTPS 使用 443 端口。 请求方法 &#xff1a; GET &#xff1a;用于获取资源&#xff0c;…...

Web后端基础(基础知识)

BS架构&#xff1a;Browser/Server&#xff0c;浏览器/服务器架构模式。客户端只需要浏览器&#xff0c;应用程序的逻辑和数据都存储在服务端。 优点&#xff1a;维护方便缺点&#xff1a;体验一般 CS架构&#xff1a;Client/Server&#xff0c;客户端/服务器架构模式。需要单独…...

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…...

pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)

目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 &#xff08;1&#xff09;输入单引号 &#xff08;2&#xff09;万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...