js加载和长任务
js加载和长任务
本文将讲解以下浏览器如何加载js,并介绍一些可以提高网页加载速度的方法。
Evaluate Script
如果我们在devtools的performance中分析过网站的加载性能,可能会看到一个很长的任务,叫做Evaluate Script.

在这种情况下,该工作足以导致长时间任务,从而阻止主线程承担其他工作(包括驱动用户交互的任务).
Evaluate Script是在浏览器中执行 JavaScript 的必要部分,因为 JavaScript在执行前即时编译。当评估脚本时,首先会分析它是否有错误。如果解析器没有发现错误,则脚本将被编译为字节码,然后可以继续执行。
因为用户可能会在页面最初显示后不久就尝试与页面进行交互=,这样就会导致Evaluate Script出现问题(页面已呈现并不意味着页面已完成加载。由于页面正忙于评估脚本,因此加载期间发生的交互可能会延迟)。
总阻塞时间 (
TBT)是一个可以让我们深入了解页面加载期间是否发生过多脚本评估的指标,因为它是一种负载响应指标。
script和评估它们的任务之间的关系
负责脚本评估的任务如何启动取决于网站正在加载的脚本是否是通过常规<script>元素加载的,或者脚本是否是使用type=module. 由于浏览器倾向于以不同的方式处理事物,因此主要浏览器引擎如何处理脚本评估将涉及它们之间的脚本评估行为的不同之处。
使用script元素
分派评估脚本的任务数量通常与页面上的<script>元素数量有直接关系。每个<script>元素都会启动一个任务来评估所请求的脚本,以便可以对其进行解析、编译和执行。
我们可以通过避免加载大块 JavaScript 来分解脚本评估工作,并使用其他<script>元素加载更多单独的、更小的脚本。
由于设备的功能各不相同,因此很难为单个脚本的大小定义一个设定的限制。为了在压缩效率、下载时间和脚本评估时间之间取得良好的平衡,每个脚本的大小限制为
100 KB是一个不错的指标。
因此我们在页面加载期间应该加载尽可能少的 JavaScript,通过拆分脚本可确保我们拥有大量不会阻塞主线程的较小任务,而不是一个可能阻塞主线程的大型任务。

由于页面 HTML 中存在多个<script>元素,因此产生了多个任务来评估脚本。这比向用户单独发送一个体积非常大的js包更好,因为这样更有可能阻塞主线程。
在script元素添加type=module属性
通过在script元素上添加type=module属性可以在浏览器中本地加载 ES 模块。这种脚本加载方法具有一些开发人员体验优势,例如在生产环境中不需要转换代码就可以使用。但是,以这种方式加载脚本会根据浏览器的不同而具有不同的任务。
基于 Chromium 的浏览器
在 Chrome 等浏览器(或衍生自 Chrome 的浏览器)中,使用type=module属性加载 ES 模块会产生与不使用时看到不同类型的任务。例如,每个将执行的模块脚本(携带type=module)的任务,都呗标记为Compile module tasks。

每个模块脚本都会生成一个编译模块任务,在评估之前编译其内容。
模块编译完成后,随后在其中运行的任何代码都标记为Evaluate module tasks。

