LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

在 HTML 中引入 JavaScript 有哪几种方式?它们各自的优缺点是什么?

zhenglin
2026年2月3日 17:23 本文热度 63

在 HTML 中引入 JavaScript 有哪几种方式?它们各自的优缺点是什么?

核心答案

在 HTML 中引入 JavaScript 有 3 种主要方式

核心原则:生产环境优先使用外链式,配合 defer 或 async 优化加载性能。


深入解析

1. 三种方式详解

方式一:行内式(Inline)

<!-- 直接在 HTML 属性中写 JS -->

<button onclick="alert('点击了!')">点击我</button>

<a href="javascript:void(0)" onmouseover="console.log('悬停')">链接</a>

优点:

  • 快速测试、简单直观

缺点:

  • ❌ HTML 和 JS 强耦合,难以维护

  • ❌ 无法复用逻辑

  • ❌ 代码混乱,可读性差

  • ❌ 存在 XSS 安全风险

  • ❌ 无法利用浏览器缓存



方式二:内嵌式(Internal / Embedded)

<!DOCTYPE html>

<html>

<head>

    <script>

        // JS 代码写在 <script> 标签内

        function init() {

            console.log('页面初始化');

        }

    </script>

</head>

<body>

    <h1>内嵌式示例</h1>

</body>

</html>

优点:

  • ✓ 适合单页应用或小型项目

  • ✓ HTML 和 JS 在同一文件,便于调试

  • ✓ 可以访问页面中的所有元素

缺点:

  • ❌ HTML 文件体积变大

  • ❌ 无法被浏览器缓存(每次加载 HTML 都要重新加载 JS)

  • ❌ 多个页面无法共享同一份 JS 代码

  • ❌ 不符合关注点分离原则




方式三:外链式(External)⭐ 推荐

<!-- 基础用法 -->

<script src="js/app.js"></script>


<!-- 推荐用法:配合 defer -->

<script src="js/app.js" defer></script>


<!-- 或者 async(取决于场景) -->

<script src="js/analytics.js" async></script>

优点:

  • ✅ HTML 与 JS 分离,结构清晰

  • ✅ 可复用:多个页面共享同一个 JS 文件

  • ✅ 可缓存:浏览器缓存 JS 文件,提升加载速度

  • ✅ 便于维护:代码独立管理

  • ✅ 支持模块化:方便团队协作

缺点:

  • ⚠️ 需要额外的 HTTP 请求(但可通过缓存和打包优化)



2. <script> 标签的关键属性

defer 和 async 的区别

页面解析流程对比:


无属性(默认):

HTML解析 → 遇到script → 停止解析 → 下载JS → 执行JS → 继续解析HTML

                ↑ 阻塞页面渲染 ↑


defer:

HTML解析 → 并行下载JS → HTML解析完成 → 按顺序执行JS → DOMContentLoaded

            ↓ 不阻塞解析 ↓


async:

HTML解析 → 并行下载JS → 下载完立即执行 → 继续解析HTML

            ↓ 执行时机不确定 ↓

 

<!-- defer 推荐用法 -->

<script src="main.js" defer></script>

<script src="utils.js" defer></script>

<!-- 保证:utils.js 一定在 main.js 之前执行 -->


<!-- async 用法 -->

<script src="analytics.js" async></script>

<script src="ads.js" async></script>

<!-- 不保证执行顺序,谁先下载完谁先执行 -->

其他重要属性

 


3. 底层机制:浏览器如何加载和执行脚本


┌─────────────────────────────────────────────────────────┐

│                    浏览器渲染流程                         │

├─────────────────────────────────────────────────────────┤

│                                                          │

│  1. HTML Parser ──→ 构建 DOM 树                          │

│         ↓                                                 │

│  2. CSS Parser ──→ 构建 CSSOM 树                         │

│         ↓                                                 │

│  3. 合并 ──→ 渲染树(Render Tree)                        │

│         ↓                                                 │

│  4. Layout(布局)                                        │

│         ↓                                                 │

│  5. Paint(绘制)                                         │

│                                                          │

└─────────────────────────────────────────────────────────┘


遇到 <script> 时:


默认行为:

┌─────────┐

│ 停止解析 │ ← 阻塞 DOM 构建

└────┬────┘

     ↓

┌─────────┐

│ 下载 JS │ ← 如果是外链脚本

└────┬────┘

     ↓

┌─────────┐

│ 执行 JS │ ← 阻塞渲染

└────┬────┘

     ↓

┌─────────┐

│ 继续解析 │

└─────────┘


使用 defer/async:

┌─────────┐      ┌─────────┐

 │继续解析 │ ←→   │并行下载 │  ← 不阻塞

└─────────┘      └─────────┘




4. 最佳实践

<!DOCTYPE html>

<html lang="zh-CN">

<head>

    <meta charset="UTF-8">

    <title>最佳实践示例</title>


    <!-- CSS 放在 head 中 -->

    <link rel="stylesheet" href="styles.css">


    <!-- 预加载关键脚本 -->

    <link rel="preload" href="critical.js" as="script">

</head>

<body>

    <!-- 页面内容 -->


    <!-- 方案1:现代浏览器推荐 -->

    <script src="main.js" defer></script>

    <script src="app.js" defer></script>


    <!-- 方案2:需要立即执行的脚本(如 polyfill) -->

    <script>

        // 同步执行的小型脚本

    </script>


    <!-- 方案3:独立第三方脚本 -->

    <script src="analytics.js" async></script>


    <!-- 方案4:ES 模块 -->

    <script type="module" src="module.js"></script>


    <!-- 方案5:模块降级方案 -->

    <script type="module" src="modern.js"></script>

    <script nomodule src="legacy.js"></script>

