HTML页面中怎么引入JavaScript脚本

作者:窗边听雨

时间:2026-10-30

浏览:119

全面讲解在HTML页面中引入JavaScript脚本的四种主流方式、加载执行机制、defer与async属性对比以及工程实践中的注意事项与排错方法。

文章目录

在 HTML 页面中引入 JavaScript 脚本主要有四种途径:通过 <script src="..."> 引入独立的外部 JS 文件、在 HTML 内部直接使用 <script> 标签嵌入代码、在 HTML 元素事件属性中编写行内脚本,以及使用现代标准的 type="module" ES 模块化引入。其中,引用外部脚本是实际开发中普遍推荐的标准方案,因为它可以实现代码与结构解耦,并充分利用浏览器的静态资源缓存机制提升访问性能。

一、四种引入 JavaScript 的具体写法

1. 外部脚本文件引入(推荐实践)

将 JavaScript 逻辑写在以 .js 为后缀的独立文件中,然后在 HTML 文档中通过 <script> 标签的 src 属性指定其存储路径。外部引入能够让结构(HTML)、表现(CSS)与行为(JavaScript)完全分离,便于多人协作维护。

<!-- index.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>引入外部脚本示例</title>
</head>
<body>
  <h1 id="title">欢迎访问页面</h1>

  <!-- 在页面底部引入外部 js 脚本 -->
  <script src="js/main.js"></script>
</body>
</html>

对应的外部 JavaScript 文件 js/main.js 内容:

// js/main.js
document.addEventListener('DOMContentLoaded', function () {
  var title = document.getElementById('title');
  if (title) {
    title.style.color = '#345E82';
  }
});

注意:当 <script> 标签带有 src 属性时,该标签内部书写的任何 JavaScript 代码都会被浏览器完全忽略。例如 <script src="main.js">console.log('无效');</script> 中的控制台打印语句不会被执行。

2. 内部脚本引入

如果不希望创建额外的文件,可以直接将脚本逻辑包裹在 <script> 与 </script> 标签之间。内部脚本通常用于页面独有的初始化配置或小型演示演示页面。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>内部脚本示例</title>
</head>
<body>
  <button id="alertBtn">点击提示</button>

  <script>
    var btn = document.getElementById('alertBtn');
    btn.addEventListener('click', function () {
      alert('已触发按钮点击事件!');
    });
  </script>
</body>
</html>

3. 行内事件脚本(不推荐)

早期 HTML 允许直接在标签的事件监听属性(例如 onclick、onmouseover)中嵌入简短的 JavaScript 代码,或者在 <a> 标签的 href="javascript:..." 中调用伪协议。

<!-- 行内事件属性 -->
<button onclick="alert('执行行内代码')">点击执行</button>

<!-- javascript 伪协议(极度不推荐) -->
<a href="javascript:console.log('链接被点击');">执行脚本链接</a>

工程规范警示:行内脚本将行为逻辑直接混入 HTML 结构,不仅破坏关注点分离、难以重用与调试,还可能在启用了严格的内容安全策略(Content Security Policy, CSP)的环境下被浏览器拦截,因此生产项目中应当避免使用。

4. 模块化脚本引入(ES Modules)

现代浏览器原生支持 ECMAScript 模块化标准。通过为 <script> 标签添加 type="module" 属性,可以直接在前端使用 import 和 export 语法。模块脚本自动采用严格模式(strict mode),并且具有独立的作用域,不会污染全局变量。

<!-- 引入模块化入口脚本 -->
<script type="module" src="js/app.js"></script>
// js/utils.js
export function formatName(name) {
  return name.trim().toUpperCase();
}

// js/app.js
import { formatName } from './utils.js';
console.log(formatName('antigravity'));
HTML页面中怎么引入JavaScript脚本
图 1:引入外部 JavaScript 文件的标准四步操作与排错验证流程

二、脚本放置位置对页面渲染的影响机制

浏览器在解析 HTML 文档时是自上而下顺序进行的。当解析器(HTML Parser)遇到普通的 <script> 标签时,会立即暂停 HTML 文档的解析和 DOM 树的构建,转而去下载并执行该脚本代码。只有在脚本执行完毕之后,浏览器才会继续往下解析 HTML。

1. 放在 <head> 区域的缺点

如果将同步执行的 <script> 放在 <head> 中,浏览器在页面主体内容(HTML DOM)尚未构建完成前就会被阻塞。如果脚本文件体积较大或网络请求较慢,用户将长时间面对白屏,页面交互体验变差。同时,此时脚本若直接尝试通过 document.getElementById() 等方法获取 <body> 中的元素,会得到 null 并抛出运行时异常。

2. 放在 <body> 底部的优势

将 <script> 放置在 </body> 闭合标签的紧邻上方,是经典的前端优化手段。此时,浏览器已经将整个页面的 DOM 结构解析渲染完毕,首屏视觉内容已经呈现给用户,脚本随后平滑加载执行,既消除了首屏白屏问题,也保证了脚本在访问 DOM 节点时能够准确获取。

HTML页面中怎么引入JavaScript脚本
图 2:常规同步脚本在 Head 与 Body 底部的 HTML 解析时序及首屏渲染机制对比

三、现代脚本加载控制属性:defer 与 async

