在没有 Angular 的情况下理解 TypeScript 和 SystemJS

Understanding TypeScript and SystemJS WITHOUT Angular

我最近一直在研究并使用 TypeScript 做一些简单的 "hello worlds"。有些事情我想我无法理解,那就是如何将 System.js 与 TypeScript 一起使用。互联网上的每个教程或演示都是关于 Angular2 的,我还不想涉及 Angular2。

例如,我有以下项目结构:

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" 的简单 class 包含以下代码:

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

export default Alerter

还有一个 app.ts(这是我的 "main" 应用程序文件),代码如下:

//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 中,我只想用 System.js 导入这个 app.js 并且只想从控制台调用 "test" 函数。但它不起作用。无论我做了什么,我都无法访问该功能。我看到第一行 console.log 正在执行,但是当我尝试从 chrome 控制台调用 test() 时,它是未定义的。

如果我从 main.ts 中删除 "alerter" class 依赖项,一切正常。因为编译后的 app.js 只包含 console.log 次调用和函数定义。

这是我在 index.html

中的 System.js 电话
System.config({
    packages: {
        "components": {
            defaultExtension: "js"
        }
    }
});

System.import("components/app");

我现在真的很绝望,我想我应该回到 Jquery 天。这么简单,却做不到。

我明白这里发生了什么。这与正确使用 TypeScript export 关键字和 SystemJS 有关。

根据您的描述,您基本上想使用 SystemJS 导入一个 JavaScript 文件,类似于仅使用 <script> 标记,然后使用其全局定义的函数。

但这意味着您需要了解 TypeScript 是如何编译您的文件的。 https://www.typescriptlang.org/docs/handbook/modules.html 的文档说:

In TypeScript, just as in ECMAScript 2015, any file containing a top-level import or export is considered a module.

这就是你正在做的。 app.ts 文件有一个 importalerter.ts 文件有一个 export 语句,因此它们都将被编译为模块。然后从你的 tsconfig.json 我看到你使用的是 system 格式(但是,这里并不重要)。

模块的一个主要好处是它们不会泄漏任何超出其范围的全局对象。因此,当您调用 System.import("components/app") 时,test() 函数仅存在于该模块中。但是你可以导出函数并在模块加载后调用它:

这意味着您需要先导出函数:

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

然后 System.import() returns 一个由模块导出对象解析的 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).