</body>

</html>




5. 常见误区


❌ 误区1defer 和 async 功能一样

✅ 纠正defer 保证顺序且在 DOMContentLoaded 前执行,async 不保证顺序

❌ 误区2:把所有 <script> 都放在 <head> 里

✅ 纠正:传统放 </body> 前,现代用 defer 可放 head

❌ 误区3defer 的脚本一定在 DOMContentLoaded 前执行

✅ 纠正:大部分情况是的,但如果脚本很大或网络慢,可能在之后

❌ 误区4:多个 async 脚本按书写顺序执行

✅ 纠正async 脚本按下载完成顺序执行,顺序不可控




代码示例

示例1:三种引入方式对比

<!DOCTYPE html>

<html lang="zh-CN">

<head>

    <meta charset="UTF-8">

    <title>JS 引入方式对比</title>


    <!-- 方式1:行内式(不推荐) -->

    <button onclick="handleClick()">行内式按钮</button>


    <!-- 方式2:内嵌式 -->

    <script>

        function handleClick() {

            console.log('内嵌式函数被调用');

        }


        // 内嵌式可以直接操作页面

        document.addEventListener('DOMContentLoaded', function() {

            console.log('DOM 加载完成');

        });

    </script>


    <!-- 方式3:外链式(推荐) -->

    <script src="js/utils.js" defer></script>

</head>

<body>

    <h1>三种引入方式</h1>


    <!-- 行内式的完整示例 -->

    <div onmouseover="this.style.background='yellow'"

         onmouseout="this.style.background='white'">

        鼠标悬停变色

    </div>

</body>

</html>

示例2:defer vs async 实际效果

<!DOCTYPE html>

<html lang="zh-CN">

<head>

    <meta charset="UTF-8">

    <title>defer vs async</title>

</head>

<body>

    <h1>页面标题</h1>

    <p>内容...</p>


    <script>

        // 同步脚本:阻塞后续渲染

        console.log('1. 同步脚本开始');

        // 模拟耗时操作

        const start = Date.now();

        while (Date.now() - start < 2000) {}

        console.log('2. 同步脚本结束(阻塞了2秒)');

    </script>


    <p>这行内容被延迟显示了</p>


    <!-- defer 脚本 -->

    <script src="defer1.js" defer></script>

    <script src="defer2.js" defer></script>

    <!-- 保证:defer1.js 在 defer2.js 之前执行 -->


    <!-- async 脚本 -->

    <script src="async1.js" async></script>

    <script src="async2.js" async></script>

    <!-- 不保证:谁先下载完谁先执行 -->


    <script>

        document.addEventListener('DOMContentLoaded', function() {

            console.log('3. DOMContentLoaded 触发');

        });


        window.addEventListener('load', function() {

            console.log('4. 页面完全加载完成');

        });

    </script>

</body>

</html>

示例3:现代项目的标准引入方式

<!DOCTYPE html>

<html lang="zh-CN">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>现代项目</title>


    <!-- 预连接到 CDN -->

    <link rel="preconnect" href="https://cdn.example.com">


    <!-- 预加载关键资源 -->

    <link rel="preload" href="critical.css" as="style">

    <link rel="preload" href="critical.js" as="script">


    <!-- 关键 CSS -->

    <link rel="stylesheet" href="critical.css">


    <!-- Polyfill:需要立即执行且不依赖 DOM -->

    <script>

        // 检测和添加必要的 polyfill

        if (!window.Promise) {

            document.write('<script src="polyfills/promise.js"><\/script>');

        }

    </script>

</head>

<body>

    <div id="app"></div>


    <!-- 主要应用脚本:使用 defer -->

    <script src="vendors.js" defer></script>

    <script src="main.js" defer></script>


    <!-- 第三方统计:使用 async -->

    <script src="analytics.js" async></script>


    <!-- ES 模块 + 降级方案 -->

    <script type="module" src="modern-app.js"></script>

    <script nomodule src="legacy-app.js"></script>

</body>

</html>

示例4:动态加载脚本

// 动态创建 script 标签

function loadScript(url, options = {}) {

    return new Promise((resolve, reject) => {

        const script = document.createElement('script');

        script.src = url;


        // 设置属性

        if (options.async) script.async = true;

        if (options.defer) script.defer = true;

        if (options.type) script.type = options.type;


        // 事件监听

        script.onload = () => resolve(script);

        script.onerror = () => reject(new Error(`Failed to load ${url}`));


        document.head.appendChild(script);

    });

}


// 使用示例

async function initApp() {

    try {

        await loadScript('/utils.js', { defer: true });

        await loadScript('/main.js', { defer: true });

        console.log('所有脚本加载完成');

    } catch (error) {

        console.error('脚本加载失败:', error);

    }

}


// 条件加载

if ('IntersectionObserver' in window) {

    // 支持,加载现代版本

    loadScript('/modern-image-lazy-load.js');

} else {

    // 不支持,加载 polyfill

    loadScript('/polyfills/intersection-observer.js')

        .then(() => loadScript('/legacy-image-lazy-load.js'));

}


一句话总结

外链式 + defer 是现代网页引入 JavaScript 的最佳实践,它实现了代码分离、可缓存、不阻塞渲染的完美平衡。


参考文章:原文链接


该文章在 2026/2/3 17:24:12 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved