1. 帶你深入了解 Module

        共 8225字,需瀏覽 17分鐘

         ·

        2021-03-10 10:20

        模塊介紹

        當(dāng)我們的應(yīng)用程序變大時,我們想要把它分割成多個文件,也就是所謂的“模塊”。一個模塊可以包含一個用于特定目的的類或函數(shù)庫。

        很長一段時間以來,JavaScript都沒有語言級的模塊語法。這不是問題,因為最初的腳本很小很簡單,所以沒有必要。

        但最終腳本變得越來越復(fù)雜,因此社區(qū)發(fā)明了各種方法來將代碼組織到模塊中,以及根據(jù)需要加載模塊的特殊庫。

        • AMD——最古老的模塊系統(tǒng)之一,最初由require.js庫實現(xiàn)。

        • CommonJS -為Node.js服務(wù)器創(chuàng)建的模塊系統(tǒng)。

        • UMD -另一個模塊系統(tǒng),建議作為一個通用的,兼容AMD和CommonJS。

        現(xiàn)在所有這些慢慢地成為歷史的一部分,但我們?nèi)匀豢梢栽诠爬系哪_本中找到它們。

        語言級模塊系統(tǒng)于2015年出現(xiàn)在標準中,后來逐漸演變,現(xiàn)在所有主流瀏覽器和Node.js都支持它。因此,我們將從現(xiàn)在開始學(xué)習(xí)現(xiàn)代JavaScript模塊。

        什么是模塊

        模塊只是一個文件。一個腳本就是一個模塊。就這么簡單。

        模塊可以相互加載,并使用特殊的指令導(dǎo)出和導(dǎo)入來交換功能,從一個模塊調(diào)用另一個模塊的函數(shù):

        • export 關(guān)鍵字標簽變量和函數(shù),這些變量和函數(shù)應(yīng)該可以從當(dāng)前模塊外部訪問。

        • import 允許從其他模塊導(dǎo)入功能。

        例如,如果我們有一個文件sayHi.js導(dǎo)出一個函數(shù):

        // ?? sayHi.js
        export function sayHi(user{
          alert(`Hello, ${user}!`);
        }

        然后另一個文件可以導(dǎo)入并使用它:

        // ?? main.js
        import {sayHi} from './sayHi.js';

        alert(sayHi); // function...
        sayHi('John'); // Hello, John!

        import指令通過相對于當(dāng)前文件的path ./sayHi.js加載模塊,并將導(dǎo)出的函數(shù)sayHi賦給相應(yīng)的變量。

        讓我們在瀏覽器中運行這個示例。

        由于模塊支持特殊的關(guān)鍵字和特性,所以我們必須通過屬性

        sayHi.js

        export function sayHi(user{
          return `Hello, ${user}!`;
        }

        index.html

        <!doctype html>
        <script type="module">
          import {sayHi} from './say.js';

          document.body.innerHTML = sayHi('John');
        </script>

        核心模塊功能

        與“常規(guī)”腳本相比,模塊有什么不同?

        有一些核心特性,對瀏覽器和服務(wù)器端JavaScript都有效。

        use strict

        默認情況下,模塊總是使用嚴格模式的。例如,給未聲明的變量賦值會產(chǎn)生錯誤。

        <script type="module">
          a = 5// error
        </script>

        塊級作用域

        每個模塊都有自己的頂級作用域。換句話說,一個模塊中的頂級變量和函數(shù)在其他腳本中看不到。

        在下面的例子中,導(dǎo)入了兩個腳本,hello.js嘗試使用user.js中聲明的user變量:

        user.js

        let user = "John";

        hello.js

        alert(user); // no such variable (each module has independent variables)

        index.html

        <!doctype html>
        <script type="module" src="user.js"></script>
        <script type="module" src="hello.js"></script>

        模塊應(yīng)該導(dǎo)出它們希望從外部訪問的內(nèi)容,并導(dǎo)入它們需要的內(nèi)容。

        因此,我們應(yīng)該將user.js導(dǎo)入到hello.js中,并從中獲取所需的功能,而不是依賴全局變量。

        這是正確的變體:

        user.js

        export let user = "John";

        hello.js

        import {user} from './user.js';

        document.body.innerHTML = user; // John

        index.html

        <!doctype html>
        <script type="module" src="hello.js"></script>

        在瀏覽器中,每個<script type="module">對象都有獨立的頂級作用域

        <script type="module">
          // The variable is only visible in this module script
          let user = "John";
        </script>

        <script type="module">
          alert(user); /
        / Error: user is not defined
        </
        script>

        如果我們真的需要創(chuàng)建一個窗口級全局變量,我們可以顯式地將它分配給window,并作為window.user訪問。但這是一個需要充分理由的例外。

        模塊代碼只在第一次導(dǎo)入時才被求值

        如果同一個模塊被導(dǎo)入到其他多個位置,它的代碼只在第一次執(zhí)行,然后導(dǎo)出將被交給所有導(dǎo)入器。

        這有重要的后果。讓我們來看看他們的例子:

        首先,如果執(zhí)行一個模塊代碼會帶來副作用,比如顯示一條消息,那么多次導(dǎo)入它只會觸發(fā)一次-第一次:

        // ?? alert.js
        alert("Module is evaluated!");
        // Import the same module from different files

        // ?? 1.js
        import `./alert.js`// Module is evaluated!

        // ?? 2.js
        import `./alert.js`// (shows nothing)

        在實踐中,頂級模塊代碼主要用于初始化、內(nèi)部數(shù)據(jù)結(jié)構(gòu)的創(chuàng)建,如果我們想要某些東西可重用—導(dǎo)出它。

        現(xiàn)在,一個更高級的例子。

        比方說,一個模塊導(dǎo)出了一個對象:

        // ?? admin.js
        export let admin = {
          name"John"
        };

        如果從多個文件導(dǎo)入此模塊,則只在第一次評估該模塊,創(chuàng)建admin對象,然后傳遞給所有進一步的導(dǎo)入器。

        所有的導(dǎo)入器都只有一個admin對象:

        // ?? 1.js
        import {admin} from './admin.js';
        admin.name = "Pete";

        // ?? 2.js
        import {admin} from './admin.js';
        alert(admin.name); // Pete

        // Both 1.js and 2.js imported the same object
        // Changes made in 1.js are visible in 2.js

        所以,讓我們重申一下——這個模塊只執(zhí)行一次。導(dǎo)出將生成,然后它們將在導(dǎo)入器之間共享,因此,如果管理對象發(fā)生了更改,其他模塊將看到這一點。

        這樣的行為允許我們在第一次導(dǎo)入時配置模塊。我們可以設(shè)置它的屬性一次,然后在進一步導(dǎo)入時,它就準備好了。

        例如,admin.js模塊可能提供某些功能,但希望憑據(jù)從外部進入admin對象:

        // ?? admin.js
        export let admin = { };

        export function sayHi({
          alert(`Ready to serve, ${admin.name}!`);
        }

        在init.js 在應(yīng)用程序的第一個腳本中,我們設(shè)置admin.name。然后所有人都會看到它,包括從admin.js內(nèi)部調(diào)用:

        // ?? init.js
        import {admin} from './admin.js';
        admin.name = "Pete";

        另一個模塊也可以看到admin.name:

        // ?? other.js
        import {admin, sayHi} from './admin.js';

        alert(admin.name); // Pete

        sayHi(); // Ready to serve, Pete!

        import.meta

        導(dǎo)入的對象。元包含關(guān)于當(dāng)前模塊的信息。

        它的內(nèi)容取決于環(huán)境。在瀏覽器中,它包含腳本的url,或者當(dāng)前網(wǎng)頁的url,如果在HTML中:

        <script type="module">
          alert(import.meta.url); // script url (url of the html page for an inline script)
        </script>

        In a module, “this” is undefined

        這是一個小特性,但是為了完整性,我們應(yīng)該提到它。

        在模塊中,這是未定義的頂層。

        與非模塊腳本相比,它是一個全局對象:

        <script>
          alert(this); // window
        </script>

        <script type="module">
          alert(this); /
        / undefined
        </
        script>

        瀏覽器 特定功能

        與常規(guī)的腳本相比,使用type="module"的腳本還有一些特定于瀏覽器的差異。

        如果您是第一次閱讀,或者您沒有在瀏覽器中使用JavaScript,那么您可能想要跳過這一部分。

        模塊腳本被延遲

        <script type="module">
          alert(typeof button); // object: the script can 'see' the button below
          // as modules are deferred, the script runs after the whole page is loaded
        </script>

        Compare to regular script below:

        <script>
          alert(typeof button); /
        / button is undefined, the script can't see elements below
          /
        / regular scripts run immediately, before the rest of the page is processed
        </
        script>

        <button id="button">Button</button>

        請注意:第二個腳本實際上在第一個腳本之前運行!首先是undefined,然后是object。

        這是因為模塊被延遲了,所以我們等待文檔被處理。常規(guī)腳本立即運行,所以我們首先看到它的輸出。

        當(dāng)使用模塊時,我們應(yīng)該注意HTML頁面在加載時顯示,JavaScript模塊在加載后運行,所以用戶可能在JavaScript應(yīng)用程序準備好之前看到頁面。有些功能可能還不能工作。我們應(yīng)該設(shè)置“加載指示符”,否則將確保訪問者不會被混淆。

        異步在內(nèi)聯(lián)腳本上工作

        對于非模塊腳本,async屬性只對外部腳本有效。異步腳本在準備好后立即運行,獨立于其他腳本或HTML文檔。

        對于模塊腳本,它也適用于內(nèi)聯(lián)腳本。

        例如,下面的內(nèi)聯(lián)腳本是異步的,所以它不等待任何東西。

        它執(zhí)行導(dǎo)入(fetch ./analytics.js)并在準備好時運行,即使HTML文檔還沒有完成,或者其他腳本仍在等待中。

        這對于不依賴于任何東西的功能來說是很好的,比如計數(shù)器、廣告、文檔級事件偵聽器。

        <!-- all dependencies are fetched (analytics.js), and the script runs -->
        <!-- doesn't wait for the document or other <script> tags -->
        <script async type="module">
          import {counter} from '
        ./analytics.js';

          counter.count();
        </script>

        外部腳本

        有type="module"的外部腳本有兩個不同:

        具有相同src的外部腳本只運行一次:

        <!-- the script my.js is fetched and executed only once -->
        <script type="module" src="my.js"></script>
        <script type="module" src="my.js"></script>

        從另一個來源(例如另一個站點)獲取的外部腳本需要CORS頭,如“獲取:跨來源請求”章節(jié)所述。換句話說,如果一個模塊腳本是從另一個來源獲取的,遠程服務(wù)器必須提供一個頭部Access-Control-Allow-Origin允許獲取。

        <!-- another-site.com must supply Access-Control-Allow-Origin -->
        <!-- otherwise, the script won't execute -->
        <script type="module" src="http://another-site.com/their.js"></script>

        不允許出現(xiàn)裸模塊

        在瀏覽器中,import必須獲得一個相對URL或絕對URL。沒有任何路徑的模塊稱為“裸”模塊。這樣的模塊是不允許導(dǎo)入的。

        例如,此導(dǎo)入無效:

        import {sayHi} from 'sayHi'// Error, "bare" module
        // the module must have a path, e.g. './sayHi.js' or wherever the module is

        Compatibility, “nomodule”

        舊的瀏覽器不理解type="module"。未知類型的腳本將被忽略。對于它們,可以使用nomodule屬性提供回退:

        <script type="module">
          alert("Runs in modern browsers");
        </script>

        <script nomodule>
          alert("Modern browsers know both type=module and nomodule, so skip this")
          alert("Old browsers ignore script with unknown type=module, but execute this.");
        </
        script>


        瀏覽 149
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
          
          

            1. 国产在线一 | 成人午夜福利在线看 | 欧美aaa日韩aaa国产 | 成人免费激情视频 | 久久天天综合 |