为了既能将 <script> 放在 <head> 统一规范管理,又能避免首屏解析阻塞,HTML5 提供了两个专门用于异步控制外部脚本的布尔属性:defer 和 async。这两个属性仅对带有 src 的外部脚本有效。

1. defer(延迟执行,推荐)

  • 下载机制:浏览器遇到带 defer 的脚本时,后台异步并行下载该文件,不打断 HTML 解析。
  • 执行时机:待整个 HTML 页面完全解析完毕后、在 DOMContentLoaded 事件触发前顺序执行。
  • 顺序保证:页面中多个带有 defer 属性的脚本,会严格按照其在 HTML 中出现的先后顺序依次执行。
  • 适用场景:绝大多数依赖完整 DOM 树或各模块之间存在先后调用依赖的业务逻辑代码。

2. async(异步加载,立即执行)

  • 下载机制:与 defer 相同,脚本文件在后台异步并行下载,下载过程不阻塞 HTML 解析。
  • 执行时机:一旦脚本下载完成,浏览器立即暂停当前的 HTML 解析,转而执行该脚本代码;执行完成后再恢复 HTML 解析。
  • 顺序不确定:谁先下载完毕就先执行谁,完全脱离书写顺序。
  • 适用场景:完全独立的脚本,例如百度统计、Google Analytics、埋点探针或第三方广告展示,它们既不依赖 DOM 也与其他脚本无依赖关系。
HTML页面中怎么引入JavaScript脚本
图 3:常规加载、async 异步加载与 defer 延迟加载三种模式的时序模型对比

四、方案特性对比与技术选型

为了在实际工程项目中正确挑选适合的引入方式,下表归纳了不同方式在阻塞表现、加载顺序和主要应用场景上的核心差异:

引入方式 是否阻塞 HTML 解析 执行时机 执行顺序是否受保全 适用场景
普通 <script> (置于 Head) 严重阻塞(下载与执行均阻塞) 下载完成后立刻执行 严格按标签书写顺序 旧式极小型测试脚本(生产不推荐)
普通 <script> (置于 Body 底部) 不阻塞 DOM 初始构建 DOM 解析至末尾时执行 严格按标签书写顺序 无须构建工具的传统多页面系统
<script defer> 完全不阻塞(后台并行下载) HTML 解析完成后、DCL 事件前 严格按标签书写顺序 现代主流 Web 应用首选,业务逻辑与框架
<script async> 下载不阻塞,执行时短暂阻塞 谁先下载完毕谁立即执行 乱序(不可预测) 无依赖的独立统计代码、埋点脚本、广告探针
<script type="module"> 完全不阻塞(原生默认行为同 defer) HTML 解析完成之后顺序执行 严格按模块依赖图解析执行 现代支持 ES Modules 标准的原生前端架构
HTML页面中怎么引入JavaScript脚本
图 4:现代 Web 工程中 JavaScript 脚本引入选型决策拓扑与执行规范总结

五、常见错误与排查指南

1. 文件路径错误导致 404 Not Found

当浏览器无法在指定的 src 路径找到脚本文件时,开发者工具的 Console 面板将输出类似于 GET http://.../main.js net::ERR_ABORTED 404 (Not Found) 的错误。排查要点如下:

  • 相对路径基准:以当前 HTML 文件所在目录为基准,同级写 ./main.js,子目录写 js/main.js,父级目录写 ../js/main.js。
  • 大小写敏感:在本地 Windows 开发时路径可能对大小写不敏感,但部署到 Linux 服务器环境后会严格区分字母大小写,确保文件名拼写完全一致。
  • 扩展名遗漏:使用原生 ES 模块引入(import ... from './file.js')时,浏览器不会像 Node.js 那样自动补全 .js 扩展名,必须完整书写后缀。

2. 脚本先于 DOM 执行导致元素为 null

如果在 <head> 中没有添加 defer 属性而直接执行 DOM 操作,会抛出 Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')。

解决办法:优先添加 defer 属性或将脚本移至 </body> 前。如果出于特定需求必须放在头部且不使用 defer,需在外层包裹 DOM 就绪监听函数:

document.addEventListener('DOMContentLoaded', function () {
  // 确保此时 DOM 结构已安全生成
  var element = document.getElementById('myElement');
  if (element) {
    element.textContent = '内容更新完毕';
  }
});

3. 模块化脚本的本地跨域限制(CORS Error)

当通过 file:// 协议直接在浏览器双击打开本地 HTML 文件并引入 type="module" 脚本时,浏览器出于安全策略限制会拦截跨域请求,报错信息为 Access to script at 'file:///...' from origin 'null' has been blocked by CORS policy。

解决办法:开发时请通过本地 HTTP 服务器运行网页。例如使用 VS Code 的 Live Server 插件,或在命令行运行 npx serve、python3 -m http.server 8000,通过 http://localhost:8000 进行访问测试。

六、全文总结

在 HTML 页面中引入 JavaScript,既要考虑实现的简便性,更要兼顾网页的性能与健壮性:

  1. 首选方案:使用带有 defer 属性的外部脚本 <script defer src="路径"></script> 放置在 <head> 中,既保证不阻塞首屏,又保证依赖顺序。
  2. 传统备选:对于无构建流程的传统页面,直接将普通 <script src="路径"></script> 放在 </body> 标签底部。
  3. 独立脚本:无依赖的埋点与监控探针使用 async 属性。
  4. 现代应用:采用 type="module" 进行现代化模块化拆分与导入。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章