文
章
目
录
章
目
录
在前端开发里,首屏优化是绕不开的,其中资源加载顺序和<script>
标签的defer
、async
属性运用,对页面性能有着关键影响。今天咱们就深入聊聊这俩知识点。
一、资源加载顺序和阻塞机制详解
(一)DOM构建的关键阶段
在网页加载过程中,DOM构建有几个关键阶段:
- domLoading:这个阶段就是浏览器开始解析收到的HTML字节流,相当于开工干活的第一步。
- domInteractive:到这个时候,HTML解析完成,DOM树也构建好了,网页的基本结构算是搭起来了。
- domContentLoaded:不仅DOM树准备好,CSSOM(层叠样式表对象模型)也就绪了,这时候就会触发一个事件,意味着页面的基本样式和结构都可以展示了。
- domComplete:所有像图片、脚本这类资源都加载完成,整个网页就完整了。
(二)CSS和JS的阻塞特性
CSS和JS在资源加载时,有着不同的阻塞行为,对页面渲染影响很大,具体看下面这个表格:
资源类型 | 阻塞行为 | 影响范围 |
---|---|---|
CSS | 会阻塞渲染树构建,可能导致页面空白,但不影响DOM解析 | 会阻塞后续JS的执行 |
JS | 会阻塞DOM构建和渲染,而且一旦加载到就立即执行 | 会让后续HTML解析暂停 |
举个典型场景,如果CSS文件在JS前面声明了,但是加载延迟了,就算JS先下载好了,也得等着CSS解析完才能执行。这就好比JS在排队等CSS,CSS没处理完,JS就只能干等着。
二、<script>标签加载模式对比
<script>
标签不同的属性,会让脚本的加载和执行方式大不一样,详细情况看下面表格:
属性 | 加载行为 | 执行时机 | 顺序保证 |
---|---|---|---|
无属性 | 同步加载,会阻塞HTML解析 | 立即执行 | 按照文档顺序 |
defer | 异步加载,不会阻塞HTML解析 | 在DOMContentLoaded事件触发前按顺序执行 | 按照加载顺序 |
async | 异步加载,同样不阻塞HTML解析 | 加载完成后立即执行 | 没有顺序保证 |
还有一种特殊场景是动态脚本,比如下面这段代码:
// 创建一个script元素
const script = document.createElement('script');
// 设置script的src属性为a.js
script.src = 'a.js';
// 将async属性设为false,强制按文档顺序执行
script.async = false;
// 将script元素添加到页面的body中
document.body.appendChild(script);
这段代码就是手动创建一个脚本元素,并且设置它按文档顺序执行。
三、开发实践中的场景选择
(一)使用defer的场景
- 当需要操作DOM元素时,比如初始化表单,就适合用
defer
。因为它能保证在DOM解析完成后再执行脚本,这样操作DOM就不会出错。 - 如果脚本之间存在依赖关系,像先加载库文件,再加载业务逻辑文件,
defer
就能保证按顺序执行。例如:
<script src="vue.js" defer></script>
<script src="app.js" defer></script> <!-- 保证vue.js先执行 -->
(二)使用async的场景
- 对于独立且没有依赖关系的脚本,比如数据分析的SDK,用
async
就很合适。它不会阻塞页面渲染,能让页面快速展示出来。 - 像错误日志上报这种优先级较低的后台任务,也可以用
async
,在不影响页面主要功能的前提下完成任务。例如:
<script src="analytics.js" async></script> <!-- 不阻塞页面渲染 -->
四、首屏性能优化实用建议
(一)关键路径优化
对于首屏展示必不可少的JS,可以选择内联的方式,或者使用无属性的<script>
标签加载。而那些不是关键的脚本,就用defer
或async
属性,这样能合理安排脚本加载顺序,提升首屏加载速度。
(二)资源优先级控制
通过<link rel="preload">
这个属性,可以提前加载核心的CSS和JS文件。这就好比提前把要用的东西准备好,等需要的时候就能直接用,减少等待时间。
掌握好资源加载顺序和defer
、async
的使用,对前端开发的首屏优化很重要,希望大家都能吃透!