Angular2 中对 SystemJS 的需求是啥?

Posted

技术标签:

【中文标题】Angular2 中对 SystemJS 的需求是啥?【英文标题】:What is the need for SystemJS in Angular2?Angular2 中对 SystemJS 的需求是什么? 【发布时间】:2017-04-01 21:08:48 【问题描述】:

我是 Angular 和 Angular2 的完全初学者。我对工作流程的结构感到困惑。我一直在查看 Angular2 站点中的sample project。

如果我错了,请纠正我,但到目前为止我所知道的是所有的打字稿都被打字稿编译器转译成 javascript。那么编译出来的javascript其实就是在浏览器中运行的。

现在,如果我使用 ES6 导入语句将 javascript 文件导入打字稿,如下所示:

import  NgModule       from '@angular/core';

为什么我又需要使用 SystemJS 来加载它们-:

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() 语句。 首先 require() 在 ES5 中是如何工作的 js 文件,其次,如果是这样的话,SystemJS 在做什么。

这真的让我很困惑。任何帮助将不胜感激。

【问题讨论】:

AFAIK,浏览器还不支持 ES6。所以 SystemJS 帮助它。无论如何等待其他人的详细回答。 【参考方案1】:

tsc 将 typescript 编译成 JavaScript 时,你最终会在本地系统上得到一堆 js 文件。它们需要以某种方式加载到浏览器中。由于浏览器还不支持原生 ES6 模块加载,你有两个选择,要么按照正确的依赖顺序将它们全部放入你的 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() 的不同语法。我猜angular2 入门教程中首选 systemjs 加载器,因为它实际上可以加载tsc 支持的所有模块类型。但是,如果您想将模块加载为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 加载app 模块,该模块映射到systemjs.config.js 中配置指定的项目目录结构中的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',

首先要了解的是,这些是映射,而不是依赖关系。这意味着如果您的文件都没有导入@angular/core,它将不会加载到浏览器中。但是,您可能会看到这个特定模块是在 app/app.module.ts 中导入的:

import  NgModule       from '@angular/core';

现在,为什么会有映射。假设systemjs 将您的app/app.module.js 加载到浏览器中。它解析其内容并找到以下内容:

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

现在systemjs 知道它需要解析和加载@angular/core。它首先经过检查mappings 的过程,如文档中所述:

地图选项类似于路径,但在 规范化过程。它允许您将模块别名映射到 位置或包裹。

我将其称为命名模块的解决方案。因此,它会找到映射并将@angular/core 替换为node_modules/@angular/core,这就是放置真实文件的位置。

我认为systemjs 试图模仿node.js 中使用的方法,您可以在其中指定没有相对路径标识符['/', '../', or './'] 的模块,就像这样require('bar.js')node.js

然后 Node.js 从当前模块的父目录开始,并且 添加 /node_modules,并尝试从中加载模块 位置。

如果您愿意,您可以避免使用命名映射并使用这样的相对路径导入:

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

但是,这应该在项目和 lib 文件中对 @angular.core 的所有引用中完成,包括 @angular,至少可以说这不是一个好的解决方案。

【讨论】:

@Maximus 那我们为什么要在system.config.js 中单独指定Angular2 依赖项。告诉我一些事情,app_module 文件包含 Angular2 的所有依赖项,所以我们为什么不能只加载它而忘记单独提及依赖项。 @angular/http 和 rxjs 依赖是分开指定的,即使 http 模块是基于 rxjs 的。 感谢您花时间详细解释这一点。在其他任何地方都很难找到这些信息。 多么棒的读物。我一直在努力理解整个生态系统是如何运作的。 @Sangram,谢谢,您可以关注angularindepth.com 发布,了解其他任何地方都找不到的更深入的主题 @karns,当前版本的 CLI 中不使用 systemjs。 Webpack 是底层构建系统

以上是关于Angular2 中对 SystemJS 的需求是啥?的主要内容,如果未能解决你的问题,请参考以下文章

在 angular2 cli 中哪里可以找到 systemjs.config.js

Angularfire2 在 Angular2 中导致 SystemJS 错误

SystemJS Typescript 输入冲突

rxjs 与 systemjs 捆绑用于 Angular 2.0 应用程序

如何使用 systemjs 在最小的 Angular 2 应用程序中加载 RxJS?

我如何实际部署 Angular 2 + Typescript + systemjs 应用程序?