Heim > Artikel > Web-Frontend > Ein Artikel zum gründlichen Verständnis der ES6-Modularisierung
CommonJs
gesprochen. Wenn Sie ihn noch nicht gelesen haben, können Sie die Spalte finden, in der sich dieser Artikel befindet, um mehr darüber zu erfahren. CommonJs
,如果还没有看,可以查找本文章所在的专栏进行学习。CommonJs
有很多优秀的特性,下面我们再简单的回顾一下:模块代码只在加载后运行;
模块只能加载一次;
模块可以请求加载其他模块;
支持循环依赖;
模块可以定义公共接口,其他模块可以基于这个公共接口观察和交互;
Es Module
的独特之处在于,既可以通过浏览器原生加载,也可以与第三方加载器和构建工具一起加载。Es module
模块的浏览器可以从顶级模块加载整个依赖图,且是异步完成。浏览器会解析入口模块,确定依赖,并发送对依赖模块的请求。这些文件通过网络返回后,浏览器就会解析它们的依赖,,如果这些二级依赖还没有加载,则会发送更多请求。Es Module
不仅借用了 CommonJs
和 AMD
的很多优秀特性,还增加了一些新行为:Es Module
默认在严格模式下执行;
Es Module
不共享全局命名空;
Es Module
顶级的 this
的值是 undefined
(常规脚本是window
);
模块中的 var
声明不会添加到 window
对象;
Es Module
是异步加载和执行的;
exports
和 import
。export
命令用于规定模块的对外接口,import
命令用于输入其他模块提供的功能。export const nickname = "moment"; export const address = "广州"; export const age = 18;
const nickname = "moment"; const address = "广州"; const age = 18; export { nickname, address, age };
export function foo(x, y) { return x + y; } export const obj = { nickname: "moment", address: "广州", age: 18, }; // 也可以写成这样的方式 function foo(x, y) { return x + y; } const obj = { nickname: "moment", address: "广州", age: 18, }; export { foo, obj };
export
输出的变量就是本来的名字,但是可以使用as
关键字重命名。const address = "广州"; const age = 18; export { nickname as name, address as where, age as old };
export default "foo"; export default { name: 'moment' } export default function foo(x,y) { return x+y } export { bar, foo as default };
if(true){ export {...}; }
export
必须提供对外的接口:// 1只是一个值,不是一个接口export 1// moment只是一个值为1的变量const moment = 1export moment// function和class的输出,也必须遵守这样的写法function foo(x, y) { return x+y }export foo复制代码
export
命令定义了模块的对外接口以后,其他js文件就可以通过 import
命令加载整个模块import {foo,age,nickname} from '模块标识符'
import
CommonJs
verfügt über viele hervorragende Funktionen. Schauen wir uns diese im Folgenden kurz an: Der Modulcode wird erst nach dem Laden ausgeführt ;
🎜🎜 Module können nur einmal geladen werden; 🎜🎜🎜🎜 Module können das Laden anderer Module anfordern; 🎜🎜🎜🎜 Module können eine öffentliche Schnittstelle definieren und andere Module können darauf basierend beobachten und interagieren öffentliche Schnittstelle ;🎜🎜🎜Es Module
ist insofern einzigartig, als es nativ über den Browser oder mit Drittanbietern geladen werden kann. Partyloader werden zusammen mit Build-Tools geladen. 🎜🎜Browser, die das Modul Es module
unterstützen, können das gesamte Abhängigkeitsdiagramm asynchron vom Modul der obersten Ebene laden. Der Browser analysiert das Eingabemodul, ermittelt die Abhängigkeiten und sendet eine Anfrage für das abhängige Modul. Nachdem diese Dateien über das Netzwerk zurückgegeben wurden, löst der Browser ihre Abhängigkeiten auf. Wenn diese sekundären Abhängigkeiten nicht geladen wurden, werden weitere Anfragen gesendet. 🎜🎜Dieser asynchrone rekursive Ladevorgang wird fortgesetzt, bis das Abhängigkeitsdiagramm der gesamten Anwendung aufgelöst ist. Nachdem das Abhängigkeitsdiagramm analysiert wurde, kann das Referenzprogramm das Modul offiziell laden. 🎜🎜Es Module
übernimmt nicht nur viele hervorragende Funktionen von CommonJs
und AMD
, sondern fügt auch einige neue Verhaltensweisen hinzu: 🎜🎜Es Module
wird standardmäßig im strikten Modus ausgeführt;🎜🎜🎜🎜Es Module
teilt den globalen Namespace nicht;🎜 🎜🎜 🎜Es Module
Der Wert des obersten Levels this
ist undefiniert
(das reguläre Skript ist window
) ; 🎜🎜🎜🎜Module Die var
-Deklaration wird nicht zum window
-Objekt hinzugefügt. 🎜🎜🎜🎜Es Module
wird asynchron geladen ; 🎜🎜🎜exports
und import
. 🎜🎜Der Befehl export
wird verwendet, um die externe Schnittstelle des Moduls anzugeben, und der Befehl import
wird verwendet, um die von anderen Modulen bereitgestellten Funktionen zu importieren. 🎜🎜// foo.js export default function foo(x, y) { return x + y; } export const bar = 777; export const baz = "moment"; // main.js import { default as foo, bar, baz } from "./foo.js"; import foo, { bar, baz } from "./foo.js"; import foo, * as FOO from "./foo.js";🎜🎜Natürlich können Sie es auch in der folgenden Form schreiben:🎜🎜
import("./foo.js").then((module) => { const { default: foo, bar, baz } = module; console.log(foo); // [Function: foo] console.log(bar); // 777 console.log(baz); // moment});复制代码🎜🎜 Exportieren eines Objekts und einer Funktion 🎜🎜
import("./foo.js").then((module) => { const { default: foo, bar, baz } = module; console.log(foo); // [Function: foo] console.log(bar); // 777 console.log(baz); // moment });🎜🎜Normalerweise sind die von
export
ausgegebenen Variablen ihre ursprünglichen Namen, sie können jedoch mit dem Schlüsselwort as
umbenannt werden. 🎜🎜const p = new Promise((resolve, reject) => { resolve(777); });const result = await p;console.log(result); // 777正常输出🎜🎜Standardexport, es ist zu beachten, dass ein Modul nur einen Standardexport haben kann:🎜🎜
// 错误 import { 'b' + 'ar' } from './foo.js'; // 错误 let module = './foo.js'; import { bar } from module; // 错误 if (x === 1) { import { bar } from './foo.js'; } else { import { foo } from './foo.js'; }
<script></script><script></script>🎜🎜
export
Muss eine externe Schnittstelle bereitstellen: 🎜🎜<script> console.log("模块情况下的"); </script> <script></script> <script> console.log("正常 script标签"); </script>
export
zum Definieren der externen Schnittstelle des Moduls verwendet wurde, können andere js-Dateien das gesamte Modul über import
laden command🎜🎜<script></script> <script></script> <script></script> <script></script> <script></script>🎜🎜Modulidentifikation Das Zeichen kann ein relativer Pfad zum aktuellen Modul, ein absoluter Pfad oder eine reine Zeichenfolge sein, es kann jedoch nicht das Ergebnis einer dynamischen Berechnung sein, beispielsweise eine Abhängigkeitsfolge. 🎜🎜Der Befehl
import
akzeptiert eine geschweifte Klammer, die den Variablennamen angibt, der aus anderen Modulen importiert werden soll, und der Variablenname muss mit dem Namen der externen Schnittstelle des importierten Moduls identisch sein. 🎜🎜Die importierte Variable kann nicht neu zugewiesen werden, da es sich um eine schreibgeschützte Schnittstelle handelt. Wenn es sich um ein Objekt handelt, können die Eigenschaften des Objekts neu zugewiesen werden. Exportierte Module können Werte ändern, und importierte Variablen ändern sich ebenfalls entsprechend. 🎜🎜🎜🎜🎜Assignment to constant variable
的类型错误。import
语句中同时取得它们。可以依次列出特定的标识符来取得,也可以使用 *
来取得:// foo.js export default function foo(x, y) { return x + y; } export const bar = 777; export const baz = "moment"; // main.js import { default as foo, bar, baz } from "./foo.js"; import foo, { bar, baz } from "./foo.js"; import foo, * as FOO from "./foo.js";
import
导入的模块是静态的,会使所有被导入的模块,在加载时就被编译(无法做到按需编译,降低首页加载速度)。有些场景中,你可能希望根据条件导入模块或者按需导入模块,这时你可以使用动态导入代替静态导入。import
可以像调用函数一样来动态的导入模块。以这种方式调用,将返回一个 promise
。import("./foo.js").then((module) => { const { default: foo, bar, baz } = module; console.log(foo); // [Function: foo] console.log(bar); // 777 console.log(baz); // moment});复制代码
await
必须在带有 async
的异步函数中使用,否则会报错:import("./foo.js").then((module) => { const { default: foo, bar, baz } = module; console.log(foo); // [Function: foo] console.log(bar); // 777 console.log(baz); // moment });
Top-level await
:const p = new Promise((resolve, reject) => { resolve(777); });const result = await p;console.log(result); // 777正常输出
import
是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。// 错误 import { 'b' + 'ar' } from './foo.js'; // 错误 let module = './foo.js'; import { bar } from module; // 错误 if (x === 1) { import { bar } from './foo.js'; } else { import { foo } from './foo.js'; }
type
属性设置为 module
用来告知浏览器将 script
标签视为模块。<script></script><script></script>
defer
的方式延迟你的 nomodule
脚本:<script> console.log("模块情况下的"); </script> <script></script> <script> console.log("正常 script标签"); </script>
nomodule
脚本会被执行多次,而模块只会被执行一次:<script></script> <script></script> <script></script> <script></script> <script></script>
nomodule
脚本会阻塞 HTML
解析。你可以通过添加 defer
属性来解决此问题,该属性是等到 HTML
解析完成之后才执行。defer
und async
sind optionale Attribute, sie können nur eines davon auswählen, unter dem nomodule
-Skript, defer-Code > analysiert das aktuelle Skript erst, wenn <code>HTML
analysiert wurde, und async
analysiert parallel zu HTML
, ohne HTML
zu blockieren > Für die Codeanalyse kann das Modulskript das Attribut async
angeben, es ist jedoch für defer
ungültig, da das Modul standardmäßig verzögert ist. defer
和 async
是一个可选属性,他们只可以选择其中一个,在 nomodule
脚本下,defer
等到 HTML
解析完才会解析当前脚本,而 async
会和 HTML
并行解析,不会阻塞 HTML
的解析,模块脚本可以指定 async
属性,但对于 defer
无效,因为模块默认就是延迟的。
async
属性,模块脚本及其所有依赖项将于解析并行获取,并且模块脚本将在它可用时进行立即执行。Es Module
模块之前,必须先了解 Es Module
与 Commonjs
完全不同,它们有三个完全不同:CommonJS
模块输出的是一个值的拷贝,Es Module
输出的是值的引用;CommonJS
模块是运行时加载,Es Module
是编译时输出接口。CommonJS
模块的 require()
是同步加载模块,ES6 模块的import
命令是异步加载,有一个独立的模块依赖的解析阶段。CommonJS
加载的是一个对象(即module.exports
属性),该对象只有在脚本运行完才会生成。而 Es Module
不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。Commonjs
输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。具体可以看上一篇写的文章。Es Module
的运行机制与 CommonJS
不一样。JS引擎
对脚本静态分析的时候,遇到模块加载命令import
,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,import
就是一个连接管道,原始值变了,import
加载的值也会跟着变。因此,Es Module
是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。Module Record
) 封装了关于单个模块(当前模块)的导入和导出的结构信息。此信息用于链接连接模块集的导入和导出。一个模块记录包括四个字段,它们只在执行模块时使用。其中这四个字段分别是:Realm
: 创建当前模块的作用域;Environment
:模块的顶层绑定的环境记录,该字段在模块被链接时设置;Namespace
:模块命名空间对象是模块命名空间外来对象,它提供对模块导出绑定的基于运行时属性的访问。模块命名空间对象没有构造函数;HostDefined
:字段保留,以按 host environments
使用,需要将附加信息与模块关联。import
async
vorhanden ist, werden das Modulskript und alle seine Abhängigkeiten parallel analysiert und abgerufen, und das Modulskript wird sofort ausgeführt, wenn es verfügbar ist. Der Unterschied zwischen Es-Modul und Commonjs
Bevor Sie dasEs-Modul
-Modul besprechen, müssen Sie zunächst dieEs verstehen Module
Völlig anders alsCommonjs
, sie haben drei völlig unterschiedliche Unterschiede: 🎜🎜🎜
🎜🎜Der zweite Unterschied besteht darin, dassCommonJS
-Modul gibt eine Kopie des Werts aus,Es Modul
Die Ausgabe ist eine Referenz auf den Wert; 🎜🎜CommonJS
Modul wird zur Laufzeit geladen undEs Module
ist eine Ausgabeschnittstelle zur Kompilierungszeit. 🎜🎜require()
desCommonJS
-Moduls lädt das Modul synchron, während derimport
-Befehl des ES6-Moduls asynchron lädt und über eine unabhängige Analysephase der Modulabhängigkeiten verfügt. 🎜CommonJS
ein Objekt lädt (d. h. die Eigenschaftmodule.exports
), das erst geladen wird, nachdem das Skript ausgeführt wurde . UndEs Module
ist kein Objekt. Seine externe Schnittstelle ist lediglich eine statische Definition, die während der statischen Analysephase des Codes generiert wird. 🎜🎜Commonjs
gibt eine Kopie des Werts aus, was bedeutet, dass Änderungen innerhalb des Moduls nach der Ausgabe eines Werts keinen Einfluss auf den Wert haben. Einzelheiten finden Sie im Artikel im vorherigen Artikel. 🎜🎜Der Betriebsmechanismus vonEs Module
unterscheidet sich von dem vonCommonJS
. Wenn dieJS-Engine
ein Skript statisch analysiert, generiert sie eine schreibgeschützte Referenz, wenn sie auf den Modulladebefehlimport
trifft. Wenn das Skript tatsächlich ausgeführt wird, wird der Wert basierend auf dieser schreibgeschützten Referenz aus dem geladenen Modul abgerufen. Mit anderen Worten:import
ist eine Verbindungspipeline. Wenn sich der ursprüngliche Wert ändert, ändert sich auch der vonimport
geladene Wert entsprechend. Daher istEs Module
eine dynamische Referenz und speichert keine Werte zwischen. Die Variablen im Modul sind an das Modul gebunden, in dem sie sich befinden. 🎜🎜Verwandte Konzepte des Arbeitsprinzips des Es-Moduls
🎜🎜Bevor wir das Arbeitsprinzip erlernen, können wir uns auch mit den verwandten Konzepten vertraut machen. 🎜🎜Module Record
🎜🎜Module Record (Module Record
) kapselt die Import- und Exportstruktur einzelner Modulinformationen (aktuelles Modul). . Diese Informationen werden verwendet, um den Import und Export des Verbindungsmodulsatzes zu verknüpfen. Ein Moduldatensatz besteht aus vier Feldern, die nur bei der Ausführung des Moduls verwendet werden. Die vier Felder sind: 🎜🎜🎜
Realm
: Erstellt den Umfang des aktuellen Moduls. 🎜🎜Environment
: Der Bindungsumgebungsdatensatz der obersten Ebene des Moduls. Dieses Feld wird festgelegt, wenn das Modul verknüpft ist. 🎜🎜Namespace
: Das Modul-Namespace-Objekt ist ein Modul-Namespace-Fremdobjekt, das laufzeiteigenschaftenbasierten Zugriff auf die Exportbindungen des Moduls bietet. Modul-Namespace-Objekte haben keine Konstruktoren; 🎜🎜HostDefined
: Felder sind für die Verwendung durchHost-Umgebungen
reserviert, zusätzliche Informationen müssen dem Modul zugeordnet werden. 🎜Module Environment Record
🎜🎜Module Environment Record ist ein deklarativer Umgebungsdatensatz, der zur Darstellung des externen Bereichs eines ECMAScript-Moduls verwendet wird. Zusätzlich zu den gewöhnlichen veränderlichen und unveränderlichen Bindungen stellen Modulumgebungsdatensätze auch unveränderlicheimport
-Bindungen bereit, die eine Bindung an ein Ziel ermöglichen, das in einem anderen Umgebungsdatensatz für indirekten Zugriff vorhanden ist. 🎜🎜🎜🎜Unveränderliche Bindung bedeutet, dass das aktuelle Modul andere Module einführt und die eingeführten Variablen nicht geändert werden können. Dies ist die einzigartige unveränderliche Bindung des Moduls. 🎜
Konstruktioncode >), suchen Sie anhand der Adresse nach der Datei <code>js
, laden Sie sie über das Internet herunter und analysieren Sie die Moduldatei in Module Record
Construction
),根据地址查找 js
文件,通过网络下载,并且解析模块文件为 Module Record
;
Instantiation
),对模块进行实例化,并且分配内存空间,解析模块的导入和导出语句,把模块指向对应的内存地址;Evaluation
),运行代码,计算值,并且将值填充到内存地址中;loader
负责对模块进行寻址及下载。首先我们修改一个入口文件,这在 HTML
中通常是一个 <script type="module"></script>
的标签来表示一个模块文件。import
语句声明,在 import
声明语句中有一个 模块声明标识符(ModuleSpecifier
),这告诉 loader
怎么查找下一个模块的地址。模块记录(Module Record)
,而每一个 模块记录
包含了 JavaScript代码
、执行上下文
、ImportEntries
、LocalExportEntries
、IndirectExportEntries
、StarExportEntries
。其中 ImportEntries
值是一个 ImportEntry Records
类型,而 LocalExportEntries
、IndirectExportEntries
、StarExportEntries
是一个 ExportEntry Records
类型。ImportEntry Records
包含三个字段 ModuleRequest
、ImportName
、LocalName
;ModuleSpecifier
);ModuleRequest
模块标识符的模块导出所需绑定的名称。值 namespace-object
表示导入请求是针对目标模块的命名空间对象的;import
导入的 ImportEntry Records
Instantiierung
), instanziieren Sie das Modul, weisen Sie Speicherplatz zu, analysieren Sie die Import- und Exportanweisungen des Moduls und verweisen Sie das Modul auf die entsprechende Speicheradresse
|
Das Modul wird weiterhin über die import -Anweisung deklariert. Es gibt eine Moduldeklarationskennung (ModuleSpecifier ) im import Diese Anweisung teilt dem loader mit, wie er die Adresse des nächsten Moduls finden soll. |
|
Jede Modulidentifikationsnummer entspricht einem |
---|---|---|---|
Ein ImportEntry Records enthält drei Felder ModuleRequest , ImportName, <code>LocalName ;
|
ModuleRequest: ein Modulbezeichner (ModuleSpecifier ); |
ImportName: ein Modulbezeichner von ModuleRequest Das Modul exportiert den Namen der erforderlichen Bindung. Der Wert namespace-object gibt an, dass die Importanforderung für das Namespace-Objekt des Zielmoduls gilt |
|
Weitere Informationen finden Sie im Bild unten: | Die folgende Tabelle zeichnet Instanzen des Felds ImportEntry Records auf, die mit dem Modul import importiert wurden: |
||
Modul Bezeichner (ModuleRequest) | ImportName(ImportName) | LocalName(LocalName) | |
import React from "react"; | "react" | "default" | "React" |
ExportEntry Records
enthält vier Felder ExportName
, ModuleRequest
, ImportName
, LocalName
, der sich von ImportEntry Records
unterscheidet, verfügt über einen zusätzlichen ExportName
. ExportEntry Records
包含四个字段 ExportName
、ModuleRequest
、ImportName
、LocalName
,和 ImportEntry Records
不同的是多了一个 ExportName
。下面这张表记录了使用 export
导出的 ExportEntry Records
ExportName: Dieses Modul wird verwendet, um den Namen beim Exportieren zu binden.
Export Statement | Exportname | Modulkennung | Importname | |
---|---|---|---|---|
Exportvariable v; | "v" | null | null | |
. | Standardfunktion f() exportieren {} | "default" | null | null |
Standardfunktion exportieren () {} | "default" | null | null" | default|
export default 42; | "default" | null | null" | default|
export {x}; | "x" | null | null | |
export {v as x}; | "x" | null | null | |
export {x} from "mod"; | "x" | "mod " | „x“ | |
export {v als ?? |
Zurück zum Thema Erst nach dem Parsen des aktuellen
linking 链接阶段
statische Analyse , führt keinen JavaScript-Code aus und erkennt nur die Schlüsselwörter export und import , also import Kann nicht dynamisch im nicht-globalen Bereich verwendet werden, außer beim Import. Was passiert, wenn mehrere Dateien gleichzeitig von einer Datei abhängen? Wird dies eine Endlosschleife verursachen? loader verwendet Module Map , um den globalen MOdule Record zu verfolgen und zwischenzuspeichern, um sicherzustellen, dass Module nur fetch > sind Einmal gibt es in jedem globalen Bereich eine unabhängige Modulzuordnung. 🎜🎜🎜MOdule Map ist ein Schlüssel/Wert-Zuordnungsobjekt, das aus einem URL-Datensatz und einer Zeichenfolge besteht. Der URL-Datensatz ist die Anforderungs-URL zum Abrufen des Moduls, eine Zeichenfolge, die den Modultyp angibt (z. B. „Javascript“). Der Wert der Modulzuordnung ist entweder das Modulskript, null (wird verwendet, um einen fehlgeschlagenen Abruf anzuzeigen) oder der Platzhalterwert „fetching“. 🎜🎜🎜 Linking-Linking-Phase
Module Record analysiert wurden, muss die JS-Engine alle Module verarbeiten Link. Die JS-Engine verwendet den Module Record der Eintragsdatei als Ausgangspunkt, verknüpft die Module rekursiv in der Reihenfolge der Tiefe zuerst und erstellt einen Module Environment Record für jedes <code>Modul Datensatz , wird zum Verwalten von Variablen im Moduldatensatz verwendet. 🎜🎜🎜🎜🎜Module Environment Record verfügt über eine Binding , die zum Speichern von Variablen verwendet wird, die von Module Record exportiert werden, wie in der Abbildung oben gezeigt Modul main.js exportiert eine Variable von count , die in Binding in Module Environment Record A count entspricht zu diesem Zeitpunkt der Kompilierungsphase von V8 , bei der ein Modulinstanzobjekt erstellt und entsprechende Attribute und Methoden hinzugefügt werden. Zu diesem Zeitpunkt lautet der Wert undefiniert oder null , weisen Sie Speicherplatz dafür zu. 🎜🎜Das Schlüsselwort import wird im Untermodul count.js verwendet, um main.js und count.js zu importieren Die Speicherorte, auf die die Variablen von <code>import von und export von main.js verweisen, sind identisch und werden somit getrennt Eltern- und Kindermodule sind verknüpft. Wie im Bild unten gezeigt:🎜🎜🎜🎜🎜Evaluation 求值阶段Es module 是如何解决循环引用的
// main.js import { bar } from "./bar.js"; export const main = "main"; console.log("main"); // foo.js import { main } from "./main.js"; export const foo = "foo"; console.log("foo"); // bar.js import { foo } from "./foo.js"; export const bar = "bar"; console.log("bar");
|
Das obige ist der detaillierte Inhalt vonEin Artikel zum gründlichen Verständnis der ES6-Modularisierung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!