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

CSS 真的会阻塞文档解析吗?

在网页开发领域,一个常见的疑问是 CSS 是否会阻塞文档解析。理解这一问题对于优化网页性能、提升用户体验至关重要。要深入解答这个问题,需要从浏览器渲染网页的原理说起。

浏览器渲染网页的基本流程

浏览器在接收到 HTML 文档后,会依次进行以下几个主要步骤:

  • 解析 HTML:浏览器从网络或本地获取 HTML 文件,然后开始解析,将 HTML 代码转换为 DOM(Document Object Model,文档对象模型)树。DOM 树是 HTML 文档的树形表示,它以节点的形式描述了页面的结构,比如标签之间的层级关系等。
  • 构建 CSSOM:同时,浏览器会解析 CSS 代码,无论是内部样式表、外部样式表还是内联样式,都会被解析并构建成 CSSOM(CSS Object Model,CSS 对象模型)树。CSSOM 树描述了页面元素的样式信息,用于后续计算元素的最终样式。
  • 合成渲染树:DOM 树和 CSSOM 树构建完成后,浏览器将两者结合,根据 DOM 中元素的结构和 CSSOM 中定义的样式,生成渲染树(Render Tree)。渲染树只包含那些需要显示在页面上的元素及其样式信息,例如

CSS 与文档解析的关系

从上述流程可以看出,CSS 本身并不直接阻塞 HTML 文档的解析。也就是说,浏览器在下载 HTML 文档的过程中,会一边下载一边解析,不会因为等待 CSS 的加载而暂停 HTML 的解析。然而,在实际情况中,CSS 确实可能导致文档解析的延迟,这主要与 JavaScript 脚本的执行以及渲染树的构建有关。

JavaScript 脚本执行的影响

由于 JavaScript 脚本可以操作 DOM 和 CSSOM,当浏览器在解析 HTML 过程中遇到

<!DOCTYPE html>
<html lang="en"><head><link rel="stylesheet" href="styles.css">
</head><body><div id="myDiv">这是一个测试div</div><script>const div = document.getElementById('myDiv');// 获取div的样式属性,比如宽度const width = window.getComputedStyle(div).width; console.log(width);</script>
</body></html>

在这个例子中,如果styles.css还没有完成下载和解析,当 JavaScript 脚本执行到获取元素样式的代码时,由于此时 CSSOM 尚未构建完成,获取到的样式值可能是错误的。为了避免这种情况,浏览器会延迟 JavaScript 脚本的执行以及 HTML 文档的解析,直到 CSSOM 构建完成。

渲染树构建的要求

渲染树的构建依赖于 DOM 树和 CSSOM 树。如果 CSS 还在加载或解析过程中,那么 CSSOM 树就不完整,此时无法准确构建渲染树。例如,假设一个网页中有大量复杂的 CSS 规则来定义元素的显示和隐藏、定位等样式,如果这些 CSS 没有加载完毕,浏览器无法确定哪些元素应该显示在页面上,也就无法正确构建渲染树。因此,在这种情况下,浏览器也会等待 CSS 的加载和解析,这间接导致了 HTML 文档解析的阻塞。

先加载 JavaScript 后加载 CSS 的问题及影响

若先加载 JavaScript 后加载 CSS,会引发一系列问题。

首先是页面闪烁,JavaScript 代码可能会操作 DOM,例如修改元素的显示、隐藏状态或样式等。由于 CSS 尚未加载,页面初始会以默认样式呈现,当 JavaScript 执行完相关操作后,CSS 才加载完成并应用样式,这就可能导致页面元素在短时间内从默认样式切换到最终样式,产生闪烁现象。比如一个图片元素原本在 JavaScript 中被设置为display:none,但 CSS 中又定义了其正常显示且有特定样式,用户可能会先看到图片消失,然后又突然出现并带有样式,影响视觉体验。

其次,脚本执行可能出现异常。JavaScript 脚本可能会依赖 CSS 样式来获取准确的元素尺寸、位置等信息进行操作。如果 CSS 未加载完成,那么getComputedStyle等获取样式的方法返回的值可能不准确。例如,在一个轮播图的 JavaScript 代码中,需要根据图片的宽度来计算切换动画的距离,若 CSS 未加载,获取的图片宽度可能是错误的,导致轮播图动画效果异常。

再者,渲染性能会下降。浏览器在渲染页面时,需要结合 DOM 和 CSSOM 构建渲染树。若先加载 JavaScript,而 JavaScript 又可能对 DOM 进行大量修改,之后 CSS 加载完成再应用样式,浏览器可能需要多次重新计算布局和重新渲染,增加了渲染的复杂性和时间成本,降低了渲染性能。

