HTML里的src和href
约 1601 字大约 5 分钟
2026-05-03
src的作用是 指定要加载的资源路径,常出现在
img, video, audio, script, iframe等标签中,用于加载JS脚本、图像、音频、视频, 或嵌入的网页文件href 的作用是指定超链接的目标地址,或定义文档与外部资源的关联。 常用在
a, link, area等标签中。link中是引入外部样式表的地址
资源加载方式也有差异
浏览器解析到 src 时,会暂停其他资源的下载和处理,直道将该资源加载、编译、执行。即阻塞加载,仅针对脚本,图片的加载和渲染是异步的,不会阻塞 HTML 的解析
识别到 href 标签时,如
a,link,会并行下载资源,即浏览器可以同时处理超链接或引入样式表。
src 加载机制
触发立即加载: 浏览器在解析到带有 src 属性的元素时,会立即向指定 URL 发送请求。
阻塞情况:
阻塞行为:对于
<script>,如果没有加 async或 defer 属性,浏览器会暂停 HTML的解析,等待脚本加载并执行非阻塞行为:对于
<img>和<iframe>,资源加载是异步的,不会阻塞页面解析。使用
async属性,与 HTML 解析并行加载,加载完成后立即执行defer与 HTML 解析并行加载,执行顺序与页面解析顺序一致,且在 HTML解析完成后才执行图片懒加载: 添加
loading=lazy,图片进入视图附近时才加载。
其中蓝色代表 JS脚本网络加载时间,红色代表 JS 脚本执行时间,绿色代表 HTML 解析。
img 延迟加载属性loading
延迟加载:图片进入视口附近(通常是几百像素)时才加载
注意,要设置 width 和 height 属性,当图像的 loading 属性设置为 lazy 时,若其未与元素的可见区域发生交集,则永远不会被加载——即使加载后会改变该状态,因为未加载的图像其 width 和 height 属性值均为 0。为延迟加载的图片设置 width 和 height 可解决此问题。此操作同时有助于防止布局偏移。
那如何兼容老的浏览器呢?或者说,旧方法是如何实现的?可以用 data-src 替代 src + 监听滚动的方案
IntersectionObserver。 用于监听元素可见性。
另外的方案。 通过监听窗口变化的事件:
srcoll, resize, orientationchange,在里面判断每个图片的位置(top)是否在预期内。
<img class="lazy" data-src="example.jpg" src="placeholder.png" alt="示例图片" width="800" height="600">
<script>
// 获取所有懒加载图片
const lazyImages = document.querySelectorAll('.lazy');
// 加载图片的函数
function loadImage(img) {
img.src = img.dataset.src; // 把data-src赋值给src
img.classList.remove('lazy'); // 移除类名,避免重复处理
}
// 监听滚动/调整窗口/横竖屏切换事件
function handleIntersection(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) { // 图片进入视口
loadImage(entry.target);
observer.unobserve(entry.target); // 停止监听已加载的图片
}
});
}
// 使用IntersectionObserver(性能优于原生scroll监听)
if ('IntersectionObserver' in window) {
const observer = new IntersectionObserver(handleIntersection, {
rootMargin: '200px 0px' // 提前200px开始加载
});
lazyImages.forEach(img => observer.observe(img));
} else {
// 降级方案:兼容不支持IntersectionObserver的浏览器
const lazyLoad = () => {
lazyImages.forEach(img => {
const rect = img.getBoundingClientRect();
if (rect.top < window.innerHeight && rect.bottom > 0) {
loadImage(img);
}
});
};
window.addEventListener('scroll', lazyLoad);
window.addEventListener('resize', lazyLoad);
window.addEventListener('orientationchange', lazyLoad);
lazyLoad(); // 初始执行一次
}
</srcipt>IntersectionObserver的用法和原理
IntersectionObserver(交叉观察器)是浏览器提供的异步监听元素与视口(或指定容器)交叉状态的 API,简单说就是能精准判断 “元素是否进入 / 离开可视区域”,且不会像scroll事件那样高频触发,性能极佳。
先说下,为啥咱们用 scroll等这类监听性能差,主要原因咱们这个是在 JS 主线程里面执行的。且频繁调用 getBoundingClientRect 会触发强制同步布局,阻塞渲染
而 IntersectionObserver 它其实是在浏览器后台注册了监听任务,在每次布局更新时会自动计算结果,触发后将回调加入 JS 主线程的任务队列,而不是立即执行回调。且其回调的触发次数也仅仅只会在交叉状态变化时触发。利用浏览器已计算的布局数据,无须其他处理。
解析HTML → 构建DOM树 → 样式计算(Style) → 布局(Layout/Reflow) → 绘制(Paint) → 合成(Composite) → 显示到屏幕getBoundingClientRect
用于获取元素相对于浏览器视口的精准几何位置 / 尺寸(返回包含top/right/bottom/left/width/height等属性的对象)。
布局抖动(Layout Thrashing)
当我们执行此方法时,它需要实时、精准的布局数据才能返回正确结果,因此,调用它时,会造成如下结果
执行JS → 调用getBoundingClientRect() → 浏览器暂停JS执行 → 立即计算所有待处理的布局 → 返回结果 → 继续执行JS如果在高频触发的事件(如scroll、resize、mousemove)中频繁调用这个方法,会导致:
- JS 执行和布局计算频繁交替,浏览器无法做批量优化;
- CPU 占用率飙升(尤其是页面元素多的时候);
- 页面帧率(FPS)下降,出现明显卡顿、掉帧;
- 极端情况下会触发重复重绘 / 重排,进一步加剧性能问题。
href 的 rel 属性
预加载
rel=preload+as。配合as指定资源类型font/img/script/style,否则无法争取处理。用于处理首屏核心资源,如字体、关键图片等默认
stylesheet,优先加载,并立即应用prefetch。空闲时加载
link 的延迟加载<link rel="stylesheet" href="style.css" media="print" onload="this.media='all'">
当media匹配当前环境(如media="screen"/media="all"):浏览器会将该 CSS 标记为 “渲染阻塞资源”—— 暂停页面渲染,等待 CSS 加载完成并解析后,才继续渲染页面(避免无样式内容闪烁)。
当media不匹配当前环境(如media="print",当前是屏幕浏览):浏览器会异步加载该 CSS(不阻塞渲染),但不会解析和应用样式(因为认为是 “打印专用样式”)。
onload回调的作用:CSS 加载完成后,将media改为all(匹配所有环境),此时浏览器会立即解析并应用该 CSS 样式,完成延迟加载。