从上图可以看见使用type=module需要承担一些不可避免的成本。虽然我们应该努力提供尽可能少的 JavaScript,但使用 ES 模块(无论浏览器如何)都可以提供以下好处:
- 所有模块代码都会在严格模式下自动运行,这允许
JavaScript引擎进行潜在的优化,而这些优化在非严格上下文中是无法实现的。 - 使用
type=module属性在加载时会默认当作为defer。可以在ES加载的脚本上使用设置async来更改此行为。
基于Safari 和 Firefox 的浏览器
当在 Safari 和 Firefox 中加载type=module模块时,每个模块都会在单独的任务中进行评估。这意味着理论上我们可以将仅包含静态import语句的单个顶级模块加载到其他模块中,并且加载的每个模块都会产生单独的网络请求和任务来进行评估。
使用动态import()
动态import()是加载脚本的另一种方法。import与需要位于 ES 模块顶部的静态语句不同,动态import()调用可以出现在脚本中的任何位置,以按需加载 JavaScript 块。这种技术称为代码分割。
使用动态import()有两个好处:
- 推迟加载的模块(设置
defer)通过减少当前加载的JavaScript量来减少启动期间的主线程争用。这释放了主线程,因此它可以更好地响应用户交互。 - 当进行动态
import()调用时,每次调用都会有效地将每个模块的编译和评估分离到自己的任务中。当然,import()加载非常大的模块的动态将启动相当大的脚本评估任务,并且如果交互与动态调用同时发生,import()则可能会干扰主线程响应用户输入的能力。因此,加载尽可能少的JavaScript还是非常重要的。
动态import()调用在所有主要浏览器引擎中的行为都类似:结果的脚本评估任务将与动态导入的模块数量相同。
在web worker中加载js
Web Worker是一个特殊的 JavaScript 用例。Web Worker 在主线程上注册,然后 Worker 中的代码会在自己的线程上运行。这可以减少主线程拥塞,并有助于保持主线程对用户交互的响应更加灵敏。
除了减少主线程工作之外,Web Worker本身还可以importScripts或者静态import来加载外部js,这样通过 Web Worker 请求的任何脚本都会在主线程之外进行评估。
总结
虽然将脚本分解为单独的较小文件有助于限制长时间任务,但是在决定如何分解脚本时也需要考虑以下几点:
压缩效率
压缩是分解脚本的一个因素。当脚本较小时,压缩效率会有所降低。较大的脚本将从压缩中受益更多。虽然提高压缩效率有助于尽可能缩短脚本的加载时间,但确保将脚本分解为足够小的块以在启动期间促进更好的交互性也是需要权衡决定的。
打包工具是一个非常理想的工具,可以用来管理我们的js打包结果:
- 比如说
webpack,可以通过SplitChunksPlugin插件来管理打包后的js的大小。 - 对于
Rollup和esbuild而言,可以通过在代码中使用动态import()来管理脚本文件大小。这些打包工具会自动将动态import()的资源分解到其他的文件中,从而避免生成较大的文件。
缓存
缓存对于一些需要重复访问时页面加载的速度起着重要作用。当我们的js包非常大时,每次更新,之前的一些打包文件都会失效(多个文件都打包在一个js文件里,当修改里边的一些代码时,整个打包文件都是更新过的),必须要重新下载。
通过分解脚本,我们不仅可以将脚本评估工作分解为较小的任务,还可以让用户尽量从浏览器的缓存中获取之前的页面内容,而不是需要重新下载文件。这意味着整体页面加载速度更快。
为了使缓存既高效又可以避免从缓存中提供过时的资源,可以在打包出的文件设置哈希值作为文件名。
嵌套模块和加载性能
我们要是在生产环境中使用 ES 模块并使用type=module属性加载它们,则需要了解模块嵌套如何影响启动时间。模块嵌套是指一个 ES 模块静态导入另一个 ES 模块,而另一个 ES 模块又静态导入另一个 ES 模块:
// a.js
import {b} from './b.js';// b.js
import {c} from './c.js';
如果这些模块没有被打包在一起,那么就会导致生成一个请求链:当一个script元素中包含了a.js的代码的时候,就会发送一个请求去请求b.js文件,然后b.js文件又会发送一个请求去请求c.js文件。这种情况下除了修改代码将这些打包在一起外,还可以通过 modulepreload方式提前预加载 ES 模块以避免网络请求链(仅支持基于 Chromium 的浏览器).
相关文章:
js加载和长任务
js加载和长任务 本文将讲解以下浏览器如何加载js,并介绍一些可以提高网页加载速度的方法。 Evaluate Script 如果我们在devtools的performance中分析过网站的加载性能,可能会看到一个很长的任务,叫做Evaluate Script. 在这种情况下&#x…...
利用Stable diffusion Ai 制作艺术二维码超详细参数和教程
大家有没有发现最近这段时间网上出现了各种各样的AI艺术二维码,这种二维码的出现,简直是对二维码的“颠覆式创新”,直接把传统的二维码提升了一个维度!作为设计师的我们怎么可以不会呢? 今天就教大家怎么制作这种超有艺…...
【C语言课程设计】图书管理系统
引言: 图书管理系统是一个重要的信息管理系统,对于图书馆和书店等机构来说,它能够方便地管理图书的录入、显示、查询、修改和删除等操作。本实验基于C语言开发了一个简单的图书管理系统,通过账户名和密码进行系统访问和权限控制&a…...
在 ArcGIS Pro 中使用 H3 创建蜂窝六边形
H3是Uber开发的分层索引系统,它使用六边形来平铺地球表面。H3在二十面体(一个具有20个三角形面和12个顶点的形状)上构建其六边形网格。由于仅用六边形不可能平铺二十面体,因此每个分辨率需要12个五边形来完成网格。分层索引网格意味着每个六边形都可以细分为子单元六边形。…...
创建Electron项目
一、使用vite 构建 electron项目 npm init vitelatest Need to install the following packages:create-vitelatest Ok to proceed? (y) y √ Project name: ... CertificateDownload √ Package name: ... certificatedownload √ Select a framework: Vue √ Select a var…...
Spring Boot实践一
一、Spring Boot简介 Spring Boot是一个基于Spring框架的快速开发应用程序的工具。它提供了一种快速、方便的方式来创建基于Spring的应用程序,而无需繁琐的配置。Spring Boot通过自动配置和约定大于配置的方式,使得开发者可以更加专注于业务逻辑的实现&…...
简单认识NoSQL的Redis配置与优化
文章目录 一、关系型数据库与非关系型数据库1、关系型数据库:2、非关系型数据库3、关系型数据库和非关系型数据库区别:4、非关系型数据库应用场景 二.Redis1、简介2、优点:3、Redis为什么这么快? 三、Redis 安装部署1、安装配置2、…...
开发一个RISC-V上的操作系统(二)—— 系统引导程序(Bootloader)
目录 文章传送门 一、什么是Bootloader 二、简单的启动程序 三、上板测试 文章传送门 开发一个RISC-V上的操作系统(一)—— 环境搭建_riscv开发环境_Patarw_Li的博客-CSDN博客 开发一个RISC-V上的操作系统(二)—— 系统引导…...
Git安装与学习
Git学习网站 Git安装教程 镜像网站 https://registry.npmmirror.com/binary.html 镜像下载是网站对服务器的一个保护措施之一,就是A站点下载的数据同 B站点下载的数据完全一样,b站点就是A站点的一面镜子。 所以镜像下载下来和原站点一摸一样。...
【Docker】docker中容器之间通信方式
文章目录 1. Docker容器之间通信的主要方式1.1 通过容器ip访问1.2. 通过宿主机的ip:port访问1.3. 通过link建立连接(官方不推荐使用)1.4. 通过 User-defined networks(推荐) 2. 参考资料 1. Docker容器之间通信的主要方式 1.1 通…...
算法-归并排序-JAVA
下面是Java实现归并排序的示例代码: public class MergeSort {public void mergeSort(int[] arr) {if (arr null || arr.length < 1) {return;}int[] temp new int[arr.length];mergeSort(arr, temp, 0, arr.length - 1);}private void mergeSort(int[] arr, …...
Flask 进阶
Flask 如何访问项目以外的文件 在工作中, 要在项目里展示一些额外的文件, 包括但不限于静态的html。图片, log, 其他的都还好说, 但是当html的时候我就开始犯难了, 因为数量过多 我并不想把它塞进我项目的t…...
home-assistant整合sso
其他软件都可以通过nginx直接做代理添加鉴权,但是这个hass果然是用户安全隐私很强,做代理需要配置白名单,而且支持的三方鉴权都不太适合我的需求,非要改源码才行,后来我发现不用改源码的折中方式 参考文章 External …...
Ip-Limit: 轻量级注解式IP限流组件(二)
author: van , ggfanwentaogmail.comIp-Limit-Example: 轻量级注解式IP限流组件使用样例 项目简介 该项目为ip-limiter的使用示例项目。 ip-limiter地址: https://github.com/DDAaTao/ip-limiter 示例项目文件树 └─example├─handler│ └─BaseException…...
【C++】开源:Redis数据库配置与使用
😏★,:.☆( ̄▽ ̄)/$:.★ 😏 这篇文章主要介绍Redis数据库配置与使用。 无专精则不能成,无涉猎则不能通。。——梁启超 欢迎来到我的博客,一起学习,共同进步。 喜欢的朋友可以关注一下,…...
TCP/IP网络编程 第二十四章:制作HTTP服务器端
实现简单的Web服务器端 现在开始在HTTP协议的基础上编写Web服务器端。先给出Windows平台下的示例,再给出Linux下的示例。在这里我假设各位都有了有关HTTP的知识,如果不知道HTTP协议的具体内容可以参考的往期博客,有了这些基础就不难分析源代…...
React 前端应用中快速实践 OpenTelemetry 云原生可观测性(SigNoz/K8S)
OpenTelemetry 可用于跟踪 React 应用程序的性能问题和错误。您可以跟踪从前端 web 应用程序到下游服务的用户请求。OpenTelemetry 是云原生计算基金会(CNCF)下的一个开源项目,旨在标准化遥测数据的生成和收集。已成为下一代可观测平台的事实标准。 React(也称为 Re…...
Linux 多线程并发Socket服务端的实现( 11 ) -【Linux通信架构系列 】
系列文章目录 C技能系列 Linux通信架构系列 C高性能优化编程系列 深入理解软件架构设计系列 高级C并发线程编程 设计模式系列 期待你的关注哦!!! 现在的一切都是为将来的梦想编织翅膀,让梦想在现实中展翅高飞。 Now everythi…...
2.7. Java 泛型了解么?什么是类型擦除?介绍一下常用的通配符?
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。 Java 的泛型是伪泛型&am…...
单例模式与构造器模式
单例模式 1、是什么 单例模式(Singleton Pattern):创建型模式,提供了一种创建对象的最佳方式,这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建 在应用程序运…...
突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
Bean 作用域有哪些?如何答出技术深度?
导语: Spring 面试绕不开 Bean 的作用域问题,这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开,结合典型面试题及实战场景,帮你厘清重点,打破模板式回答,…...
c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
高考志愿填报管理系统---开发介绍
高考志愿填报管理系统是一款专为教育机构、学校和教师设计的学生信息管理和志愿填报辅助平台。系统基于Django框架开发,采用现代化的Web技术,为教育工作者提供高效、安全、便捷的学生管理解决方案。 ## 📋 系统概述 ### 🎯 系统定…...
Unity VR/MR开发-VR开发与传统3D开发的差异
视频讲解链接:【XR马斯维】VR/MR开发与传统3D开发的差异【UnityVR/MR开发教程--入门】_哔哩哔哩_bilibili...