此外,当 JavaScript 修改 CSS 时,也存在潜在风险。恶意的 JavaScript 代码若被注入网页,就可能篡改 CSS 样式,破坏页面的布局和视觉效果。例如,将原本用于隐藏敏感信息的 CSS 样式修改为显示,从而导致信息泄露;或者恶意改变页面的字体、颜色、排版等,影响用户对页面内容的正常理解和使用。即便 CSS 已加载完成,JavaScript 操作 CSS 仍可能引发新问题。大量频繁的 JavaScript - CSS 操作,如在循环中不断修改元素的 CSS 属性,会导致浏览器频繁重新计算布局和重新绘制,严重影响页面性能。例如,通过 JavaScript 循环快速改变一个列表中所有项的背景颜色,页面会出现明显卡顿。而且,JavaScript 代码逻辑错误也可能导致 CSS 样式被错误修改。比如,错误地选择了元素,将原本应用于导航栏的样式错误地应用到了内容区域,破坏页面的视觉呈现。

解决办法

针对先加载 JavaScript 后加载 CSS 出现的问题,以及 JavaScript 修改 CSS 带来的潜在风险,有以下解决办法。
对于非关键的 JavaScript 脚本,可以使用async属性
<script async src="script.js"></script>,async会让浏览器在下载完脚本后立即执行,不会阻塞页面的解析和其他资源的加载,这样可以在一定程度上减少 JavaScript 对 CSS 加载的影响。
而对于需要按顺序执行且不会阻塞页面渲染的脚本,使用defer属性<script defer src="script.js"></script>、,defer会使脚本在 HTML 解析完成后、DOMContentLoaded 事件触发前执行,确保在 CSS 加载完成后再执行脚本,避免脚本执行时因 CSS 未加载而获取错误样式信息。

另外,将关键的 CSS 样式直接写在 HTML 文件的style标签内,这样浏览器在解析 HTML 时就能立即应用这些样式,减少页面闪烁的可能性

例如,对于页面的基本布局样式、字体样式等关键部分进行内联,保证页面初始显示的基本样式正确,后续再加载外部 CSS 文件来补充完整样式。

同时,在 JavaScript 代码中,避免在 CSS 未加载完成时就进行依赖样式的操作。可以通过监听DOMContentLoaded事件,确保 DOM 和 CSSOM 都已准备好后再执行相关逻辑。

例如:

document.addEventListener('DOMContentLoaded', function() {// 这里写依赖样式的JavaScript代码
});

实际案例分析

以一个电商网站的产品详情页为例。该页面包含丰富的产品图片、描述文字、价格信息以及购买按钮等元素,同时有大量的 CSS 代码用于控制页面的布局和样式,比如设置图片的尺寸和排列方式、文字的字体和颜色、按钮的样式和交互效果等。

当用户访问该页面时,浏览器开始下载 HTML 文档。在解析 HTML 过程中,遇到了外部 CSS 文件的链接,于是同时开始下载 CSS 文件。然而,由于 CSS 文件较大,包含了许多针对不同屏幕尺寸的响应式样式以及复杂的动画效果样式,下载和解析时间较长。

在 HTML 文档中,还包含一些用于实现交互功能的 JavaScript 代码,比如当用户点击某个产品特性标签时,显示或隐藏相关的详细说明。这些 JavaScript 代码在执行时,需要获取页面元素的当前样式来进行相应的操作。例如,判断某个元素是否已经应用了特定的隐藏样式,从而决定是显示还是隐藏该元素。

由于 CSS 文件尚未完成下载和解析,CSSOM 树不完整,JavaScript 无法获取到准确的样式信息。为了保证 JavaScript 能够正确执行,浏览器会暂停 HTML 文档的解析,等待 CSSOM 构建完成。这就导致了整个页面的渲染延迟,用户可能会看到页面长时间处于空白或部分加载的状态,影响了用户体验。

综上所述,虽然 CSS 本身并不直接阻塞 HTML 文档的解析,但在与 JavaScript 脚本执行以及渲染树构建相关的情况下,CSS 的加载和解析过程可能会导致文档解析的延迟。同时,先加载 JavaScript 后加载 CSS 会带来诸多问题,JavaScript 对 CSS 的修改也存在潜在风险。网页开发者在优化页面性能时,需要充分考虑这些因素,例如合理优化 CSS 代码,减少其体积和复杂度,采用异步加载 CSS 的方式,以及合理安排 JavaScript 代码的执行顺序等,以确保页面能够快速、流畅地渲染,同时保障网页的安全性和稳定性。

