ES6 模塊:現代 JavaScript 的模塊化方案
本文探討 ES6 模塊,並展示如何在轉譯器的幫助下使用它們。幾乎所有語言都有模塊的概念——一種在另一個文件中包含已聲明功能的方法。通常,開發人員會創建一個封裝的代碼庫,負責處理相關的任務。該庫可以被應用程序或其他模塊引用。其優勢在於:
JavaScript 中的模塊在哪裡?
幾年前開始 Web 開發的任何人都會驚訝地發現 JavaScript 中沒有模塊的概念。無法直接引用或包含一個 JavaScript 文件到另一個文件中。因此,開發人員求助於其他方法。
多種 HTML
除非使用適當的模塊模式,否則函數可能會覆蓋其他函數。早期的 JavaScript 庫因使用全局函數名或覆蓋原生方法而臭名昭著。
或者內聯:
<p>
</p>
<ul></ul>
腳本合併
<🎜>
</pre>
<p>
<strong>
</strong>
HTML 可以使用多个 <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>
标签中设置 type="module" 属性来加载。例如:
模塊回退<script>
标签可以获取其他域上的脚本,但模块是使用跨域资源共享 (CORS) 获取的。因此,不同域上的模块必须设置适当的 HTTP 标头,例如 Access-Control-Allow-Origin: *。最后,除非在
<script>
标签中添加 crossorigin="use-credentials" 属性并且响应包含标头 Access-Control-Allow-Credentials: true,否则模块不会发送 Cookie 或其他标头凭据。模块执行被延迟
<script>
标签的defer
属性会延迟脚本执行,直到文档加载并解析完毕。模块(包括内联脚本)默认情况下会延迟。示例:不支持模塊的瀏覽器不會運行 type="module" 腳本。可以使用 nomodule 屬性提供一個回退腳本,模塊兼容的瀏覽器會忽略該屬性。例如:
<🎜>登入後複製您應該在瀏覽器中使用模塊嗎?
瀏覽器支持正在增長,但現在切換到 ES6 模塊可能還為時過早。目前,最好使用模塊打包器來創建一個在任何地方都能工作的腳本。
在 Node.js 中使用 ES6 模塊
Node.js 在 2009 年發佈時,任何運行時不提供模塊都是不可想像的。採用了 CommonJS,這意味著可以開發 Node 包管理器 npm。從那時起,使用量呈指數級增長。 CommonJS 模塊的編碼方式與 ES2015 模塊類似。使用 module.exports 而不是 export:
<🎜> <🎜>登入後複製使用 require(而不是 import)將此模塊導入到另一個腳本或模塊中:
// 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); } module.exports = { PI, sum, mult };登入後複製require 也可以導入所有項目:
const { sum, mult } = require('./lib.js'); console.log(sum(1, 2, 3, 4)); // 10 console.log(mult(1, 2, 3, 4)); // 24登入後複製那麼,在 Node.js 中實現 ES6 模塊很容易,對嗎? 不對。 ES6 模塊在 Node.js 9.8.0 中位於標誌之後,並且至少要到版本 10 才會完全實現。雖然 CommonJS 和 ES6 模塊具有相似的語法,但它們的工作方式根本不同:
- ES6 模塊在執行代碼之前預先解析以解析進一步的導入。
- CommonJS 模塊在執行代碼時按需加載依賴項。
在上面的示例中這沒有區別,但請考慮以下 ES2015 模塊代碼:
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登入後複製ES2015 的輸出:
// 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';登入後複製使用 CommonJS 編寫的類似代碼:
<code>running two.js running one.js Hello from two.js</code>登入後複製CommonJS 的輸出:
// 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';登入後複製執行順序在某些應用程序中可能至關重要,如果在同一個文件中混合使用 ES2015 和 CommonJS 模塊會發生什麼?為了解決這個問題,Node.js 僅允許在擴展名為 .mjs 的文件中使用 ES6 模塊。擴展名為 .js 的文件將默認為 CommonJS。這是一個簡單的選項,它消除了大部分複雜性,並且應該有助於代碼編輯器和代碼檢查器。
您應該在 Node.js 中使用 ES6 模塊嗎?
ES6 模塊僅在 Node.js v10 及更高版本(於 2018 年 4 月發布)中實用。轉換現有項目不太可能帶來任何好處,並且會使應用程序與早期版本的 Node.js 不兼容。對於新項目,ES6 模塊提供了一種 CommonJS 的替代方案。語法與客戶端編碼相同,並且可能為同構 JavaScript 提供更簡單的途徑,同構 JavaScript 可以在瀏覽器或服務器上運行。
模塊混戰
標準化的 JavaScript 模塊系統花了多年時間才出現,並且花了更長時間才實現,但問題已經得到糾正。從 2018 年年中開始,所有主流瀏覽器和 Node.js 都支持 ES6 模塊,儘管在每個人都升級時應該預期會有一個切換延遲。今天學習 ES6 模塊,以便在明天從您的 JavaScript 開發中受益。
關於 ES6 模塊的常見問題解答 (FAQ)
(此處省略了原文檔中的FAQ部分,因為已經對全文進行了充分的偽原創)
以上是了解ES6模塊的詳細內容。更多資訊請關注PHP中文網其他相關文章!