ホームページ  >  記事  >  ウェブフロントエンド  >  es6 のモジュール性について詳しく説明した記事

es6 のモジュール性について詳しく説明した記事

青灯夜游
青灯夜游転載
2022-11-02 20:17:171623ブラウズ

es6 のモジュール性について詳しく説明した記事

CommonJs には多くの優れた機能があります。以下で簡単にレビューしてみましょう:

  • モジュール コードは、次の場合にのみロードされます。実行中;

  • #モジュールは 1 回だけロードできます;

  • #モジュールは他のモジュールのロードを要求できます;
  • #循環依存関係のサポート;
  • モジュールはパブリック インターフェイスを定義でき、他のモジュールはこのパブリック インターフェイスに基づいて監視および対話できます;
  • CommonJs CommonJs

Es Module
のユニークな点は、ブラウザを通じて、またはサードパーティのローダーやビルド ツールを使用してネイティブにロードできることです。負荷。

Es module

モジュールをサポートするブラウザは、トップレベル モジュールから依存関係グラフ全体をロードでき、それは非同期で行われます。ブラウザはエントリ モジュールを解析し、依存関係を特定し、依存モジュールに対するリクエストを送信します。これらのファイルがネットワーク経由で返された後、ブラウザーはその依存関係を解決します。これらの二次的な依存関係が読み込まれていない場合は、さらに多くのリクエストが送信されます。

この非同期再帰読み込みプロセスは、アプリケーション全体の依存関係グラフが解決されるまで続行されます。依存関係グラフが解析された後、参照プログラムはモジュールを正式にロードできます。

Es Module

は、

CommonJsAMD の多くの優れた機能を借用するだけでなく、いくつかの新しい動作も追加します。 Es モジュール

デフォルトでは厳密モードで実行します。
  • ##Es モジュール グローバル名前空間を共有しません;

  • Es Module 最上位の

    this
  • の値は
  • unknown

    です (通常のスクリプトは window です) );var モジュール内の宣言は

    window
  • オブジェクトに追加されません;
  • Es Module は非同期でロードおよび実行されます。

  • エクスポートとインポート

モジュール関数 これは主に、exports


import
    という 2 つのコマンドで構成されます。
  • 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 };
  • デフォルトのエクスポート。モジュールはデフォルトのエクスポートを 1 つだけ持つことができることに注意してください: <pre class="brush:js;toolbar:false;">export default &quot;foo&quot;; export default { name: &amp;#39;moment&amp;#39; } export default function foo(x,y) { return x+y } export { bar, foo as default };</pre>
エクスポートの間違った使用法
#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 の基本的な使用法
  • Usage
import

コマンドでモジュールの外部インターフェイスを定義した後、他の js ファイルは import コマンドを通じてモジュール全体をロードできます。

import {foo,age,nickname} form &#39;模块标识符&#39;

    モジュール識別子現在のモジュールの相対パス、絶対パスまたは純粋な文字列も可能ですが、文字列などの動的計算の結果にすることはできません。
  • import コマンドは、他のモジュールからインポートされる変数名を指定する中括弧を受け入れます。変数名は、インポートされたモジュールの外部インターフェイスの名前と同じである必要があります。モジュール。
  • インポートされた変数は読み取り専用インターフェイスであるため、再割り当てできません。オブジェクトの場合は、オブジェクトのプロパティを再割り当てできます。エクスポートされたモジュールは値を変更でき、インポートされた変数もそれに応じて変更されます。
  • 上の図からわかるように、オブジェクトのプロパティは再割り当てされ、変数は
  • Assignment to constant を報告しています。変数
  • の型が間違っています。
モジュールが名前付きエクスポートとデフォルト エクスポートの両方をエクスポートする場合、それらは

importes6 のモジュール性について詳しく説明した記事 ステートメントで同時に取得できます。特定の識別子をリストして取得することも、

#
// 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";
  • dynamic import
  • のように
  • #
    import("./foo.js").then((module) => {
      const { default: foo, bar, baz } = module;
      console.log(foo); // [Function: foo]
      console.log(bar); // 777
      console.log(baz); // moment
    });
    を使用して取得することもできます。
標準的な使用方法

import インポートされたモジュールは静的であるため、ロード時にインポートされたすべてのモジュールがコンパイルされます (オンデマンドでのコンパイルは達成できないため、モジュールのロード速度が低下します)。ホームページ)。シナリオによっては、条件に基づいて、またはオンデマンドでモジュールをインポートする必要がある場合があります。この場合、静的インポートの代わりに動的インポートを使用できます。 キーワード import

