欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

ECMAScript?modules規(guī)范示例詳解

 更新時間:2022年11月14日 11:12:59   作者:何遇er  
這篇文章主要為大家介紹了ECMAScript?modules規(guī)范示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

很多編程語言都有模塊這一概念,JavaScript 也不例外,但在 ECMAScript 2015 規(guī)范發(fā)布之前,JavaScript 沒有語言層面的模塊語法。模塊實際上是一種代碼重用機(jī)制,要實現(xiàn)代碼重用,將不同的功能劃分到不同的文件中是必不可少的,如何在其他的文件中使用這些文件定義的功能呢?在 ECMAScript 2015 之前,web 開發(fā)人員不得不尋求 JavaScript 語法之外的解決方法,例如:SystemJS、RequireJS 等模塊加載工具,也有開發(fā)人員使用 webpack、Browserify 等模塊打包工具。ECMAScript 2015 發(fā)布之后,JavaScript 擁有了語言層面的模塊語法,它被稱為 ECMAScript modules,簡稱 ES modules,這使 web 開發(fā)人員很容易就能創(chuàng)建模塊,使用模塊。

在本文中會介紹 ES modules 的基本用法、ES modules 的特點以及在瀏覽器中使用 ES modules。

基本語法

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

模塊導(dǎo)出的4種寫法

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

  • 默認(rèn)導(dǎo)出
export default function myFunc() {}
export default function () {}
export default class MyClass {}
export { foo as default }
export default 'Hello Es modules!'
  • 行內(nèi)命名導(dǎo)出
export function myFunc() {}
export class MyClass {}
export const fooStr = 'Hello Es modules!'
  • 通過一個 export 子句批量命名導(dǎo)出
function myFunc() {}
class MyClass {}
const fooStr = 'Hello Es modules!'
export {myFunc, MyClass , fooStr } // 在這個地方一次性導(dǎo)出多個
  • 重新導(dǎo)出
// 重新導(dǎo)出 other_module 中除默認(rèn)導(dǎo)出之外的內(nèi)容
export * from './other_module.js'
// 重新導(dǎo)出 other_module 中的默認(rèn)導(dǎo)出
export { default } from './other_module.js'
// 重新導(dǎo)出 other_module 中的默認(rèn)導(dǎo)出,并且將 other_module 中的 sayName 命名為 getName 之后再導(dǎo)出
export { default, sayName as getName } from './other_module.js'

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

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

3中模塊說明符

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

  • 相對路徑
import foo from './myModule.js'
import { sayName } from '../other_module.js'

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

  • 絕對路徑
import React from 'https://cdn.skypack.dev/react'

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

  • bare(裸 露) 模式
import React from 'react'
import Foo from 'react/lib.js'

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

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

模塊導(dǎo)入的 6 寫法

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

  • 默認(rèn)導(dǎo)入
// 你可以將 myFunc 改成任何你喜歡的變量名
import myFunc from './myModule.js'
  • 將模塊作為一個對象導(dǎo)入(即命名空間導(dǎo)入)
import * as api from './myModule.js'
// 通過對象的 default 屬性訪問 myModule.js 中的默認(rèn)導(dǎo)出
console.log(api.default)
  • 命名導(dǎo)入
//   導(dǎo)入 myModule.js 中的fooStr 
import { fooStr } from './myModule.js'
// 將myModule.js中默認(rèn)導(dǎo)出命名為myFunc 
import { default as myFunc } './myModule.js'
// 將 myModule.js中的 fooStr 命名為 myStr
import { fooStr as myStr } from './myModule.js'

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

  • 只加載模塊,不導(dǎo)入任何東西
import './myModule.js'

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

  • 將默認(rèn)導(dǎo)入與命名導(dǎo)入混合使用
import myFunc, { fooStr  } from './myModule.js'
  • 將默認(rèn)導(dǎo)入與命名空間導(dǎo)入混合使用
import myFunc, * as api from './myModule.js'

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

ES modules的 4 個特點

導(dǎo)入是導(dǎo)出的只讀引用

例如有個模塊 A,它導(dǎo)出了一個變量 count,模塊 B 導(dǎo)入模塊 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 導(dǎo)出了對象 obj,在模塊 B 中不能直接給 obj 賦值,但是可以增、刪、改 obj 中的屬性。

現(xiàn)在我已經(jīng)介紹了只讀的含義,下面介紹引用的含義。引用意味著在項目中多個模塊用的是同一個變量,例如:模塊 B 和模塊 C 都導(dǎo)入了模塊 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 導(dǎo)入的是引用,而非副本,模塊導(dǎo)出的變量在整個項目中是一個單例。

支持循環(huán)依賴

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

導(dǎo)入會被提升

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

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

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

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

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

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

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

  • 模塊運行在嚴(yán)格模式下
  • 模塊具備詞法頂部作用域

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

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

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

  • 模塊中的 this 關(guān)鍵字沒有指向全局 this,它是 undefined,如果要在模塊中訪問全局 this 要使用 globalThis,在瀏覽器中 globalThis 是 window 對象。
  • export 和靜態(tài)導(dǎo)入 import 只能在模塊中使用
  • 在模塊頂層能使用 await 關(guān)鍵字,在常規(guī) JavaScript 腳本中只能在 async 函數(shù)中使用 await 關(guān)鍵字

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

在這部分介紹的這 5 個差異是與 JavaScript 運行環(huán)境無關(guān)的差異,在之后的部分會介紹在瀏覽器中使用 ES modules,這那里會補(bǔ)充一些新的差異。

在瀏覽器中使用 ES modules

現(xiàn)代瀏覽器支持 ES modules,你可以將 script 標(biāo)簽的 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=’xxx.js’></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 解析

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

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

上圖源于 (html.spec.whatwg.org/multipage/s…)

  • 模塊腳本通過 CORS 獲取

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

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

動態(tài)導(dǎo)入

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

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

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

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

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

  • 動態(tài)導(dǎo)入的模塊說明符可以是變量,但靜態(tài)導(dǎo)入的模塊說明符只能是字符串字面量
  • 動態(tài)導(dǎo)入能在模塊和常規(guī)腳本中使用,但是靜態(tài)導(dǎo)入只能在模塊中使用
  • 動態(tài)導(dǎo)入不必位于文件的頂層,但靜態(tài)導(dǎo)入只能位于模塊的頂層

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

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

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

以上就是ECMAScript modules規(guī)范示例詳解的詳細(xì)內(nèi)容,更多關(guān)于ECMAScript modules規(guī)范的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論