相关文章:

CSS 真的会阻塞文档解析吗?

在网页开发领域&#xff0c;一个常见的疑问是 CSS 是否会阻塞文档解析。理解这一问题对于优化网页性能、提升用户体验至关重要。要深入解答这个问题&#xff0c;需要从浏览器渲染网页的原理说起。 浏览器渲染网页的基本流程 浏览器在接收到 HTML 文档后&#xff0c;会依次进行…...

大模型的UI自动化:Cline 使用Playwright MCP Server完成测试

大模型的UI自动化:Cline 使用Playwright MCP Server完成测试 MCP MCP(Model Context Protocol),是一个开发的协议,标准化了应用程序如何为大模型提供上下文。MCP提供了一个标准的为LLM提供数据、工具的方式,使用MCP会更容易的构建Agent或者是基于LLM的复杂工作流。 最近…...

碰撞检测 | 图解凸多边形分离轴定理(附ROS C++可视化)

目录 0 专栏介绍1 凸多边形碰撞检测2 多边形判凸算法3 分离轴定理(SAT)4 算法仿真与可视化4.1 核心算法4.2 仿真实验 0 专栏介绍 &#x1f525;课设、毕设、创新竞赛必备&#xff01;&#x1f525;本专栏涉及更高阶的运动规划算法轨迹优化实战&#xff0c;包括&#xff1a;曲线…...

Python 基本数据类型

目录 1. 字符串&#xff08;String&#xff09; 2. 列表&#xff08;List&#xff09; 3. 字典&#xff08;Dictionary&#xff09; 4. 集合&#xff08;Set&#xff09; 5. 数字&#xff08;Number&#xff09; 6. 布尔值&#xff08;Boolean&#xff09; 1. 字符串&…...

突破“第一崇拜“:五维心理重构之路

一、视频介绍 在这个崇尚"第一"的时代&#xff0c;我们如何找到自己的独特价值&#xff1f;本视频将带您踏上五维心理重构之旅&#xff0c;从诗意人生的角度探讨如何突破"圣人之下皆蝼蚁"的局限。我们将穿越人生的不同阶段&#xff0c;从青春的意气风发到…...

KubeKey一键安装部署k8s集群和KubeSphere详细教程

目录 一、KubeKey简介 二、k8s集群KubeSphere安装 集群规划 硬件要求 Kubernetes支持版本 操作系统要求 SSH免密登录 配置集群时钟 所有节点安装依赖 安装docker DNS要求 存储要求 下载 KubeKey 验证KubeKey 配置集群文件 安装集群 验证命令 登录页面 一、Ku…...

UE5网络通信架构解析

文章目录 前言一、客户端-服务器架构&#xff08;C/S Model&#xff09;二、对等网络架构&#xff08;P2P&#xff0c;非原生支持&#xff09;三、混合架构&#xff08;自定义扩展&#xff09;四、UE5网络核心机制 前言 UE5的网络通信主要基于客户端-服务器&#xff08;C/S&am…...

实验3 知识表示与推理

实验3 知识表示与推理 一、实验目的 &#xff08;1&#xff09;掌握知识和知识表示的基本概念&#xff0c;理解其在AI中的深刻含义与意义&#xff1b; &#xff08;2&#xff09;熟悉AI中常用的知识表示方法的优缺点及其应用场景&#xff1b; &#xff08;3&#xff09;掌握产…...

基于Springboot银行信用卡额度管理系统【附源码】

基于Springboot银行信用卡额度管理系统 效果如下&#xff1a; 系统登陆页面 用户个人中心页面 新增信用卡申请页面 评估审核页面 管理员主页面 评估审核页面 操作日志管理页面 消费页面 研究背景 随着金融行业的快速发展和信息技术的不断进步&#xff0c;信用卡作为一种便捷…...

达梦数据库学习笔记@1

目录 达梦数据库学习笔记一、表空间管理&#xff08;一&#xff09;默认表空间&#xff08;二&#xff09;相关数据字典&#xff08;三&#xff09;表空间操作&#xff08;四&#xff09;临时表空间管理 二、重做日志管理&#xff08;一&#xff09;系统视图&#xff08;二&…...

图像处理篇---图像处理中常见参数

文章目录 前言一、分贝&#xff08;dB&#xff09;的原理1.公式 二、峰值信噪比&#xff08;PSNR, Peak Signal-to-Noise Ratio&#xff09;1.用途2.公式3.示例 三、信噪比&#xff08;SNR, Signal-to-Noise Ratio&#xff09;1.用途2.公式3.示例 四、动态范围&#xff08;Dyna…...

AI Agent实战:打造京东广告主的超级助手 | 京东零售技术实践

前言 自2022年末ChatGPT的问世&#xff0c;大语言模型&#xff08;LLM&#xff09;技术引发全球关注。在大模型技术落地的最佳实践中&#xff0c;智能体&#xff08;Agent&#xff09;架构显现出巨大潜力&#xff0c;成为业界的普遍共识&#xff0c;各大公司也纷纷启动Agent技…...

50周学习go语言:第1周 环境搭建

以下是为零基础学习者准备的详细第1周教程&#xff0c;包含环境搭建、工具配置和首个Go程序的完整操作指南&#xff1a; 一、Go语言环境安装&#xff08;Windows/macOS/Linux通用&#xff09; 1. 下载安装包 官网地址&#xff1a;https://go.dev/dl//根据系统选择对应版本&am…...

4. MySQL 逻辑架构说明

4. MySQL 逻辑架构说明 文章目录 4. MySQL 逻辑架构说明1. 逻辑架构剖析1.1 服务器处理客户端请求1.2 Connectors(连接器)1.3 第1层&#xff1a;连接层1.4 第2层&#xff1a;服务层1.5 第3层&#xff1a;引擎层1.6 存储层 2. SQL执行流程2.1 MySQL 中的 SQL 执行流程 2.2 MySQL…...

《AI与NLP:开启元宇宙社交互动新纪元》

在科技飞速发展的当下&#xff0c;元宇宙正从概念逐步走向现实&#xff0c;成为人们关注的焦点。而在元宇宙诸多令人瞩目的特性中&#xff0c;社交互动体验是其核心魅力之一。人工智能&#xff08;AI&#xff09;与自然语言处理&#xff08;NLP&#xff09;技术的迅猛发展&…...

面对STM32的庞大体系,如何避免迷失在细节中?

我第一次接触STM32时&#xff0c;我以为抱着开发板就是拥抱未来&#xff0c;实际上一开机就喜提四大耳光&#xff0c;看到卖家演示的MP3播放、TFT彩屏、网口通信好炫酷&#xff0c;忍不住买回来掌握这些神技&#xff0c;到最后发现最实用的还是开发板的关机键和复位键。 看视频…...

ragflow-RAPTOR到底是什么?请通俗的解释!

RAPTOR有两种不同的含义&#xff0c;具体取决于上下文&#xff1a; RAPTOR作为一种信息检索技术 RAPTOR是一种基于树状结构的信息检索系统&#xff0c;全称为“Recursive Abstractive Processing for Tree-Organized Retrieval”&#xff08;递归抽象处理树组织检索&#xff09…...

Linux系统移植之Uboot启动流程

Linux系统移植之Uboot启动流程 一&#xff0c;Uboot启动流程1.Uboot的两阶段1.1.第一阶段1.11.硬件初始化1.12.复制 U-Boot 到 RAM1.13.跳转到第二阶段 1.2.第二阶段1.21.C 语言环境初始化1.22. 硬件设备初始化1.23. 加载环境变量1.24. 显示启动信息1.25. 等待用户输入&#xf…...

【Open X-Embodiment】简单数据下载与预处理

文章目录 1. RLDS Dataset2. 处理成numpy格式3. 存储桶 1. RLDS Dataset 从 Octo 里面找到数据下载的代码 rlds_dataset_mod github 按照官网代码配置环境后&#xff0c;修改 prepare_open_x.sh&#xff0c;相当于只用 gsutil 下载数据&#xff1a; DOWNLOAD_DIR/mnt/data…...

【第四节】C++设计模式(创建型模式)-Builder(建造者)模式

目录 引言 一、Builder 模式概述 二、Builder 模式举例 三、Builder 模式的结构 四、Builder 模式的实现 五、Builder 模式的优缺点 六、总结 引言 Builder 模式是一种创建型设计模式&#xff0c;旨在将复杂对象的构建过程与其表示分离。通过一步步构建对象&#xff0c;…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

边缘计算医疗风险自查APP开发方案

核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

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

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

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别

【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而&#xff0c;传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案&#xff0c;能够实现大范围覆盖并远程采集数据。尽管具备这些优势&#xf…...

探索Selenium:自动化测试的神奇钥匙

目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...

c++第七天 继承与派生2

这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分&#xff1a;派生类构造函数与析构函数 当创建一个派生类对象时&#xff0c;基类成员是如何初始化的&#xff1f; 1.当派生类对象创建的时候&#xff0c;基类成员的初始化顺序 …...