溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

ECMAScript?modules規(guī)范怎么寫

發(fā)布時間:2022-11-15 09:13:20 來源:億速云 閱讀:195 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要講解了“ECMAScript modules規(guī)范怎么寫”,文中的講解內(nèi)容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“ECMAScript modules規(guī)范怎么寫”吧!

    基本語法

    ES modules 是 JavaScript 的標準模塊系統(tǒng),模塊是一個簡單的 JavaScript 文件,在這個文件中包含 export 或者 import 關(guān)鍵字。export 用于將模塊中聲明的內(nèi)容導出,import 用于從其他模塊中導入。

    模塊導出的4種寫法

    模塊導出用到的關(guān)鍵字是 export,它只能在模塊頂層使用。模塊可以導出函數(shù)、類、或其他基本類型等。模塊導出有4種寫法

    • 默認導出

    export default function myFunc() {}
    export default function () {}
    export default class MyClass {}
    export { foo as default }
    export default 'Hello Es modules!'
    • 行內(nèi)命名導出

    export function myFunc() {}
    export class MyClass {}
    export const fooStr = 'Hello Es modules!'
    • 通過一個 export 子句批量命名導出

    function myFunc() {}
    class MyClass {}
    const fooStr = 'Hello Es modules!'
    export {myFunc, MyClass , fooStr } // 在這個地方一次性導出多個
    • 重新導出

    // 重新導出 other_module 中除默認導出之外的內(nèi)容
    export * from './other_module.js'
    // 重新導出 other_module 中的默認導出
    export { default } from './other_module.js'
    // 重新導出 other_module 中的默認導出,并且將 other_module 中的 sayName 命名為 getName 之后再導出
    export { default, sayName as getName } from './other_module.js'

    雖然模塊導出有4種寫法,但是只有兩種方式,一種默認導出,另一種是命名導出,在同一個模塊中命名導出可以有多個,默認導出只能有一個,這兩種方式可以混合使用。

    在軟件開發(fā)的過程中,通常有多種寫法能到達同一目的,但并不是每一種寫法都值得推薦,模塊導出也是類似的。如果在同一個模塊中,即有默認導出,又有行內(nèi)命名導出,還有 export 子句批量命名導出,那么你的模塊很可能會變得混亂。在這里我推薦使用默認導出,并且將 export default 放在模塊的末尾。如果你必須要命名導出,我推薦使用export 子句批量命名導出,并將 export 子句放在文件的末尾。

    3中模塊說明符

    介紹完模塊導出之后,按理說應該介紹模塊導入,但我決定先介紹模塊說明符,這是因為模塊導入依賴模塊說明符。說明符是字符串字面值,它表示導入模塊的路徑,說明符一共有三種類型,分別是:相對路徑、絕對路徑和 bare(裸 露) 模式。

    • 相對路徑

    import foo from './myModule.js'
    import { sayName } from '../other_module.js'

    相對路徑說明符以 / 、./ 、../ 開頭,當使用相對路徑說明符時不能省略文件的擴展名。在 web 項目開發(fā)中使用相對路徑導入模塊的時候,你可能省略了文件擴展名,它還是能夠工作,那是因為你的項目使用了如 webpack 這樣的模塊打包工具。

    • 絕對路徑

    import React from 'https://cdn.skypack.dev/react'

    上述代碼表示從 cdn 導入模塊,當使用絕對路徑導入模塊時,是否能省略文件擴展名,這與服務器配置相關(guān)。

    • bare(裸 露) 模式

    import React from 'react'
    import Foo from 'react/lib.js'

    bare 模式從 node_module 中導入模塊,在 web 項目開發(fā)中,用這種說明符導入模塊很常見,但是 ES modules 并不支持它,在你的項目中,你之所以能夠使用它,是因為你的項目用了如 webpack 這樣的模塊打包工具。

    到目前為止,我已經(jīng)介紹完了3種模塊說明符,ES modules 只支持其中兩種,分別是:相對路徑和絕對路徑。

    模塊導入的 6 寫法

    模塊導入用到的關(guān)鍵字是 import,import 與 export 一樣只能在模塊頂部使用,模塊說明符不能包含變量,它必須是固定的字符串字面量。模塊導入有6種不同的寫法,如下:

    • 默認導入

    // 你可以將 myFunc 改成任何你喜歡的變量名
    import myFunc from './myModule.js'
    • 將模塊作為一個對象導入(即命名空間導入)

    import * as api from './myModule.js'
    // 通過對象的 default 屬性訪問 myModule.js 中的默認導出
    console.log(api.default)
    • 命名導入

    //   導入 myModule.js 中的fooStr 
    import { fooStr } from './myModule.js'
    // 將myModule.js中默認導出命名為myFunc 
    import { default as myFunc } './myModule.js'
    // 將 myModule.js中的 fooStr 命名為 myStr
    import { fooStr as myStr } from './myModule.js'

    當某個模塊中導出了很多內(nèi)容,而你只需要用到它導出的一部分內(nèi)容,你可以使用這個寫法只導入你需要的部分,在做搖樹優(yōu)化的時候這至關(guān)重要。

    • 只加載模塊,不導入任何東西

    import './myModule.js'

    不會將 myModule.js 中的任何內(nèi)容導入到當前模塊,但是會執(zhí)行 myModule.js 模塊體,這通常用于執(zhí)行一些初始化操作。

    • 將默認導入與命名導入混合使用

    import myFunc, { fooStr  } from './myModule.js'
    • 將默認導入與命名空間導入混合使用

    import myFunc, * as api from './myModule.js'

    補充:同一個模塊可以被多次導入,但是它的模塊體只會執(zhí)行一次

    ES modules的 4 個特點

    導入是導出的只讀引用

    例如有個模塊 A,它導出了一個變量 count,模塊 B 導入模塊 A 的 count,count 對模塊 B 而言是只讀的,所以在模塊 B 中不能直接修改 count,下面用代碼演示一下:

    // 模塊A的代碼如下:
    export var count = 0 // 注意:這里用的是 var 關(guān)鍵字
    // 模塊B的代碼如下:
    import { count  } from './moduleA.js'
    count++ //  Uncaught TypeError: Assignment to constant variable

    將上述代碼放在瀏覽器中運行,瀏覽器會報錯,錯誤類型是:TypeError。如果模塊 A 導出了對象 obj,在模塊 B 中不能直接給 obj 賦值,但是可以增、刪、改 obj 中的屬性。

    現(xiàn)在我已經(jīng)介紹了只讀的含義,下面介紹引用的含義。引用意味著在項目中多個模塊用的是同一個變量,例如:模塊 B 和模塊 C 都導入了模塊 A 的 count 和 changeCount 函數(shù),模塊 B 通過 changeCount 修改了 count 的值,模塊C中的 count 會被一同修改,代碼如下:

    // 模塊A的代碼如下:
    export var count = 0 
    export function changeCount() {
    	count++
    }
    // 模塊B的代碼如下:
    import { count, changeCount } from './moduleA.js'
    changeCount ()
    console.log(count) // 1
    // 模塊C的代碼如下:
    import { count } from './moduleA.js'
    console.log(count) // 1

    模塊 B 和模塊 C 導入的是引用,而非副本,模塊導出的變量在整個項目中是一個單例。

    支持循環(huán)依賴

    循環(huán)依賴指的是兩個模塊相互依賴,比如模塊 A 導入了模塊 B,模塊 B 又導入了模塊 A。盡管 ES modules 支持循環(huán)依賴,但應該避免,因為這會使兩個模塊強耦合。ES modules 支持循環(huán)依賴這是因為導入是導出的只讀引用。

    導入會被提升

    如果你知道 JavaScript 函數(shù)提升,那么你很容易理解 ES modules 的導入提升。由于 ES modules 的導入會被提升到模塊作用域的開頭,所以你不需要先導入再使用。下面的代碼可以工作:

    foo()
    import foo from './myModule.js'

    導出和靜態(tài)導入必須位于模塊的頂層

    導出必須位于模塊的頂層這一點毋庸置疑,在 ECMAScript 2020 規(guī)范中添加了動態(tài)導入,它使模塊導入可以不必位于模塊的頂層。在后面會單獨介紹動態(tài)導入,在這里介紹的是靜態(tài)導入。

    ECMAScript 2020 之前,JavaScript 的 ES modules 是一個靜態(tài)模塊系統(tǒng),它意味著模塊的依賴項在你寫代碼的時候就確定了,不用等到代碼運行階段才確定,這讓代碼打包工具,如 webpack,很容易就能分析出 ES 模塊中的依賴,給搖樹優(yōu)化提供了便利。

    即便 ECMAScript 2020 增加了動態(tài)導入,靜態(tài)導入與動態(tài)導入在寫法上有差異,靜態(tài)導入使用 import 關(guān)鍵字,動態(tài)導入使用 import()。靜態(tài)導入只能位于模塊頂層。

    模塊與常規(guī)JavaScript腳本的差異

    • 模塊運行在嚴格模式下

    • 模塊具備詞法頂部作用域

    這句話的意思是,在模塊中創(chuàng)建的變量,如:foo,不能通過 window.foo 訪問。代碼如下:

    var foo = 'hi'
    console.log(window.foo) // undefined
    console.log(foo) // hi
    export {} // 將這個文件標記成模塊

    在模塊中的聲明的變量是針對該模塊的,這意味著在模塊中聲明的任何變量對其他模塊都不可用,除非它們被顯式地導出。

    • 模塊中的 this 關(guān)鍵字沒有指向全局 this,它是 undefined,如果要在模塊中訪問全局 this 要使用 globalThis,在瀏覽器中 globalThis 是 window 對象。

    • export 和靜態(tài)導入 import 只能在模塊中使用

    • 在模塊頂層能使用 await 關(guān)鍵字,在常規(guī) JavaScript 腳本中只能在 async 函數(shù)中使用 await 關(guān)鍵字

    注意:由于 JavaScript 運行時會區(qū)別對待模塊和常規(guī)的 JavaScript 腳本,所以在寫代碼的時候做好顯示地標記 JavaScript 文件是模塊,只要 JavaScript 文件中包含 export 或者 import 關(guān)鍵字,JavaScript 運行時就會認為這個文件是模塊

    在瀏覽器中使用 ES modules

    現(xiàn)代瀏覽器支持 ES modules,你可以將 script 標簽的 type 屬性設(shè)置為 module 來告訴瀏覽器這個腳本是模塊,代碼如下:

    <!--外部模塊-->
    <script type="module" src="./module.js"></script>
    <!--內(nèi)聯(lián)模塊-->
    <script type="module">
       import {count} from './moduleA.js';
       import React from 'https://cdn.skypack.dev/react'
       console.log(count, React)
    </script>

    出于對兼容性的考慮,可能還需要 <script nomodule src=&rsquo;xxx.js&rsquo;></script>,在這里不做介紹。

    在之前介紹了模塊和常規(guī) JavaScript 腳本與運行環(huán)境無關(guān)的差異,現(xiàn)在來介紹在瀏覽器環(huán)境中二者的差異

    • 模塊只會被執(zhí)行一次

    不管模塊被引入了多少次,它只會被執(zhí)行一次,而常規(guī)的 JavaScript 腳本執(zhí)行次數(shù)與它被添加到 DOM 的次數(shù)一致,添加多少次就執(zhí)行多少次。比如有下面一段代碼:

    <!--外部模塊-->
    <script type="module" src="./module.js"></script>
     <script type="module" src="./module.js"></script>
     <script type="module" src="./module.js"></script>
    <!--內(nèi)聯(lián)模塊-->
    <script type="module">
            import { count } from './module.js';
    </script>
    <script src='./classic.js'></script>
    <script src='./classic.js'></script>

    在上述代碼中 module.js 只會被執(zhí)行一次,classic.js 會被執(zhí)行兩次

    • 下載模塊腳本不會阻塞 HTML 解析

    默認情況,當瀏覽器下載常規(guī)外部腳本時,它會暫停解析 HTML,我們可以在 script 標簽上添加 defer 屬性,使瀏覽器在下載腳本期間不暫停解析 HTML。當下載模塊腳本時,瀏覽器默認模塊腳本是 defer 的。

    下圖展示了瀏覽器獲取外部模塊腳本和常規(guī)腳本的流程

    ECMAScript?modules規(guī)范怎么寫

    • 模塊腳本通過 CORS 獲取

    模塊腳本以及它的依賴項是通過 CORS 獲取的,當獲取一個跨域的模塊腳本時需要特別注意這個問題,跨域腳本的響應頭 Access-Control-Allow-Origin 必須包含當前域,否則模塊會獲取失敗,而獲取常規(guī)腳本則沒有這個限制。

    為了保證獲取同源模塊腳本時,瀏覽器始終帶上 credentials(cookie 等),推薦給 script 標簽加上 crossorigin 屬性。

    動態(tài)導入

    到目前為止介紹的都是靜態(tài)導入模塊,靜態(tài)導入必須等模塊代碼全部下載之后才會執(zhí)行程序,這可能會使網(wǎng)站的首屏渲染性能下降。通過動態(tài)導入模塊可以根據(jù)用戶在界面上的操作按需下載資源,節(jié)省流量,動態(tài)導入在 ECMAScript 2020 正式發(fā)布,它需要用到import(),用法如下所示:

    // 通過相對路徑導入
    import('./exportDefault.js').then((module) => {
        console.log(module) // line A
    })
    // 通過絕對路徑導入
    import('https://cdn.skypack.dev/react').then((react) => {
        console.log(react) // line B
    })

    從上述代碼可以看出 import() 的返回值是一個 promise 對象,當模塊加載成功之后 promise 對象的狀態(tài)會變成 fulfilled,import() 可以與 async/await 配合使用

    上述代碼中的 line A 和 line B 標識的變量 module 和 react 都是 JavaScript 對象,我們可以用對象的點語法和中括號語法訪問模塊導出的任何方法和屬性,模塊的默認導出通過 default 屬性名訪問。

    動態(tài)導入與靜態(tài)導入存在如下 3 個差異:

    • 動態(tài)導入的模塊說明符可以是變量,但靜態(tài)導入的模塊說明符只能是字符串字面量

    • 動態(tài)導入能在模塊和常規(guī)腳本中使用,但是靜態(tài)導入只能在模塊中使用

    • 動態(tài)導入不必位于文件的頂層,但靜態(tài)導入只能位于模塊的頂層

    雖然動態(tài)導入模塊和靜態(tài)導入模塊存在差異,但它們都通過 CORS 獲取模塊腳本,所以在獲取跨域模塊腳本時,腳本的 Access-Control-Allow-Origin 響應頭一定要配置正確。

    動態(tài)導入和靜態(tài)導入有它們各自的使用場景。在初始渲染時要用到的模塊使用靜態(tài)導入,其他情況,特別是那些與用戶操作相關(guān)的功能,可以使用動態(tài)導入按需加載依賴的模塊,這種做法能提高首屏渲染性能,但是會降低用戶操作過程中的性能。所以,哪些模塊使用靜態(tài)導入,哪些模塊使用動態(tài)導入需要你根據(jù)實際情況考慮。

    提示:在動態(tài)導入模塊時要用到 import(),看上去這像是函數(shù)調(diào)用,實際上它并不是函數(shù)調(diào)用,而是一種特殊的語法,你不能使用 import.call()、import.apply()、const myImport = import; myImport()。

    感謝各位的閱讀,以上就是“ECMAScript modules規(guī)范怎么寫”的內(nèi)容了,經(jīng)過本文的學習后,相信大家對ECMAScript modules規(guī)范怎么寫這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!

    向AI問一下細節(jié)

    免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI