Maison >interface Web >js tutoriel >Un article expliquant la modularité dans es6 en détail
CommonJs
possède de nombreuses excellentes fonctionnalités. Passons-les brièvement en revue ci-dessous : 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的基本使用
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 };
export 的错误使用
if(true){ export {...}; }
export
必须提供对外的接口:// 1只是一个值,不是一个接口 export 1 // moment只是一个值为1的变量 const moment = 1 export moment // function和class的输出,也必须遵守这样的写法 function foo(x, y) { return x+y } export foo
import的基本使用
export
命令定义了模块的对外接口以后,其他js文件就可以通过 import
命令加载整个模块import {foo,age,nickname} form '模块标识符'
import
命令后面接受一个花括弧,里面指定要从其他模块导入的变量名,而且变量名必须与被导入模块的对外接口的名称相同。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
导入的模块是静态的,会使所有被导入的模块,在加载时就被编译(无法做到按需编译,降低首页加载速度)。有些场景中,你可能希望根据条件导入模块或者按需导入模块,这时你可以使用动态导入代替静态导入。import
可以像调用函数一样来动态的导入模块。以这种方式调用,将返回一个 promise
Es Module
est unique en ce sens que les deux peuvent être chargé de manière native via le navigateur ou avec des chargeurs et des outils de construction tiers. 🎜🎜Les navigateurs qui prennent en charge le module Es module
peuvent charger l'intégralité du graphique de dépendances à partir du module de niveau supérieur de manière asynchrone. Le navigateur analysera le module d'entrée, déterminera les dépendances et enverra une demande pour le module dépendant. Une fois ces fichiers renvoyés sur le réseau, le navigateur résoudra leurs dépendances, et si ces dépendances secondaires n'ont pas été chargées, d'autres requêtes seront envoyées. 🎜🎜Ce processus de chargement récursif asynchrone se poursuivra jusqu'à ce que l'ensemble du graphe de dépendances de l'application soit résolu. Une fois le graphe de dépendance analysé, le programme de référence peut officiellement charger le module. 🎜🎜Es Module
emprunte non seulement de nombreuses excellentes fonctionnalités de CommonJs
et AMD
, mais ajoute également de nouveaux comportements : 🎜Es Module
est exécuté en mode strict par défaut ; 🎜🎜Es Module
ne partage pas le global espace de noms ; 🎜🎜Es Module
La valeur de this
de niveau supérieur est undefined
(le script normal est window code>); 🎜🎜<li>🎜La déclaration <code>var
dans le module ne sera pas ajoutée à l'objet window
🎜🎜Es Module ;
est chargé et exécuté de manière asynchrone ;🎜🎜🎜exports et <code>importer
. 🎜🎜export
est utilisée pour spécifier l'interface externe du module, et la commande import
est utilisée pour importer les fonctions fournies par d'autres modules. 🎜🎜🎜🎜🎜Utilisation de base de l'export🎜🎜🎜import("./foo.js").then((module) => { const { default: foo, bar, baz } = module; console.log(foo); // [Function: foo] console.log(bar); // 777 console.log(baz); // moment });
const p = new Promise((resolve, reject) => { resolve(111); }); // SyntaxError: await is only valid in async functions and the top level bodies of modules const result = await p; console.log(result);
const p = new Promise((resolve, reject) => { resolve(777); }); const result = await p; console.log(result); // 777正常输出
export
est son nom d'origine, mais vous pouvez utiliser le as mot-clé Renommer. 🎜🎜<pre class="brush:js;toolbar:false;">// 错误
import { &#39;b&#39; + &#39;ar&#39; } from &#39;./foo.js&#39;;
// 错误
let module = &#39;./foo.js&#39;;
import { bar } from module;
// 错误
if (x === 1) {
import { bar } from &#39;./foo.js&#39;;
} else {
import { foo } from &#39;./foo.js&#39;;
}</pre><ul><li>Export par défaut, il est à noter qu'un module ne peut avoir qu'un seul export par défaut : 🎜🎜<pre class="brush:js;toolbar:false;"><script type="module" src="./main.mjs"></script>
<script type="module"></script></pre>🎜🎜🎜export utilisation incorrecte🎜🎜🎜<ul><li>La déclaration d'export doit être en le module Niveau supérieur, ne peut pas être imbriqué dans un bloc : 🎜🎜<pre class="brush:js;toolbar:false;"> <script type="module">
console.log("模块情况下的");
</script>
<script src="./main.js" type="module" defer></script>
<script>
console.log("正常 script标签");
</script></pre><ul><li>
<code>export
Doit fournir une interface externe : 🎜🎜<script src="./foo.js"></script> <script src="./foo.js"></script> <script type="module" src="./main.js"></script> <script type="module" src="./main.js"></script> <script type="module" src="./main.js"></script>🎜🎜🎜Utilisation basique de l'import🎜🎜🎜
export
pour définir l'interface externe du module, d'autres fichiers js peuvent charger l'intégralité du module via la commande import
🎜🎜// 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");
import
La commande accepte une accolade, qui spécifie le nom de la variable à importer depuis d'autres modules, et le nom de la variable doit être le même que le nom de l'interface externe du module importé . 🎜Affectation à une variable constante
. 🎜import
. Vous pouvez lister des identifiants spécifiques afin de les obtenir, ou vous pouvez utiliser *
pour les obtenir : 🎜🎜rrreee🎜🎜🎜importation dynamique🎜🎜🎜 import Les modules importés sont statiques, ce qui entraînera la compilation de tous les modules importés lors de leur chargement (la compilation à la demande ne peut pas être réalisée, ce qui réduit la vitesse de chargement de la page d'accueil). Dans certains scénarios, vous souhaiterez peut-être importer des modules en fonction de conditions ou à la demande. Dans ce cas, vous pouvez utiliser l'importation dynamique au lieu de l'importation statique. 🎜<li>Le mot-clé <code>import
peut importer dynamiquement des modules comme appeler une fonction. Appelée de cette façon, une promesse
sera renvoyée. 🎜🎜rrreee🎜🎜🎜Utilisez l'attente de niveau supérieur🎜🎜🎜await
必须在带有 async
的异步函数中使用,否则会报错:const p = new Promise((resolve, reject) => { resolve(111); }); // SyntaxError: await is only valid in async functions and the top level bodies of modules const result = await p; console.log(result);
Top-level await
:const p = new Promise((resolve, reject) => { resolve(777); }); const result = await p; console.log(result); // 777正常输出
import 的错误使用
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 type="module" src="./main.mjs"></script> <script type="module"></script>
defer
的方式延迟你的 nomodule
脚本:<script type="module"> console.log("模块情况下的"); </script> <script src="./main.js" type="module" defer></script> <script> console.log("正常 script标签"); </script>
nomodule
脚本会被执行多次,而模块只会被执行一次:<script src="./foo.js"></script> <script src="./foo.js"></script> <script type="module" src="./main.js"></script> <script type="module" src="./main.js"></script> <script type="module" src="./main.js"></script>
默认情况下,nomodule
脚本会阻塞 HTML
解析。你可以通过添加 defer
属性来解决此问题,该属性是等到 HTML
解析完成之后才执行。
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
模块记录(Module Record
) 封装了关于单个模块(当前模块)的导入和导出的结构信息。此信息用于链接连接模块集的导入和导出。一个模块记录包括四个字段,它们只在执行模块时使用。其中这四个字段分别是:
Realm
: 创建当前模块的作用域;
Environment
:模块的顶层绑定的环境记录,该字段在模块被链接时设置;
Namespace
: l'objet espace de noms de module est un objet étranger d'espace de noms de module qui fournit un accès basé sur les propriétés d'exécution aux liaisons d'exportation de module. Les objets d'espace de noms de module n'ont pas de constructeur ;
Namespace
:模块命名空间对象是模块命名空间外来对象,它提供对模块导出绑定的基于运行时属性的访问。模块命名空间对象没有构造函数;HostDefined
:字段保留,以按 host environments
使用,需要将附加信息与模块关联。
Module Environment Record
import
绑定,这些绑定提供了对存在于另一个环境记录中的目标绑定的间接访问。不可变绑定就是当前的模块引入其他的模块,引入的变量不能修改,这就是模块独特的不可变绑定。
在开始之前,我们先大概了解一下整个流程大概是怎么样的,先有一个大概的了解:
阶段一:构建(Construction
),根据地址查找 js
文件,通过网络下载,并且解析模块文件为 Module Record
;
阶段二:实例化(Instantiation
),对模块进行实例化,并且分配内存空间,解析模块的导入和导出语句,把模块指向对应的内存地址;
阶段三:运行(Evaluation
),运行代码,计算值,并且将值填充到内存地址中;
Construction 构建阶段
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
;ModuleRequest: 一个模块标识符(ModuleSpecifier
);
ImportName: 由 ModuleRequest
模块标识符的模块导出所需绑定的名称。值 namespace-object
表示导入请求是针对目标模块的命名空间对象的;
LocalName: 用于从导入模块中从当前模块中访问导入值的变量;
import
导入的 ImportEntry Records
HostDefined
: champ réservé à l'utilisation par les environnements hôtes
, des informations supplémentaires sont requises pour s'associer au module. L'enregistrement d'environnement de module est un enregistrement d'environnement déclaratif utilisé pour représenter la portée externe d'un module ECMAScript. En plus des liaisons mutables et immuables ordinaires, les enregistrements d'environnement de module fournissent également des liaisons import immuables, qui fournissent une liaison à une cible qui existe dans un autre enregistrement d'environnement avec accès indirect. |
La liaison immuable signifie que le module actuel introduit d'autres modules et que les variables introduites ne peuvent pas être modifiées. Il s'agit de la liaison immuable unique du module. | Avant de commencer, ayons une idée générale de ce à quoi ressemble l'ensemble du processus. Avoir une compréhension générale : |
Phase 1 : Construction (Construction ), recherche selon l'adresse js , téléchargez-le via Internet et analysez le fichier de module dans Module Record ; |
Phase de construction de la construction |
loader est responsable de l'adressage et du téléchargement des modules. Tout d'abord, nous modifions un fichier d'entrée, qui en HTML est généralement une balise <script type="module"></script> pour représenter un fichier de module. |
p> |
---|---|---|---|
p> | Chaque numéro d'identification de module correspond à un Enregistrement de module , et chaque Enregistrement de module contient un Code JavaScript , un Contexte d'exécution , ImportEntries , LocalExportEntries , IndirectExportEntries , StarExportEntries . La valeur ImportEntries est de type ImportEntry Records et LocalExportEntries , IndirectExportEntries , StarExportEntries Est un type ExportEntry Records . |
ImportEntry Records | Un |
LocalName : variable utilisée pour accéder à la valeur importée du module courant depuis le module importé ; | Veuillez vous référer à l'image ci-dessous pour plus de détails :Le tableau suivant enregistre les instances du champ | ||
Identifiant du module (ModuleRequest) | Nom de l'importation (ImportName) | Nom local (LocalName) |
ExportEntry Records
contient quatre champs ExportName
, ModuleRequest
, ImportName
, LocalNameImportEntry Records
, possède un ExportName
supplémentaire. ExportName : Le nom que ce module utilise pour se lier lors de l'exportation. ExportEntry Records
包含四个字段 ExportName
、ModuleRequest
、ImportName
、LocalName
,和 ImportEntry Records
不同的是多了一个 ExportName
。
ExportName: 此模块用于导出时绑定的名称。
下面这张表记录了使用 export
导出的 ExportEntry Records
Déclaration d'exportation | Nom de l'exportation | Identifiant du module | Nom de l'importation par défaut"null | |
---|---|---|---|---|
export default function () {} | "default" | null | null | |
default | "export par défaut 42 ; | "par défaut" | null | |
" | default | "export {x}; | "x" | null|
"x" | exporter {v sous x }; | "x" | null | null|
exporter {x} depuis "mod"; | "x" | "mod" | "x" | |
exporter {v as x} depuis "mod"; | "x" | "mod" | "v" | |
export * depuis "mod"; | null | "mod " | tout sauf-par défaut | |
export * as ns from "mod"; | "ns | "mod" | all | |
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!