大白话 请简单说一下 src 与 href 他俩有什么区别?
上周帮新来的实习生改bug,屏幕上赫然一行红色报错:GET http://localhost:8080/logo.png 404
。我点开代码一看——<img href="./logo.png">
,当时差点把手里的咖啡泼键盘上。
“哥,我明明路径没错啊,为啥图片就是不出来?”实习生挠着头,黑眼圈比我熬夜改React项目时还重。
这场景是不是特眼熟?咱前端er天天跟HTML标签打交道,src
和href
就像俩穿同款衣服的孪生兄弟,看着差不多,脾气却差了十万八千里。用错了,轻则图片不显示、脚本跑不起来,重则整个页面白屏,老板在身后盯着时能让你手心冒汗。
今天咱就来给这俩“祖宗”做个全面体检,从3个核心差异讲到8段实战代码,再附赠面试时的“保命回答模板”。保证你看完之后,再遇到src
和href
的问题,就像吃了布洛芬一样——通透、舒服,再也不用对着屏幕发呆。
(偷偷说:文末有4个“坑王级”扩展问题,90%的前端在项目里踩过,记得看到最后)
问题场景:这些崩溃瞬间,你肯定经历过
先别急着翻技术文档,咱先来盘盘那些因为搞混src
和href
而踩过的坑,看看你中了几个:
- Vue项目里的图片谜案:用
v-bind:href
绑定图片路径,本地开发好好的,一打包上线全变成裂开的图标。后来才发现,webpack对src
引入的资源会特殊处理,href
只是个普通字符串。 - React组件的脚本灾难:在
useEffect
里动态创建link
标签加载JS,结果控制台疯狂报错“Uncaught SyntaxError: Unexpected token ‘<’”。原来加载脚本得用src
,href
只会让浏览器把JS当样式表解析。 - CSS加载的玄学延迟:把
link
标签的href
写成src
,页面加载时样式闪一下就没了。查了半天才知道,src
会让浏览器把CSS当成“必须执行的资源”,加载完就扔了,根本不会应用到DOM上。 - a标签的跳转失灵:想做个锚点跳转,写成
<a src="#footer">去底部</a>
,点半天没反应。同事路过瞥了一眼:“哥们儿,a
标签认href
不认src
,这是HTML5基础啊”。
这些坑本质上都是一个问题:没搞懂src
和href
在浏览器眼里到底是啥角色。接下来咱就扒开浏览器的“脑回路”,看看它是怎么处理这俩属性的。
技术原理:浏览器眼里的“搬运工”和“介绍信”
其实src
和href
的核心区别,用一句话就能说清:src
是“搬运工”,负责把外部资源搬进页面并替换自己;href
是“介绍信”,只告诉浏览器“我和某个资源有关系”,不搬东西。
咱来拆解开说:
关于src:“这东西我要了,现在就用!”
src
的全名叫“source”,翻译过来就是“源头”。当浏览器看到带src
的标签(比如<img>
、<script>
),会立刻停下手里的活儿,去下载这个资源,下载完之后直接把src
所在的标签换成资源内容。
比如<script src="app.js"></script>
,浏览器会:
- 暂停DOM解析(这就是为啥
script
放头部会阻塞页面加载) - 发送HTTP请求下载
app.js
- 下载完成后,把
<script>
标签换成app.js
里的代码并执行 - 执行完再继续解析后面的DOM
这就像你正在砌墙(解析DOM),突然发现少了块砖(外部资源),于是停下手里的活,亲自去建材市场把砖拉回来(下载资源),把砖嵌进墙里(替换标签),再继续砌墙。
关于href:“这是我朋友,你们认识下”
href
的全名叫“hypertext reference”,翻译是“超文本引用”。当浏览器遇到带href
的标签(比如<link>
、<a>
),只会记录“这个标签和某个资源有关联”,然后继续干自己的事,不会停下解析DOM。
比如<link href="style.css" rel="stylesheet">
,浏览器会:
- 记下“这个
link
标签关联着style.css
,是个样式表” - 继续解析后面的DOM,同时“偷偷”并行下载
style.css
- 下载完之后,把样式表应用到已解析的DOM上,不影响之前的解析流程
这就像你在写报告(解析DOM),看到一句“参考资料见附件A”(href
),你会先记下来附件A的位置,继续写报告,等有空了再去翻附件(下载资源),不会停下写报告的进度。
关键区别:阻塞 vs 不阻塞
这是最影响前端性能的一点:src
会阻塞DOM解析,href
不会。
为啥script
标签推荐放body
底部?就是因为src
加载JS时会阻塞DOM解析,放头部可能导致页面半天出不来。而link
标签加载CSS用href
,浏览器可以一边解析DOM一边下载CSS,两者并行,页面加载更快——这也是前端性能优化里“关键CSS内联”的底层逻辑。
代码示例:8段代码让你看清“谁在干活,谁在摸鱼”
光说原理太抽象,咱直接上代码。下面8段示例,包含正确用法、错误用法和对比效果,每句代码都加了注释,保证你一看就懂。
1. img标签:src是亲妈,href是后妈
<!-- 正确用法:用src引入图片 -->
<!-- 浏览器会下载pic.jpg,并用图片替换img标签 -->
<img src="pic.jpg" alt="风景图">
<!-- 错误用法:用href引入图片 -->
<!-- 浏览器只会记录“img标签和pic.jpg有关”,不会下载图片,所以显示alt文字 -->
<img href="pic.jpg" alt="风景图(这行会显示错误)">
效果对比:第一个img
会显示图片,第二个只会显示“风景图(这行会显示错误)”,打开控制台会看到“GET … 404”(如果路径对的话也不会显示,因为img
不认识href
)。
2. script标签:src是执行者,href是摆设
<!-- 正确用法:用src引入JS -->
<!-- 浏览器会下载app.js,暂停DOM解析,执行JS后继续解析 -->
<script src="app.js"></script>
<!-- 错误用法:用href引入JS -->
<!-- 浏览器不知道该怎么处理,不会下载JS,更不会执行 -->
<script href="app.js"></script>
<!-- 正确用法:内联JS不需要src -->
<script>
// 直接写在script标签里的代码,浏览器会直接执行
console.log('我是内联JS,不用src也能跑');
</script>
效果对比:第一个script
会执行app.js
里的代码,第二个啥也不做,第三个会在控制台输出文字。
3. link标签:href是联络员,src是捣蛋鬼
<!-- 正确用法:用href引入CSS -->
<!-- 浏览器会并行下载style.css,解析DOM的同时处理样式 -->
<link rel="stylesheet" href="style.css">
<!-- 错误用法:用src引入CSS -->
<!-- 浏览器会下载style.css,但不会应用样式,白下载了 -->
<link rel="stylesheet" src="style.css">
<!-- 正确用法:用href引入icon -->
<!-- 浏览器会下载favicon.ico,显示在标签页上 -->
<link rel="icon" href="favicon.ico">
效果对比:第一个link
会让页面应用style.css
的样式,第二个link
下载的CSS不会生效,第三个link
会在浏览器标签页显示图标。
4. a标签:只认href,跟src没关系
<!-- 正确用法:用href做跳转 -->
<!-- 点击会跳转到baidu.com,href还能写锚点#footer、邮箱mailto:test@163.com -->
<a href="https://www.baidu.com">去百度</a>
<!-- 错误用法:用src做跳转 -->
<!-- 点击没反应,a标签根本不认识src属性 -->
<a src="https://www.baidu.com">点我没用</a>
效果对比:第一个a
标签点击会跳转,第二个点击毫无反应。
5. iframe标签:src是窗口,href是空气
<!-- 正确用法:用src引入iframe内容 -->
<!-- 浏览器会下载page.html,在iframe里显示页面内容 -->
<iframe src="page.html" frameborder="0"></iframe>
<!-- 错误用法:用href引入iframe内容 -->
<!-- iframe不会加载任何内容,只会显示空白 -->
<iframe href="page.html" frameborder="0"></iframe>
效果对比:第一个iframe
会显示page.html
的内容,第二个是空白框。
6. Vue中的动态绑定:v-bind:src vs v-bind:href
<template>
<div>
<!-- 正确:动态绑定图片用v-bind:src -->
<!-- webpack会处理src路径,打包时正确引入图片 -->
<img :src="imgUrl" alt="vue图片">
<!-- 错误:动态绑定图片用v-bind:href -->
<!-- webpack不会处理href路径,打包后可能出现404 -->
<img :href="imgUrl" alt="vue图片(错误)">
<!-- 正确:跳转链接用v-bind:href -->
<a :href="linkUrl">去首页</a>
</div>
</template>
<script>
export default {
data() {
return {
imgUrl: require('./assets/pic.jpg'), // 注意:Vue中动态src需要用require
linkUrl: '/home'
}
}
}
</script>
效果对比:Vue项目中,第一个img
会正确显示图片,第二个img
可能显示错误,a
标签点击会跳转到/home
。
7. React中的资源引入:src的特殊待遇
import React from 'react';
// React中引入图片需要import,相当于src的作用
import logo from './logo.png';
function App() {
return (
<div>
{/* 正确:用src引入图片,需要先import */}
<img src={logo} alt="react logo" />
{/* 错误:直接写字符串路径,打包后会找不到 */}
<img src="./logo.png" alt="错误示例" />
{/* 正确:加载外部脚本用src */}
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</div>
);
}
效果对比:第一个img
能显示logo,第二个在开发环境可能正常,打包后会404(因为React的打包工具不会处理字符串路径)。
8. 性能优化:src的阻塞与破解
<!-- 问题:script的src会阻塞DOM解析,放头部会让页面加载变慢 -->
<script src="slow.js"></script> <!-- 假设这个JS要加载3秒 -->
<!-- 解决方案1:用async,下载时不阻塞,下载完立即执行(可能打乱顺序) -->
<script src="fast.js" async></script>
<!-- 解决方案2:用defer,下载时不阻塞,等DOM解析完再执行(保持顺序) -->
<script src="order1.js" defer></script>
<script src="order2.js" defer></script> <!-- 会在order1之后执行 -->
<!-- 解决方案3:把script放body底部,等DOM解析完再加载 -->
<body>
<!-- 页面内容 -->
<script src="last.js"></script>
</body>
效果对比:第一个script
会让页面停3秒再继续解析,async
和defer
的脚本不会阻塞,body
底部的脚本会在页面内容显示后再加载。
对比效果:一张表格分清“谁该干什么”
为了让大家更直观地对比,我做了一张表格,从定义、作用、浏览器处理、常见标签等8个维度整理了src
和href
的区别:
维度 | src(搬运工) | href(介绍信) |
---|---|---|
核心作用 | 引入外部资源并替换当前标签 | 建立当前文档与外部资源的关联 |
浏览器处理方式 | 暂停DOM解析,下载资源后执行/替换标签 | 不暂停DOM解析,并行下载资源(如果需要) |
是否阻塞解析 | 是(会阻塞,除非加async/defer) | 否(并行处理,不影响解析) |
资源加载完成后 | 资源内容会成为标签的一部分(比如JS执行、图片显示) | 资源会被浏览器记录关联(比如CSS应用、跳转目标) |
常见标签 | <img> 、<script> 、<iframe> 、<video> | <link> 、<a> 、<base> |
路径处理(框架中) | webpack/Vite会特殊处理(比如打包时替换路径) | 仅作为字符串处理,需要手动确保路径正确 |
典型错误场景 | 用src加载CSS导致样式不生效 | 用href加载图片导致图片不显示 |
性能影响 | 可能阻塞页面加载,需优化(async/defer等) | 几乎无阻塞,可放心放头部 |
面试题的两种回答方法:专业版 vs 大白话版
面试时被问到“src和href的区别”,该怎么回答?我准备了两种版本,根据面试官的风格选着用:
正常回答方法(专业版)
“src
和href
都是HTML中用于引入外部资源的属性,核心区别在于作用机制不同:
-
src
(source)用于引入必须嵌入到当前文档的资源,浏览器会暂停DOM解析,下载并执行该资源,然后用资源内容替换当前标签。例如<script src="app.js">
会下载并执行JS,<img src="pic.jpg">
会下载并显示图片。src
会阻塞DOM解析,因此在加载JS时通常需要用async
、defer
或放在body
底部优化性能。 -
href
(hypertext reference)用于建立当前文档与外部资源的关联,浏览器不会暂停DOM解析,只会记录关联关系并并行下载资源(如果需要)。例如<link href="style.css">
会关联CSS并应用样式,<a href="page.html">
会指定跳转目标。href
不阻塞解析,因此link
标签加载CSS可以放心放在头部。
此外,src
常用于<img>
、<script>
、<iframe>
等标签,href
常用于<link>
、<a>
、<base>
等标签,在Vue、React等框架中,src
引入的资源会被打包工具特殊处理,href
则作为普通字符串处理。”
大白话回答方法(接地气版)
“其实就是俩干活方式不一样的属性:
src
是个急性子,看到它浏览器就跟接到快递似的,立马停下手里的活去取,取回来当场拆开用,还得把自己的位置让给快递(替换标签)。所以<script>
用src
时会让页面卡一下,因为浏览器在等快递。
href
是个慢性子,就像给浏览器递了张名片,说“我认识某某资源,你们熟络下”,然后该干嘛干嘛。浏览器收到名片后,会抽空去联系那个资源(比如下载CSS),但手里的活(解析DOM)不停。所以<link>
加载CSS用href
,页面加载不会卡。
简单说,src
是“我要这个东西,现在就用”,href
是“我认识这个东西,需要时找它”。”
面试官追问时的加分回答
如果面试官继续问“在性能优化中怎么处理src的阻塞问题?”,可以这样答:
“主要有三种方式:一是给<script>
加async
或defer
属性,async
让JS下载时不阻塞,下载完立即执行;defer
让JS下载完等待DOM解析完再执行,还能保证顺序。二是把<script>
放在body
底部,等DOM解析完再加载。三是用动态创建script
标签的方式异步加载,比如在DOMContentLoaded
事件后加载非关键JS。这些方法在React、Vue项目中也常用,比如通过import()
动态导入组件,本质上也是优化资源加载的阻塞问题。”
总结:3句话记住核心区别
看到这里,估计大家对src
和href
已经门儿清了,最后用3句话总结下核心点:
- 作用不同:
src
是“拿来用”(引入并替换),href
是“认个亲”(建立关联)。 - 阻塞不同:
src
会阻塞DOM解析,href
不会。 - 场景不同:加载图片、JS、视频用
src
;加载CSS、做跳转、关联资源用href
。
记住这3点,以后写代码时就不会再把这俩兄弟弄混了。
扩展思考:4个“坑王级”问题解答
光懂基础还不够,实际开发中还有很多“变种问题”。我整理了4个前端er最常遇到的扩展问题,逐个解答:
问题1:为什么<script>
用src
时会阻塞CSSOM?
答:这是浏览器的“渲染流水线”决定的。浏览器解析HTML生成DOM,解析CSS生成CSSOM,两者结合才能生成渲染树(Render Tree)。当<script>
用src
加载JS时,JS可能会操作DOM或CSSOM(比如document.styleSheets
),所以浏览器会等CSSOM构建完再执行JS,这就导致src
加载的JS不仅阻塞DOM解析,还会阻塞CSSOM——这也是“关键CSS内联”能提升首屏加载速度的原因(减少CSSOM构建时间,让JS更快执行)。
问题2:<link>
标签的href
加载CSS时,会阻塞渲染吗?
答:会阻塞渲染,但不会阻塞DOM解析。浏览器在解析到<link href="style.css">
时,会继续解析DOM,但渲染树的生成必须等CSSOM构建完,所以如果CSS加载慢,会导致页面“白屏”或“无样式内容闪烁(FOUC)”。解决办法是:把关键CSS内联到<style>
标签,非关键CSS用media="print"
延迟加载,或通过JS动态加载。
问题3:Vue的v-bind:src
和v-bind:href
在打包时有什么区别?
答:在Vue项目中,v-bind:src
会被webpack/Vite特殊处理:如果路径是相对路径(比如./pic.jpg
),打包工具会把图片当成资源处理,生成哈希文件名(比如pic.8f32d.jpg
),并自动替换路径;而v-bind:href
只会把路径当成普通字符串,打包后不会改变。所以在Vue中动态加载图片必须用v-bind:src
,且路径需要用require
包裹(Vue2)或import
引入(Vue3),否则会出现404。
问题4:为什么<base>
标签的href
能影响整个页面的相对路径?
答:<base>
标签的href
是页面所有相对路径的“基准地址”。比如设置<base href="https://www.example.com/page/">
后,页面中所有的相对路径(比如<img src="pic.jpg">
)都会被解析为https://www.example.com/page/pic.jpg
。这是href
的“全局关联”特性,它不引入资源,只定义路径规则,所以<base>
只能用href
不能用src
。
结尾:你踩过最离谱的src/href的坑是啥?
写到这里,关于src
和href
的知识点就全讲完了。从凌晨改bug的崩溃,到浏览器的处理机制,再到面试回答技巧,希望这些内容能让你下次遇到这俩属性时,不再头大。
其实前端开发就是这样,很多基础知识点看着简单,实际用起来全是细节。就像src
和href
,懂了是“理所当然”,不懂就是“天坑不断”。
别忘了点赞+收藏,下次遇到类似问题,直接翻出来看——毕竟好记性不如烂笔头,好文章不如常回顾~