diff --git a/1-js/13-modules/01-modules-intro/article.md b/1-js/13-modules/01-modules-intro/article.md index 319794c91..249cbf873 100644 --- a/1-js/13-modules/01-modules-intro/article.md +++ b/1-js/13-modules/01-modules-intro/article.md @@ -1,32 +1,32 @@ -# Modules, introduction +# 模組 (Modules) 簡介 -As our application grows bigger, we want to split it into multiple files, so called "modules". A module usually contains a class or a library of functions. +當程式規模變大, 就會切分成多個檔案, 這每個檔案就是所謂的『模組』, 每個模組通常就包含一個類別或是函式庫。 -For a long time, JavaScript existed without a language-level module syntax. That wasn't a problem, because initially scripts were small and simple, so there was no need. +長久以來, JavaScript 在語言層次上並沒有模組的語法, 這並不會造成問題, 因為原本腳本都很小也不複雜, 因此並不需要模組。 -But eventually scripts became more and more complex, so the community invented a variety of ways to organize code into modules, special libraries to load modules on demand. +不過腳本終究越來越複雜, 於是社群就發明了多種將程式碼組織成模組, 以及在需要時載入模組的特殊用途函式庫。 -For instance: +例如: -- [AMD](https://en.wikipedia.org/wiki/Asynchronous_module_definition) -- one of the most ancient module systems, initially implemented by the library [require.js](http://requirejs.org/). -- [CommonJS](http://wiki.commonjs.org/wiki/Modules/1.1) -- the module system created for Node.js server. -- [UMD](https://github.com/umdjs/umd) -- one more module system, suggested as a universal one, compatible with AMD and CommonJS. +- [AMD](https://en.wikipedia.org/wiki/Asynchronous_module_definition) -- 早期的模組系統之一, 一開始是以 [require.js](http://requirejs.org/) 函式庫的形式實作。 +- [CommonJS](http://wiki.commonjs.org/wiki/Modules/1.1) -- Node.js 伺服器所建立的模組系統。 +- [UMD](https://github.com/umdjs/umd) -- 與 AMD 及 CommonJS 相容的另一種模組系統, 提出來作為一統模組系統的方案。 -Now all these slowly become a part of history, but we still can find them in old scripts. +上述這些都已慢慢成為歷史, 但在舊的腳本中還是會看到它們的蹤跡。 -The language-level module system appeared in the standard in 2015, gradually evolved since then, and is now supported by all major browsers and in Node.js. So we'll study it from now on. +語言層次的模組系統是在 2015 年的標準中現身, 逐漸成為主流瀏覽器與 Node 都支援的功能, 因此我們要學的就是這一種模組系統。 -## What is a module? +## 模組是什麼? -A module is just a file. One script is one module. +簡而言之, 模組就是單一個檔案, 單一個腳本檔就是模組。 -Modules can load each other and use special directives `export` and `import` to interchange functionality, call functions of one module from another one: +模組可以相互載入, 並且使用 `export` 與 `import` 特殊指示詞來交換功能, 叫用其他模組中的函式: -- `export` keyword labels variables and functions that should be accessible from outside the current module. -- `import` allows the import of functionality from other modules. +- `export` 關鍵字可以標示要讓模組外部使用的變數與函式。 +- `import` 可以從其他模組中匯入功能。 -For instance, if we have a file `sayHi.js` exporting a function: +舉例來說, 如果檔案 `sayHi.js` 匯出一個函式: ```js // 📁 sayHi.js @@ -35,7 +35,7 @@ export function sayHi(user) { } ``` -...Then another file may import and use it: +...那在在其他檔案中就可以匯入並使用這個函式: ```js // 📁 main.js @@ -45,27 +45,27 @@ alert(sayHi); // function... sayHi('John'); // Hello, John! ``` -The `import` directive loads the module by path `./sayHi.js` relative to the current file, and assigns exported function `sayHi` to the corresponding variable. +`import` 指示詞會載入由相對於目前檔案的 `./sayHi.js` 路徑指定的模組, 並將該模組匯出的函式 `sayHi` 匯入成對應的變數。 -Let's run the example in-browser. +讓我們在瀏覽器中執行看看這個範例。 -As modules support special keywords and features, we must tell the browser that a script should be treated as a module, by using the attribute ` ``` -### Module-level scope +### 模組層次的作用域 -Each module has its own top-level scope. In other words, top-level variables and functions from a module are not seen in other scripts. +每個模組都有自己的頂層作用域, 也就是說, 模組內頂層的變數與函式在其他的腳本中都是看不到的。 -In the example below, two scripts are imported, and `hello.js` tries to use `user` variable declared in `user.js`, and fails: +底下的範例匯入了兩個模組, 其中 `hello.js` 企圖使用宣告在 `user.js` 中的 `user` 變數, 因此失敗: [codetabs src="scopes" height="140" current="index.html"] -Modules are expected to `export` what they want to be accessible from outside and `import` what they need. +模組必須將要讓外界使用的項目用 `export` 匯出, 在外部腳本中比需用 `import` 匯入想要使用的項目。 -So we should import `user.js` into `hello.js` and get the required functionality from it instead of relying on global variables. +因此, 我們必須將 `user.js` 匯入到 `hello.js` 中才能取得需要的功能, 而不是使用全域變數。 -This is the correct variant: +正確的寫法是這樣: [codetabs src="scopes-working" height="140" current="hello.js"] -In the browser, independent top-level scope also exists for each ` @@ -104,15 +104,15 @@ In the browser, independent top-level scope also exists for each ` ``` -### In a module, "this" is undefined +### "this" 在模組中沒有定義 -That's kind of a minor feature, but for completeness we should mention it. +這雖然是個小差異, 不過為了完整性, 我們還是得提一下。 -In a module, top-level `this` is undefined. +在模組中, 頂層的 `this` 是未定義的。 -Compare it to non-module scripts, where `this` is a global object: +在非模組的腳本中, `this` 是全域的物件: ```html run height=0 ``` -## Browser-specific features +## 瀏覽器中的特別差異 -There are also several browser-specific differences of scripts with `type="module"` compared to regular ones. +在瀏覽器中, 加上 `type="module"` 的腳本與一般的腳本還有一些特別的差異。 -You may want skip this section for now if you're reading for the first time, or if you don't use JavaScript in a browser. +如果您是第一次閱讀本書, 或是並不會在瀏覽器中使用 JavaScript, 可以先略過這一節的內容。 -### Module scripts are deferred +### 模組腳本會被延遲執行 -Module scripts are *always* deferred, same effect as `defer` attribute (described in the chapter [](info:script-async-defer)), for both external and inline scripts. +不論是外部還是行內模組腳本, 都 *會被* 延遲執行, 如同加上 `defer` 屬性 (會在 [](info:script-async-defer) 說明) 那樣。 -In other words: -- downloading external module scripts ` -Compare to regular script below: +如果和底下非模組版本的腳本相比: ``` -Please note: the second script actually runs before the first! So we'll see `undefined` first, and then `object`. +請注意:第二個腳本會比第一個腳本先執行, 所以實際執行時, 會先看到第二個腳本顯示的 `undefined` , 然後才會看到第二個腳本顯示的 `object`。 -That's because modules are deferred, so we wait for the document to be processed. The regular script runs immediately, so we see its output first. +這就是因為模組會延遲執行, 因此是等到文件處理完才會執行, 但是非模組的腳本會立刻執行, 所以我們會先看到它的輸出結果。 -When using modules, we should be aware that the HTML page shows up as it loads, and JavaScript modules run after that, so the user may see the page before the JavaScript application is ready. Some functionality may not work yet. We should put "loading indicators", or otherwise ensure that the visitor won't be confused by that. +在使用模組時, 我們要體認到 HTML 內容會先顯示出來才會執行模組內的腳本, 因此使用者會在 JavaScript 應用備妥可用前先看到網頁內容, 這時有些功能可能還無法使用, 請務必在畫面上顯示『載入中』的指示, 否則就必須確認不會造成使用者的困惑。 -### Async works on inline scripts +### 在行內腳本形式的模組中可以使用 Async -For non-module scripts, the `async` attribute only works on external scripts. Async scripts run immediately when ready, independently of other scripts or the HTML document. +在非模組腳本中, `async` 屬性只能用在外部腳本, 會在載入完成後立刻執行, 不會等待其他腳本或是 HTML 文件內容完成。 -For module scripts, it works on inline scripts as well. +對於模組腳本, 即使是行內腳本, 也可以套用 'async' 屬性。 -For example, the inline script below has `async`, so it doesn't wait for anything. +例如以下的行內腳本就使用了 `async` 屬性, 所以並不會等待其他工作完成才執行。 -It performs the import (fetches `./analytics.js`) and runs when ready, even if the HTML document is not finished yet, or if other scripts are still pending. +它會執行擷取檔案 (`./analytics.js`), 並在擷取完成後立刻執行, 而不會等待 HTML 文件處理完成, 或是其他腳本執行結束。 -That's good for functionality that doesn't depend on anything, like counters, ads, document-level event listeners. +這對不需依賴其他部分的功能, 像是計數、廣告、建立文件層次的監聽器等等就很方便。 ```html - - + + ``` -### External scripts +### 外部腳本 -External scripts that have `type="module"` are different in two aspects: +加上 `type="module"` 的外部腳本會有兩個不一樣的地方: -1. External scripts with the same `src` run only once: +1. 同樣 `src` 的外部腳本只會執行一次: ```html - + ``` -2. External scripts that are fetched from another origin (e.g. another site) require [CORS](mdn:Web/HTTP/CORS) headers, as described in the chapter . In other words, if a module script is fetched from another origin, the remote server must supply a header `Access-Control-Allow-Origin` allowing the fetch. +2. 從其他來源 (例如其他網站) 下載外部腳本需要 [CORS](mdn:Web/HTTP/CORS) 表頭, 就像在 這章中所提到的。換句話說, 如果要從其他來源下載模組腳本檔, 遠端伺服器就必須在回應中加入 `Access-Control-Allow-Origin` 表頭, 允許瀏覽器執行該檔案。 ```html - - + + ``` - That ensures better security by default. + 這可以在預設情況下確保較佳的安全性。 -### No "bare" modules allowed +### 不允許載入沒有路徑的模組 -In the browser, `import` must get either a relative or absolute URL. Modules without any path are called "bare" modules. Such modules are not allowed in `import`. +在瀏覽器中, `import` 只能用在指明相對或是絕對路徑的模組, 沒有指明路徑的模組稱為 "bare" 模組, 這種模組不能用在 `import` 中。 -For instance, this `import` is invalid: +像是以下的 `import` 就不合語法: ```js -import {sayHi} from 'sayHi'; // Error, "bare" module -// the module must have a path, e.g. './sayHi.js' or wherever the module is +import {sayHi} from 'sayHi'; // Error, "bare" 模組 +// 模組一定要指明相對或是絕對路徑 ``` -Certain environments, like Node.js or bundle tools allow bare modules, without any path, as they have their own ways for finding modules and hooks to fine-tune them. But browsers do not support bare modules yet. +在特定的環境下, 像是 Node.js 或是打包工具中, 由於它們有自己自成體系的模組搜尋方式, 因此可以不指定模組的路徑, 但瀏覽器並不支援這樣的功能。 -### Compatibility, "nomodule" +### 使用 "nomodule" 維持相容性 -Old browsers do not understand `type="module"`. Scripts of an unknown type are just ignored. For them, it's possible to provide a fallback using the `nomodule` attribute: +舊式的瀏覽器並不支援 `type="module"`, 只會略過未知類型的腳本, 最好可以使用 `nomodule` 屬性作為退路: ```html run ``` -## Build tools +## 建置工具 -In real-life, browser modules are rarely used in their "raw" form. Usually, we bundle them together with a special tool such as [Webpack](https://webpack.js.org/) and deploy to the production server. +在實務中, 我們很少直接使用原生的個別模組, 而是透過特殊的工具, 像是 [Webpack](https://webpack.js.org/) 將模組打包在一起後才部署到產品環境伺服器 (production server) 上。 -One of the benefits of using bundlers -- they give more control over how modules are resolved, allowing bare modules and much more, like CSS/HTML modules. +使用打包工具的好處就是更能掌控要如何搜整模組, 包括 bare 模組以及 CSS/HTML 等不同形式的模組。 -Build tools do the following: +建置工具的工作流程如下: -1. Take a "main" module, the one intended to be put in ` ``` -That said, native modules are also usable. So we won't be using Webpack here: you can configure it later. +也就是說, 即使用到原生的模組經過轉換後也可以用在不支援模組的環境。在本教學中, 我們不會使用 Webpack, 之後若有需要你可以自己設定使用。 -## Summary +## 總結 -To summarize, the core concepts are: +以下總結核心概念: -1. A module is a file. To make `import/export` work, browsers need `