関数を呼び出すのと同じように、モジュールを動的にインポートできます。このように呼び出すと、
    promise
  • が返されます。
    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);
  • トップレベルの await を使用する
    • 在经典脚本中使用 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 { &#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;;
    }

    在浏览器中使用 Es Module


    • 在浏览器上,你可以通过将 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>

    es6 のモジュール性について詳しく説明した記事

    • 在浏览器中,引入相同的 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>

    es6 のモジュール性について詳しく説明した記事

    模块的默认延迟


    • 默认情况下,nomodule 脚本会阻塞 HTML 解析。你可以通过添加 defer 属性来解决此问题,该属性是等到 HTML 解析完成之后才执行。

    es6 のモジュール性について詳しく説明した記事

    • deferasync 是一个可选属性,他们只可以选择其中一个,在 nomodule 脚本下,defer 等到 HTML 解析完才会解析当前脚本,而 async 会和 HTML 并行解析,不会阻塞 HTML 的解析,模块脚本可以指定 async 属性,但对于 defer 无效,因为模块默认就是延迟的。
    • 对于模块脚本,如果存在 async 属性,模块脚本及其所有依赖项将于解析并行获取,并且模块脚本将在它可用时进行立即执行。

    Es Module 和 Commonjs 的区别


    讨论 Es Module 模块之前,必须先了解 Es ModuleCommonjs 完全不同,它们有三个完全不同:

    • 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 是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。

    Es Module 工作原理的相关概念


    • 在学习工作原理之前,我们不妨来认识一下相关的概念。

    Module Record

    模块记录(Module Record) 封装了关于单个模块(当前模块)的导入和导出的结构信息。此信息用于链接连接模块集的导入和导出。一个模块记录包括四个字段,它们只在执行模块时使用。其中这四个字段分别是:

    • Realm: 创建当前模块的作用域;

    • Environment:模块的顶层绑定的环境记录,该字段在模块被链接时设置;

    • Namespace: モジュール名前空間オブジェクトは、モジュール エクスポート バインディングへの実行時のプロパティベースのアクセスを提供するモジュール名前空間外部オブジェクトです。モジュール名前空間オブジェクトにはコンストラクターがありません;

    • HostDefined: ホスト環境で使用するために予約されたフィールド、追加情報を関連付ける必要がありますモジュール 。

    #モジュール環境レコード

      モジュール環境レコードは、外部スコープを表すために使用される宣言型環境レコードです。 ECMAScript モジュールの。通常の変更可能および不変更バインディングに加えて、モジュール環境レコードは、別の環境レコードの間接アクセスに存在するターゲットへのバインディングを提供する不変
    • import バインディングも提供します。
    不変バインディングとは、現在のモジュールが他のモジュールを導入し、導入された変数は変更できないことを意味します。これは、モジュール固有の不変バインディングです。

    Es モジュールの解析プロセス


    始める前に、まずプロセス全体がどのようなものであるかを一般的に理解しましょう。


    • フェーズ 1: 構築 (

      Construction)、アドレスに従って js ファイルを見つけ、ネットワーク経由でダウンロードします。モジュール ファイルは Module Record;

    • フェーズ 2: インスタンス化 (

      Instantiation) で、モジュールをインスタンス化し、メモリ領域を割り当てます。モジュールのインポートおよびエクスポート ステートメントを解析し、モジュールが対応するメモリ アドレスを指すようにします。

    • フェーズ 3: 実行 (

      評価)、コードの実行、計算値、および値がメモリ アドレスに埋め込まれます。

    #建設建設フェーズ

    • loader モジュールのアドレス指定とダウンロードを担当します。まずエントリ ファイルを変更します。これは通常、HTML ではモジュール ファイルを表す タグです。

    es6 のモジュール性について詳しく説明した記事

      モジュールは、
    • import ステートメントを通じて宣言され続けます。import にはモジュール宣言識別子があります。 宣言ステートメント。文字 (ModuleSpecifier)。loader に次のモジュールのアドレスを見つける方法を指示します。

    es6 のモジュール性について詳しく説明した記事

      各モジュール識別番号は、
    • モジュール レコード (モジュール レコード)、および各 モジュール レコード# #に対応します。 #JavaScript コード実行コンテキストImportEntriesLocalExportEntriesIndirectExportEntriesStarExportEntries ##が含まれます#。 ImportEntries 値は ImportEntry Records タイプであり、LocalExportEntriesIndirectExportEntriesStarExportEntries です。 ExportEntry Records タイプ。
    • ImportEntry Records

    A

    ImportEntry Records
      には 3 つのフィールド
    • ModuleRequest ImportName が含まれています, LocalName;ModuleRequest: モジュール識別子 (
    • ModuleSpecifier
    );

    ImportName: by ModuleRequest

    モジュール識別子のモジュールエクスポートに必要なバインディングの名前。値

    namespace-object は、インポート要求がターゲット モジュールの名前空間オブジェクトに対するものであることを示します。LocalName: インポートされたモジュールから現在のモジュールのインポート値にアクセスするために使用される変数。

    詳細については、以下の図を参照してください。

    • 次の表には、es6 のモジュール性について詳しく説明した記事import## を使用してインポートされた
    • ImportEntry Records
    • フィールドの例が記録されています。 #:
    インポート ステートメント (インポート ステートメント フォーム)モジュール識別子 (ModuleRequest)##"react";"react""default から React をインポートします""React"import * as Moment from "react";"react"namespace-obj"モーメント"「反応」から {useEffect} をインポート;"反応""useEffect""useEffect"「react」から {useEffect をエフェクトとしてインポート };"react""useEffect"
    インポート名 (ImportName) LocalName(LocalName)
    ###"効果"############

    ExportEntry Records

    • A ExportEntry Records には、4 つのフィールド ExportNameModuleRequest、# # が含まれています。 #ImportNameLocalName、および ImportEntry Records は、追加の ExportName があるという点で異なります。
    ExportName: このモジュールがエクスポート時にバインドするために使用する名前。

      #次の表には、
    • export

      : を使用してエクスポートされた

      ExportEntry Records フィールドの例が記録されています。 ##エクスポート宣言エクスポート名モジュール識別子インポート名ローカル名##エクスポート変数 v;"v""default""default"default"デフォルト"default"x""x""x""x"null#デフォルト以外のすべて ##export * as ns from "mod";" ns"mod"allnull
    • トピックに戻る

    • 現在の モジュール レコード を解析した後でのみ、現在のモジュールがどのサブモジュールに依存しているかを知ることができます。サブモジュールを 解決し、サブモジュールを取得し、次にサブモジュールを解析し、このプロセスの解決 -> フェッチ -> 解析を継続的に繰り返す必要があります。結果は次の図に示すようになります:

    • es6 のモジュール性について詳しく説明した記事

      • このプロセスは、静的分析とも呼ばれます。JavaScript コードは実行されず、export## のみが識別されます。 # および import キーワードがあるため、動的インポートを除き、import は非グローバル スコープでは使用できません。
      • 複数のファイルが 1 つのファイルに同時に依存している場合はどうなりますか?これにより無限ループが発生しますか?答えはノーです。
      • loader Module Map を使用してグローバル MOdule Record を追跡およびキャッシュし、モジュールが fetch## のみであることを確認します #一旦、各グローバル スコープに独立したモジュール マップが存在します。
      MOdule Map は、URL レコードと文字列で構成されるキー/値マッピング オブジェクトです。 URL レコードは、モジュールを取得するためのリクエスト URL、モジュールのタイプを示す文字列 (例: "javascript") です。モジュール マップの値は、モジュール スクリプト、null (フェッチの失敗を示すために使用)、またはプレースホルダー値「fetching」のいずれかです。

      es6 のモジュール性について詳しく説明した記事

      #リンクリンクフェーズ

      すべての

      モジュールレコード
        解析後、JS エンジンはすべてのモジュールをリンクする必要があります。 JS エンジンは、エントリ ファイルの
      • モジュール レコード を開始点として使用し、モジュールを深さ優先順に再帰的にリンクし、各 モジュールに対して モジュール環境レコード を作成します。 RecordModule Record 内の変数を管理するために使用されます。

      es6 のモジュール性について詳しく説明した記事モジュール環境レコード

      には、
        Module 変数を保存するために使用される
      • Binding があります。上の図に示すように、Record によってエクスポートされます。モジュール環境レコード のモジュール main.js にある count の変数をエクスポートします。 ##Binding には count が含まれます。この時点では、モジュール インスタンス オブジェクトを作成し、対応する属性とメソッドを追加する V8 のコンパイル フェーズに相当します。このときの値は unknown または null なので、メモリ領域を割り当てます。 import キーワードは、サブモジュール count.js
      • main.jscount をインポートするために使用されます。 js の #importmain.jsexport 変数は同じメモリの場所を指しているため、親モジュールと子モジュール間の関係がリンクされています。 。以下の図に示すように: ##親モジュールとしてエクスポートされた
      • export

      と呼ぶことに注意してください。 import es6 のモジュール性について詳しく説明した記事 サブモジュールが導入されます。親モジュールは変数を変更でき、読み取りおよび書き込み権限を持ちますが、サブモジュールには読み取り権限しかありません。

      • 評価評価フェーズ

      モジュールが相互にリンクされた後、対応するモジュール ファイル リンクフェーズで定義された変数の値を決定し、メモリに配置するコード。 Es モジュールが循環参照を解決する方法

      Es モジュールには 5 つの状態がありますそれぞれ

      unlinked
        linking
      • linkedevaluatingevaluated で、ループ モジュール ( Cyclic Module Records) は Status フィールドで表され、モジュールが実行されたかどうかはこのフィールドによって判断されます。各モジュールは 1 回だけ実行されます。これは、Module Map がグローバル キャッシュ Module Record に使用される理由でもあります。モジュールのステータスが evaluated の場合、次の実行は自動的にスキップされます。モジュールのラップは 1 回だけ実行されます。 Es Module は、 Depth first メソッドを使用してモジュール グラフを走査します。各モジュールは 1 回だけ実行されるため、無限ループが回避されます。

      深度优先搜索算法(英语:Depth-First-Search,DFS)是一种用于遍历或搜索树或图的算法。这个算法会尽可能深地搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。

      es6 のモジュール性について詳しく説明した記事

      • 看下面的例子,所有的模块只会运行一次:
      // 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");
      • 通过 node 运行 main.js ,得出以下结果:

      es6 のモジュール性について詳しく説明した記事

      • 好了,这篇文章到这也就结束了。

      原文地址:https://juejin.cn/post/7166046272300777508

      【推荐学习:javascript高级教程

      null null "v" デフォルト関数 f() をエクスポート {}
      null null "f" export デフォルト関数 () {}
      null null "" デフォルト 42 をエクスポート;
      null null "" エクスポート {x};
      null null " x" export {v as x};
      null null "v" 「mod」から {x} をエクスポート;
      "mod" "x " null export {v as x} from "mod";
      "mod" "v" null エクスポート * "mod" から;
      "mod" #null

以上がes6 のモジュール性について詳しく説明した記事の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。