web-dev-qa-db-ja.com

Angular2のSystemJSの必要性は何ですか?

私はAngularとAngular2の完全な初心者です。ワークフローの構造について混乱しています。 Angular2サイトにある サンプルプロジェクト を見てきました。

私が間違っている場合は修正しますが、今まで知っていることは、すべてのTypeScriptがTypeScriptコンパイラーによってjavascriptに変換されることです。コンパイルされたjavascriptは、実際にはブラウザーで実行されるものです。

ここで、次のようなES6インポートステートメントを使用してjavascriptファイルをTypeScriptにインポートする場合:

import { NgModule }      from '@angular/core';

なぜそれらをロードするために再びSy​​stemJSを使用する必要があるのか​​-:

map: {
      // our app is within the app folder
      app: 'app',
      // angular bundles
      '@angular/core': 'npm:@angular/core/bundles/core.umd.js',

つまり、それは逆効果ではないということですか? tsファイルの変換されたjavascriptを見ると、すべてのimportステートメントがrequire()ステートメントに変換されていることが示されています。まず、ES5でrequire()がどのように機能するか jsファイル、2番目の場合は、SystemJSが実行していることです。

これは本当に私を混乱させます。どんな助けも大歓迎です。

50
ng.newbie

tscがTypeScriptをJavaScriptにコンパイルすると、ローカルシステムに大量のjsファイルが作成されます。何らかの形でブラウザにロードする必要があります。ブラウザはまだネイティブES6モジュールの読み込みをサポートしていないため、2つのオプションがあります。それらをすべて、依存関係の正しい順序でindex.htmlファイルに入れるか、ローダーを使用してすべてを行うことができます。すべてのモジュールのルートを指定すると、すべてのファイルがその依存関係の正しい順序でそのローダーによってロードおよび実行されます。多くのローダーがあります:requirejs、webpack、systemjsなど。特定のケースでは、systemjsです。

Tsファイルの変換されたjavascriptを見ると、すべてのimportステートメントがrequire()ステートメントに変換されていることがわかります。

はい、これはSystemJsがバンドルをロードする方法です。 require()exports構文を使用します。これは、バンドルをロードするためのCommonJS構文であり、tsconfig.jsonでこのタイプを指定したためです。

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",

module:'es6'を配置すると、コンパイルされたjavascriptファイルでインポートおよびエクスポートステートメントが保持されることがわかります。ただし、前述のように、ブラウザーはネイティブでサポートしていないため、この構文は使用できません。 module:'AMD'を配置すると、define()を使用する異なる構文が表示されます。 systemjsローダーは、tscでサポートされているすべてのモジュールタイプを実際にロードできるため、angular2スターターチュートリアルで推奨されていると思います。ただし、モジュールをes6モジュールとしてロードする場合は、module: 'system'tsconfig.jsonに配置する必要があります。これはes6 modules標準に準拠するように設計されたモジュールシステムであり、ブラウザでes6 modulesが完全にサポートされるまで使用されます。

セットアップの仕組み

index.htmlに次のスクリプトを追加します。

<script>
    System.import('app').catch(function (err) {
        console.error(err);
    });
</script>

index.htmlがロードされたときに実行されます。 import('app')メソッドは、systemjsに、systemjs.config.jsの構成で指定されたプロジェクトディレクトリ構造のappフォルダーにマップされているappモジュールをロードするよう指示します。

map: {
    // our app is within the app folder
    app: 'app',

SystemJsはそのフォルダーでmain.jsファイルを探します。 app/main.jsが見つかってブラウザにロードされると、そのコード内でrequireの呼び出しが見つかります。

var app_module_1 = require('./app.module');

そしてsystemjsはローカルシステムからapp.module.jsファイルを取得します。これには、次のような独自の依存関係があります。

var core_1 = require('@angular/core');

そして、サイクルが繰り返されます-ロード、依存関係の検索、ロード、実行。そして、これはすべての依存関係がsystemjsによってブラウザーで解決、ロード、実行される方法です。

@ angularライブラリのコアへのマッピングが必要な理由

systemjs.config.tsファイルには、コア@angularモジュールへのマッピングがあります。

map: {
  ...
  // angular bundles
  '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
  '@angular/common': 'npm:@angular/common/bundles/common.umd.js',

ここで最初に理解することは、これらがmappingsであり、依存関係ではないことです。つまり、どのファイルも@angular/coreをインポートしない場合、ブラウザーにロードされません。ただし、この特定のモジュールがapp/app.module.ts内にインポートされていることがわかります。

import { NgModule }      from '@angular/core';

さて、なぜマッピングがあるのか​​。 systemjsapp/app.module.jsをブラウザにロードしたとします。コンテンツを解析し、次を見つけます。

var core_1 = require('@angular/core');

systemjsは、@angular/coreを解決およびロードする必要があることを理解しています。ドキュメントで指定されているように、最初にmappingsをチェックするプロセスを通過します。

マップオプションはパスに似ていますが、正規化プロセスの非常に早い段階で機能します。モジュールのエイリアスを場所またはパッケージにマッピングできます。

名前付きモジュールによる解決と呼びます。そのため、マッピングを検出し、@angular/corenode_modules/@angular/coreに置き換えます。これが実際のファイルが置かれる場所です。

systemjsnode.jsで使用されるアプローチを模倣しようとすると思います。この場合、単にrequire('bar.js')['/', '../', or './']のように、相対パス識別子node.jsなしでモジュールを指定できます。

node.jsは現在のモジュールの親ディレクトリで開始し、/ node_modulesを追加し、その場所からモジュールをロードしようとします。

必要に応じて、次のような相対パスを使用して、名前付きマッピングの使用を避け、インポートすることができます。

import {NgModule} from '../node_modules/@angular/core';

ただし、これは、プロジェクト内の@angular.coreへのすべての参照と、@angularを含むlibファイルで実行する必要があります。