首页 >web前端 >js教程 >了解ES6模块

了解ES6模块

Lisa Kudrow
Lisa Kudrow原创
2025-02-15 10:57:11530浏览

Understanding ES6 Modules

ES6 模块:现代 JavaScript 的模块化方案

本文探讨 ES6 模块,并展示如何在转译器的帮助下使用它们。几乎所有语言都有模块的概念——一种在另一个文件中包含已声明功能的方法。通常,开发人员会创建一个封装的代码库,负责处理相关的任务。该库可以被应用程序或其他模块引用。其优势在于:

  1. 代码可以拆分成更小的、自包含功能的文件。
  2. 相同的模块可以在任意数量的应用程序之间共享。
  3. 理想情况下,模块无需被其他开发人员检查,因为它们已被证明有效。
  4. 引用模块的代码知道它是一个依赖项。如果模块文件被更改或移动,问题会立即显现。
  5. 模块代码(通常)有助于消除命名冲突。模块 1 中的函数 x() 不会与模块 2 中的函数 x() 冲突。可以使用命名空间等选项,使调用变为 module1.x() 和 module2.x()。

JavaScript 中的模块在哪里?

几年前开始 Web 开发的任何人都会惊讶地发现 JavaScript 中没有模块的概念。无法直接引用或包含一个 JavaScript 文件到另一个文件中。因此,开发人员求助于其他方法。

