web-dev-qa-db-ja.com

TypeScriptとSystemJSを理解するAngular

私は最近、TypeScriptを使用して単純な「HelloWorlds」を調査および実行しています。頭を抱えることができないと思うことがあります。それが、TypeScriptでSystem.jsを使用する方法です。インターネット上にあるすべてのチュートリアルまたはデモはAngular2に関するものであり、私はまだAngular 2に関与したくありません。

例として、次のプロジェクト構造があります。

RootFolder
| 
| _lib
| ...... ts (where .ts files are)
|
| components (where compiled .js files are)
| libraries
| ......... systemjs (where system.js is)
|
| index.html
| tsconfig.json

私のtsconfig.jsonファイルは次のようになっています:

{
  "compileOnSave": true,
  "compilerOptions": {
    "noImplicitAny": true,
    "noEmitOnError": true,
    "removeComments": false,
    "sourceMap": true,
    "target": "es5",
    "module": "system",
    "moduleResolution": "node",
    "outDir": "./components"
  },
  "exclude": [
    "node_modules",
    "wwwroot"
  ],
  "include": [
    "./_lib/ts/**/*"
  ]
}

TypeScriptコンパイルは期待どおりに機能し、問題はありません。 「Alerter」という名前の単純なクラスを作成しました。これには次のコードが含まれています。

//alerter.ts
class Alerter {
    showMessage(): void {
        alert("Message displayed.");
    }
}

export default Alerter

そして、次のコードを持つapp.ts(私の「メイン」アプリケーションファイル):

//app.ts
import Alerter from "./alerter";

console.log("app.js included & executed");

function test() {
    console.log("test called");
    const alt = new Alerter();
    alt.showMessage();
};

そして、私のindex.htmlでは、このapp.jsをSystem.jsとともにインポートし、コンソールから「test」関数を呼び出したいだけです。しかし、それは機能しません。何をしても、機能にアクセスできません。最初のconsole.log行が実行されているのがわかりますが、chrome consoleからtest()を呼び出そうとすると、定義されていません。

Main.tsから「alerter」クラスの依存関係を削除すると、すべてが機能します。コンパイルされたapp.jsには、console.log呼び出しと関数定義のみが含まれているためです。

これがindex.htmlでの私のSystem.js呼び出しです

System.config({
    packages: {
        "components": {
            defaultExtension: "js"
        }
    }
});

System.import("components/app");

私は今本当に必死で、Jqueryの時代に戻るべきだと思います。これはとても単純ですが、それを機能させることはできません。

12
Alaminut

ここで何が起こっているのかわかります。これは、TypeScript exportキーワードとSystemJSの適切な使用法の両方に関連しています。

説明から、基本的には、_<script>_タグだけを使用するのと同じように、SystemJSを使用してJavaScriptファイルをインポートし、そのグローバル定義関数を使用します。

ただし、これは、TypeScriptがファイルをコンパイルする方法を知っておく必要があることを意味します。 https://www.typescriptlang.org/docs/handbook/modules.html のドキュメントには次のように書かれています。

TypeScriptでは、ECMAScript 2015と同様に、トップレベルのインポートまたはエクスポートを含むファイルはすべてモジュールと見なされます。

これがあなたがしていることです。 _app.ts_ファイルには1つのimportがあり、_alerter.ts_ファイルには1つのexportステートメントがあるため、これらは両方ともモジュールとしてコンパイルされます。次に、_tsconfig.json_から、system形式を使用していることがわかります(ただし、ここでは問題ありません)。

モジュールの主な利点の1つは、スコープ外のグローバルオブジェクトをリークしないことです。したがって、System.import("components/app")を呼び出すと、test()関数はそのモジュール内にのみ存在します。ただし、関数をエクスポートして、モジュールのロード後に呼び出すことができます。

これは、最初に関数をエクスポートする必要があることを意味します。

_// app.ts
export function test() {
  ...
};
_

次に、System.import()は、モジュールexportsオブジェクトで解決されるPromiseを返すため、そこでtest()メソッドを呼び出すことができます。

_System.import("components/app").then(function(m) {
  m.test();
});
_

これは本当に期待どおりに機能します。

ただし、test()関数をグローバルに定義したかったようです。このような場合、windowグローバルオブジェクトで関数を自分で定義する必要があります。

_// app.ts
function test() {
  ...
}
declare const window;
window.test = test;
_

これで、パッケージをインポートした後、いつでも使用できます。

_System.import("components/app").then(function(m) {
  test();
});
_

SystemJSにはグローバルオブジェクトを操作する複数の方法がありますが、インポートしたパッケージに解決する必要のある依存関係がある場合、それらを使用する簡単な方法はないと思います(それ以外の場合はこれを見てくださいが、ユースケースではありません- https://github.com/systemjs/systemjs/blob/master/docs/module-formats.md#exports )。

9
martin