讓我們從一個(gè)問題開始。
名為 increment
的 ES2015 模塊包含以下代碼:
1// increment.js
2let counter = 0;
3counter++;
4
5export default counter;
然后在另一個(gè)模塊 consumer
中,將上述模塊 increment
導(dǎo)入兩次:
1// consumer.js
2import counter1 from './increment';
3import counter2 from './increment';
4
5counter1; // => ???
6counter2; // => ???
問題是:當(dāng) consumer
模塊運(yùn)行時(shí),變量 counter1
和 counter2
的內(nèi)容是什么?
要回答這個(gè)問題,首先你需要了解 JavaScript 如何評估和導(dǎo)入模塊。
1. 模塊評估
理解 JavaScript 內(nèi)部原理的一個(gè)好方法是查看其說明。
根據(jù)規(guī)范,每個(gè) JavaScript 模塊都與模塊記錄相關(guān)聯(lián)。模塊記錄具有方法 Evaluate()
,該方法對模塊進(jìn)行評估:
如果該模塊已經(jīng)被成功評估,則返回 undefined
;……否則,便可遞歸地評估此模塊所有的模塊依賴性,然后再評估此模塊。
所以同一模塊僅被評估一次。
不幸的是,問題不止于此。如何確保使用相同路徑兩次調(diào)用 import 語句返回相同的模塊?
2. 解決導(dǎo)入問題
將路徑(也稱為說明符)關(guān)聯(lián)到具體模塊的職責(zé)由 HostResolveImportedModule() 執(zhí)行操作。
1import module from 'path';
根據(jù)規(guī)則:
HostResolveImportedModule
的實(shí)現(xiàn)必須符合以下要求:
正常的返回值必須是 Module Record
的具體子類的實(shí)例。
如果與 referencingScriptOrModule, specifier
對相對應(yīng)的 Module Record
不存在或無法創(chuàng)建,則必須引發(fā)異常。
每次使用特定的 referencingScriptOrModule, specifier
對作為參數(shù)調(diào)用此操作時(shí),如果正常完成,則必須返回相同的 Module Record
實(shí)例。
讓我們以一種易于理解的方式看看都發(fā)生了什么。
HostResolveImportedModule(referencingScriptOrModule, specifier)
是一個(gè)抽象操作,該操作返回對應(yīng)于ReferencingScriptOrModule, specifier
的模塊:
最后,HostResolveImportedModule()
從相同路徑導(dǎo)入模塊時(shí),將導(dǎo)入相同模塊:
1import moduleA from 'path';
2import moduleB from 'path';
3import moduleC from 'path';
4
5// moduleA, moduleB and moduleC are equal
6
7moduleA === moduleB; // => true
8moduleB === moduleC; // => true
有趣的是,規(guī)范指出主機(jī)(指瀏覽器,Node 或任何嘗試運(yùn)行 JavaScript 的東西)必須提供 HostResolveImportedModule()
的具體實(shí)現(xiàn)。
3. 答案
查看規(guī)范之后,你將知道對 JavaScript 模塊進(jìn)行了一次評估。另外,從相同路徑導(dǎo)入模塊時(shí),將返回相同的模塊實(shí)例。
讓我們回到問題。
increment
模塊總是被評估一次:
1// increment.js
2let counter = 0;
3counter++;
4
5export default counter;
不管 increment
模塊被導(dǎo)入多少次,counter++
語句僅執(zhí)行一次。默認(rèn)導(dǎo)出的 counter
變量的值為 1
。
現(xiàn)在看 consumer
模塊:
1// consumer.js
2import counter1 from './increment';
3import counter2 from './increment';
4
5counter1; // => 1
6counter2; // => 1
import counter1 from './increment'
和 import counter2 from './increment'
有相同的路徑:'./increment'
。所以你將會導(dǎo)入相同的模塊實(shí)例。
最后,consumer
內(nèi)部的 counter1
和 counter2
變量都等于 1
。
4. 結(jié)論
僅通過研究提出的簡單問題,就可以找到有關(guān)如何評估和導(dǎo)入 JavaScript 模塊的詳細(xì)信息。
規(guī)則非常簡單:同一模塊僅被評估一次,換句話說,模塊級作用于僅被執(zhí)行一次。如果評估后的模塊再次導(dǎo)入,則會跳過第二次評估,并使用已解決的已導(dǎo)出文件。
如果某個(gè)模塊多次導(dǎo)入但使用相同的說明符(即路徑),則 JavaScript 規(guī)范可確保你將得到相同的模塊實(shí)例。
原文鏈接
https:///javascript-module-import-twice/

?? 看完三件事
如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我三個(gè)小忙:
點(diǎn)個(gè)「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點(diǎn)在看,都是耍流氓 -_-)
關(guān)注我的博客 https://github.com/SHERlocked93/blog,讓我們成為長期關(guān)系
關(guān)注公眾號「前端下午茶」,持續(xù)為你推送精選好文,也可以加我為好友,隨時(shí)聊騷。