多种 HTML <p> </p> <ul></ul>

  • 然而,这并非理想方案:
  • 每个脚本都会发起一个新的 HTTP 请求,这会影响页面性能。HTTP/2 在一定程度上缓解了这个问题,但它无助于引用其他域(如 CDN)上的脚本。
  • 每个脚本在其运行期间都会暂停进一步的处理。
  • 依赖项管理是一个手动过程。在上面的代码中,如果 lib1.js 引用了 lib2.js 中的代码,则代码会失败,因为它尚未加载。这可能会破坏进一步的 JavaScript 处理。

    除非使用适当的模块模式,否则函数可能会覆盖其他函数。早期的 JavaScript 库因使用全局函数名或覆盖原生方法而臭名昭著。

    脚本合并

    <🎜>
    解决多个

    或者内联:

    模块只解析一次,无论它们在页面或其他模块中被引用多少次。

    <p> <strong> </strong></p>

    服务器注意事项

    <script> 标签</script>

    HTML 可以使用多个 <script></script> 标签加载任意数量的 JavaScript 文件:

    <script src="lib1.js"></script>模块必须使用 MIME 类型 application/javascript 提供服务。大多数服务器会自动执行此操作,但要注意动态生成的脚本或 .mjs 文件(请参阅下面的 Node.js 部分)。常规 <script src="lib2.js"></script><script src="core.js"></script>
    <script>console.log('inline code');</script><script></code> 标签问题的一种方法是将所有 JavaScript 文件合并成一个大型文件。这解决了一些性能和依赖项管理问题,但可能会导致手动构建和测试步骤。</p>
    <p><strong>模块加载器</strong></p>
    <p>RequireJS 和 SystemJS 等系统提供了一个库,用于在运行时加载和命名其他 JavaScript 库。模块在需要时使用 Ajax 方法加载。这些系统有所帮助,但对于大型代码库或添加标准 <code><script></code> 标签的网站来说,可能会变得复杂。</p>
    <p><strong>模块打包器、预处理器和转译器</strong></p>
    <p>打包器引入了编译步骤,以便在构建时生成 JavaScript 代码。代码经过处理以包含依赖项并生成单个 ES5 跨浏览器兼容的合并文件。流行的选项包括 Babel、Browserify、webpack 以及更通用的任务运行器,如 Grunt 和 Gulp。</p>
    <p>JavaScript 构建过程需要一些努力,但也有好处:</p>
    <ul>
    <li>处理是自动化的,因此人为错误的可能性较小。</li>
    <li>进一步的处理可以整理代码、删除调试命令、缩小结果文件等。</li>
    <li>转译允许您使用替代语法,如 TypeScript 或 CoffeeScript。</li>
    </ul>
    <p><strong>ES6 模块</strong></p>
    <p>上述选项引入了各种相互竞争的模块定义格式。广泛采用的语法包括:</p>
    <ul>
    <li>CommonJS——Node.js 中使用的 module.exports 和 require 语法</li>
    <li>异步模块定义 (AMD)</li>
    <li>通用模块定义 (UMD)</li>
    </ul>
    <p>因此,在 ES6 (ES2015) 中提出了单一的原生模块标准。</p>
    <p>ES6 模块内部的所有内容默认情况下都是私有的,并且在严格模式下运行(不需要“use strict”)。公共变量、函数和类使用 export 导出。例如:</p>
    <pre class="brush:php;toolbar:false"><code class="language-javascript">// lib.js
    export const PI = 3.1415926;
    export function sum(...args) {
      log('sum', args);
      return args.reduce((num, tot) => tot + num);
    }
    export function mult(...args) {
      log('mult', args);
      return args.reduce((num, tot) => tot * num);
    }
    // 私有函数
    function log(...msg) {
      console.log(...msg);
    }

    或者,可以使用单个 export 语句。例如:

    // lib.js
    const PI = 3.1415926;
    
    function sum(...args) {
      log('sum', args);
      return args.reduce((num, tot) => tot + num);
    }
    
    function mult(...args) {
      log('mult', args);
      return args.reduce((num, tot) => tot * num);
    }
    
    // 私有函数
    function log(...msg) {
      console.log(...msg);
    }
    
    export { PI, sum, mult };

    然后使用 import 将模块中的项目导入到另一个脚本或模块中:

    // main.js
    import { sum } from './lib.js';
    
    console.log(sum(1, 2, 3, 4)); // 10

    在这种情况下,lib.js 与 main.js 在同一个文件夹中。可以使用绝对文件引用(以 / 开头)、相对文件引用(以 ./ 或 ../ 开头)或完整 URL。可以一次导入多个项目:

    import { sum, mult } from './lib.js';
    
    console.log(sum(1, 2, 3, 4));  // 10
    console.log(mult(1, 2, 3, 4)); // 24

    并且可以为导入指定别名以解决命名冲突:

    import { sum as addAll, mult as multiplyAll } from './lib.js';
    
    console.log(addAll(1, 2, 3, 4));      // 10
    console.log(multiplyAll(1, 2, 3, 4)); // 24

    最后,可以通过提供命名空间来导入所有公共项目:

    import * as lib from './lib.js';
    
    console.log(lib.PI);            // 3.1415926
    console.log(lib.sum(1, 2, 3, 4));  // 10
    console.log(lib.mult(1, 2, 3, 4)); // 24

    在浏览器中使用 ES6 模块

    在撰写本文时,ES6 模块受 Chromium 系浏览器 (v63+)、Safari 11+ 和 Edge 16+ 支持。Firefox 支持将在版本 60 中到来(在 v58+ 中位于 about:config 标志之后)。使用模块的脚本必须通过在 <script></script> 标签中设置 type="module" 属性来加载。例如:

    模块回退<script></script> 标签可以获取其他域上的脚本,但模块是使用跨域资源共享 (CORS) 获取的。因此,不同域上的模块必须设置适当的 HTTP 标头,例如 Access-Control-Allow-Origin: *。
    

    最后,除非在 <script></script> 标签中添加 crossorigin="use-credentials" 属性并且响应包含标头 Access-Control-Allow-Credentials: true,否则模块不会发送 Cookie 或其他标头凭据。

    模块执行被延迟

    <script></script> 标签的 defer 属性会延迟脚本执行,直到文档加载并解析完毕。模块(包括内联脚本)默认情况下会延迟。示例:

    <!-- 运行在第二位 -->
    <script type="module">
      // 执行某些操作...
    </script>
    <script defer src="c.js"></script>不支持模块的浏览器不会运行 type="module" 脚本。可以使用 nomodule 属性提供一个回退脚本,模块兼容的浏览器会忽略该属性。例如:<script src="a.js"></script><pre class='brush:php;toolbar:false;'>&lt;🎜&gt;</pre>
    <p><strong>您应该在浏览器中使用模块吗?</strong></p>
    <p>浏览器支持正在增长,但现在切换到 ES6 模块可能还为时过早。目前,最好使用模块打包器来创建一个在任何地方都能工作的脚本。</p>
    <p><strong>在 Node.js 中使用 ES6 模块</strong></p>
    <p>Node.js 在 2009 年发布时,任何运行时不提供模块都是不可想象的。采用了 CommonJS,这意味着可以开发 Node 包管理器 npm。从那时起,使用量呈指数级增长。CommonJS 模块的编码方式与 ES2015 模块类似。使用 module.exports 而不是 export:</p>
    <pre class='brush:php;toolbar:false;'>&lt;🎜&gt;
    &lt;🎜&gt;</pre>
    <p>使用 require(而不是 import)将此模块导入到另一个脚本或模块中:</p>
    <pre class='brush:php;toolbar:false;'>// lib.js
    const PI = 3.1415926;
    
    function sum(...args) {
      log('sum', args);
      return args.reduce((num, tot) =&gt; tot + num);
    }
    
    function mult(...args) {
      log('mult', args);
      return args.reduce((num, tot) =&gt; tot * num);
    }
    
    // 私有函数
    function log(...msg) {
      console.log(...msg);
    }
    
    module.exports = { PI, sum, mult };</pre>
    <p>require 也可以导入所有项目:</p>
    <pre class='brush:php;toolbar:false;'>const { sum, mult } = require('./lib.js');
    
    console.log(sum(1, 2, 3, 4));  // 10
    console.log(mult(1, 2, 3, 4)); // 24</pre>
    <p>那么,在 Node.js 中实现 ES6 模块很容易,对吗?<em>不对</em>。ES6 模块在 Node.js 9.8.0  中位于标志之后,并且至少要到版本 10 才会完全实现。虽然 CommonJS 和 ES6 模块具有相似的语法,但它们的工作方式根本不同:</p>
    <ul>
    <li>ES6 模块在执行代码之前预先解析以解析进一步的导入。</li>
    <li>CommonJS 模块在执行代码时按需加载依赖项。</li>
    </ul>
    <p>在上面的示例中这没有区别,但请考虑以下 ES2015 模块代码:</p>
    <pre class='brush:php;toolbar:false;'>const lib = require('./lib.js');
    
    console.log(lib.PI);            // 3.1415926
    console.log(lib.sum(1, 2, 3, 4));  // 10
    console.log(lib.mult(1, 2, 3, 4)); // 24</pre>
    <p>ES2015 的输出:</p>
    <pre class='brush:php;toolbar:false;'>// ES2015 模块
    
    // ---------------------------------
    // one.js
    console.log('running one.js');
    import { hello } from './two.js';
    console.log(hello);
    
    // ---------------------------------
    // two.js
    console.log('running two.js');
    export const hello = 'Hello from two.js';</pre>
    <p>使用 CommonJS 编写的类似代码:</p>
    <pre class="brush:php;toolbar:false">&lt;code&gt;running two.js
    running one.js
    Hello from two.js&lt;/code&gt;</pre>
    <p>CommonJS 的输出:</p>
    <pre class='brush:php;toolbar:false;'>// CommonJS 模块
    
    // ---------------------------------
    // one.js
    console.log('running one.js');
    const hello = require('./two.js');
    console.log(hello);
    
    // ---------------------------------
    // two.js
    console.log('running two.js');
    module.exports = 'Hello from two.js';</pre>
    <p>执行顺序在某些应用程序中可能至关重要,如果在同一个文件中混合使用 ES2015 和 CommonJS 模块会发生什么?为了解决这个问题,Node.js 仅允许在扩展名为 .mjs 的文件中使用 ES6 模块。扩展名为 .js 的文件将默认为 CommonJS。这是一个简单的选项,它消除了大部分复杂性,并且应该有助于代码编辑器和代码检查器。</p>
    <p><strong>您应该在 Node.js 中使用 ES6 模块吗?</strong></p>
    <p>ES6 模块仅在 Node.js v10 及更高版本(于 2018 年 4 月发布)中实用。转换现有项目不太可能带来任何好处,并且会使应用程序与早期版本的 Node.js 不兼容。对于新项目,ES6 模块提供了一种 CommonJS 的替代方案。语法与客户端编码相同,并且可能为同构 JavaScript 提供更简单的途径,同构 JavaScript 可以在浏览器或服务器上运行。</p>
    <p><strong>模块混战</strong></p>
    <p>标准化的 JavaScript 模块系统花了多年时间才出现,并且花了更长时间才实现,但问题已经得到纠正。从 2018 年年中开始,所有主流浏览器和 Node.js 都支持 ES6 模块,尽管在每个人都升级时应该预期会有一个切换延迟。今天学习 ES6 模块,以便在明天从您的 JavaScript 开发中受益。</p>
    <p><strong>关于 ES6 模块的常见问题解答 (FAQ)</strong></p>
    <p><strong>(此处省略了原文档中的FAQ部分,因为已经对全文进行了充分的伪原创)</strong></p>

    以上是了解ES6模块的详细内容。更多信息请关注PHP中文网其他相关文章!

    声明:
    本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn