Angular: 大型项目上的 NgModule

创建于 2016-08-07  ·  144评论  ·  资料来源: angular/angular

我正在提交... (用“x”勾选)

[X] feature request / proposal

我一直在阅读有关 NgModule 的文章,我想列出一些我不完全确定当前提案(https://docs.google.com/document/d/1isijHlib4fnukj-UxX5X1eWdUar6UkiGKPDFlOuNy1U/pub)考虑的用例.

语境

我是构建企业框架(基于 Angular 2)的团队的一员。 然后,该框架将成为同一生态系统中其他应用程序的基础。

我们将框架划分为更小的项目/模块(将它们视为单独的 npm 包)。 这些模块是一组控件(然后在其他模块中重复使用)或使用这些控件的页面。

例子

一个简单的例子可以是:

控制模块

import {Component} from "@angular/core";

@Component({
   selector: "my-combobox",
   ...
})
export class MyComboBox{

}

清单模块
// 清单模块依赖于控制模块。 控件被视为第 3 方模块。

import {Component} from "@angular/core";
import {MyComboBox} from "controlsmodule/components/mycombobox";
// Please note that we are only loading a specific component within the module, not all components inside that module.

@Component({
     selector: "my-checklist-page",
     directives: [MyComboBox, ...],
     ...
})
export class ChecklistPage{

}

Bootstrap 不知道 Controls 和 Checklist 模块。 它们是延迟加载的,具体取决于用户的交互。 在这种情况下,如果用户导航到 Checklist,则会加载 ChecklistPage 组件,然后 MyComboBox 也将跟随(因为 ChecklistPage 生成的_import_)

清单模块还有其他十几个组件。 每个都依赖于来自多个模块的其他十几个组件。

将所有组件都导入到 NgModule 声明中是不切实际的(不是说几乎不可能)。 我们正在谈论在应用程序运行时_可能_使用的数百个组件。

此外,应用程序需要尽可能模块化和延迟加载。 应用程序内的不同交互将导致加载完全不同的模块。

预期/期望的行为

当前的解决方案,带有组件范围的指令,对于这个用例来说就像一个魅力。 不确定这将如何在 NgModule 中发挥作用。

不仅如此,目前我们可以清楚地看到每个组件所需的依赖关系(在本例中为 ChecklistPage)。 使维护变得更加容易。

将所有需要的组件导入到 NgModule 中,然后在几个组件上模糊地使用它们似乎是小型应用程序的绝佳解决方案。 我觉得在长期开发中,经过多年的多次迭代,团队轮换,...​​...让每个组件明确说明它所依赖的内容而不查看模板(并且在缺少某些内容时出现编译错误)是一个很棒的优势。

结论

如果我的解释清楚,请告诉我。 本期的目的是提高对这种情况的认识,并从您那里获得有关如何进行的反馈。

我们可以向您展示我们当前的工作,我们在去年开发的 8 个项目中拥有数百个 Angular 2 组件(自 alpha 27 以来)。

最有用的评论

感谢大家的反馈,很抱歉花了这么长时间才得到回复——我们上周一直很忙(将内部 Google 应用程序迁移到 NgModules,所以我们也感受到了重构的痛苦)

让我看看我是否可以澄清这里的一些问题和误解。

关于@NgModule()(以及@Component和任何其他Angukar 装饰器)首先要了解的是它们是纯粹的编译时构造——它们的存在是为了允许角度编译器在应用程序中发现依赖关系图。

我们的装饰器所做的(简化)版本如下所示:

//simplified Component decorator
export function Component(componentConfig){
  return function(componentClass){
    Reflect.defineMetadata('annotations', componentConfig, componentClass);
  }
}

它们不会以任何方式改变或修改装饰类的行为——它们只是附加一些元数据。 Angular 使用该元数据来构建您的应用程序并编译模板。

在 JiT 模式下,这发生在“及时”——在你调用 bootstrapModule 和你的第一个组件渲染之间,Angular 的编译器使用 Reflect API 检索附加到类的元数据:

let metadata = Reflect.getOwnMetadata('annotations', componentClass);

然而,在 AoT 模式下,这有点不同——在构建时,我们_静态地_(即,不执行您的代码)通过扫描装饰器从源代码中提取相同的元数据。

当您引导单个组件时,这可以正常工作,但是我们已经从正在做更复杂事情的开发人员那里听到了很多反馈 - 引导多个根组件,或基于身份验证状态引导不同的组件等。

因此,虽然@Component装饰器使我们能够静态分析组件,但我们没有能力可靠地静态分析 _Application_

属于“应用程序”的东西

  • PLATFORM_DIRECTIVES/管道/提供者
  • 你之前添加到 bootstrap() 的东西
  • 编译器级别设置
  • 多个根组件
  • 服务器端使用。

NgModules 引入了一组静态可分析特性的概念。 有趣的是,在 AoT 编译模式下,我们分析您的根模块,并为应用程序中的每个模块_生成_一个 ModuleFactory - 这是模块的预编译版本,它包含_仅_您在模板中静态引用的工厂以及您标记为“entryComponents”

因为我们已经提前提取了编译所需的信息,所以我们实际上可以对装饰器进行 tree-shake (ngc 将自动处理最终处理) - 而不是从根模块开始捆绑您的应用程序,您开始在您生成的根 Module_Factory_ 中,它仅包含应用程序中实际使用的代码,因此您无需为模块化付出代价,并且 rollup 和 webpack2 等工具可以_更_高效地工作

更多内容在下一个回复中...

所有144条评论

在我看来,ngModules 并没有禁止你要进行的设置,你玩过吗? 您可以有几个不同的模块并进行延迟加载等。 http://plnkr.co/edit/NAtRQJBy50R19QAl90jg?p=info

对于与您似乎正在尝试做的事情更相似的事情,请注意 material2 的过渡: https ://github.com/angular/material2/pull/950/files

@qdouble

感谢你能这么快回复。
查看https://github.com/jelbourn/material2/blob/ecbb4f42e0473899f6ad15d8e4ed8f262ded7a99/src/components/button-toggle/button-toggle.ts ,您是说为了实现我们现在拥有的相同功能,我们需要在每个组件上声明一个 NgModule? (那是在文件末尾添加的内容对吗?)

此外,它不包括我在最初声明中提到的维护问题。 对我来说,在其装饰器上声明每个组件/指令的依赖关系是我想保留的一个很大的优势。

@jpsfs如果您确实想为每个组件创建单独的范围,那么我想您必须为每个组件创建不同的 ngModules。 虽然这可能会在您的情况下创建更多代码,但我认为它会通过限定我的模块而不是每个组件来为绝大多数其他人创建更少的代码。

至于第二个问题,您可以将 ngModule 和组件声明为彼此相邻,因此虽然它会在您的文件中添加额外的 3 或 4 行代码,但我认为这不会造成大问题。

我想说绝大多数用例不需要每个组件的作用域指令,但在它需要的情况下,ngModules 仍然支持多行代码。

@qdouble谢谢。

我不确定这是一种或/或情况,我可以看到两种情况一起工作,无需删除我们已有的功能。 如果有人想使用模块,我认为这是一个很好的补充。 同时,该框架可以在没有模块的情况下工作(就像今天一样)。 我相信即使是离线编译问题也可以按照目前的情况来解决。

我让这个打开看看是否有人有什么要补充的。

@jpsfs明白,如果我遇到你的情况,我绝对希望他们让两个选项都打开:)

他们确实在您发布的文档中写了弃用组件指令的原因,就它创建两个范围而言,他们认为 ngModule 范围足够小并且更符合 ES6 模型。

一个团队成员之前也提到过,有两种不同的做事方式通常是有问题的......从长远来看,我可以在这里看到这个问题......如果你有一些人们正在使用 ngModules 和其他项目的项目如果没有,则会产生更多的维护、培训和兼容性问题。

在最终确定之前,永远不知道这个项目会朝着什么方向发展,所以我们会看看他们是否考虑到你所说的。

甚至我目前也在为一个大型企业应用程序设计架构。
@jpsfs的情况不同,我对 NgModules 感到很兴奋,并且我的应用程序架构几乎都基于 NgModule。

每个模块都有自己的一组路由和组件依赖项。 我们永远不能只用一个组件来创建一个功能,它需要路由、至少一个智能组件和几个哑组件和服务来配合它。 将这一切都插入一个模块,你就可以开始了。

来到延迟加载,而不是为每个组件加载代码,每个 NgModule 代码将立即加载看起来不错,因此您的功能在下载后完全可用。

创建模块的层次结构也更简单,它免费提供了很好的即插即用功能。

我们目前也在开发一个包含大量组件(不是数百而是几十个)的应用程序。 我们不需要将这个应用程序拆分为几个(延迟加载)模块,但是现在将所有这些组件导入引导文件并将它们传递给declarations不知何故感觉不对并破坏了组件的封装. 正如@jpsfs所说,之前非常清楚哪些组件和指令被另一个组件使用。 所以我也很感激有这样的选择:

  • 如果它是一个非常常用的指令,请在模块中声明它
  • 如果它类似于TaskListItem ,只需将其导入TaskList

也许一些核心成员可以更多地说明弃用第二种方法的决定。 已经用了几个月了,感觉还不错;)

为了呼应@choeller的观点,离开提供组件封装的能力确实感觉很奇怪。

我特别担心的是,现在组件名称/选择器在整个应用程序中泄漏,而在您可以通过适当地包含特定指令来为不同组件重用选择器之前。

现在所有选择器都需要每个组件都是唯一的,对吧? 还是我误解了这是如何工作的?

我觉得原始功能与 CSS shadow-DOM 仿真提供的类似优势相匹配,因为我们可以更少担心跨大型应用程序的选择器冲突等。 这是 IMO 的一大优势。

我对 ngModule 的第一个想法是“哦,就像在 angular 1 中一样”。 与角度 1 一样出色,角度 2 在很多方面都好得多。 对我来说最好的一点是,组件创建了某种依赖树。 我有一个带有路由器的主要组件,它用它自己的组件定义了几个入口点。 每个组件都知道它需要什么,主组件没有理由知道树末端的任何组件需要什么。
现在我们回到了 Angular 1 的美好时光,我们有一个巨大的模块定义。
还记得您的应用程序入口点看起来像这样的时代吗?

angular.module("myApp")
.controller("…")
.controller("…")
.controller("…")
.controller("…")
.controller("…")
.controller("…")
.component("…")
.component("…")
.component("…")
.component("…")
.component("…")
.component("…")
.directive("…")
.directive("…")
.directive("…")
.directive("…")
.directive("…")
.directive("…")
.directive("…")
.service("…")
.service("…")
.service("…")
.service("…")
.service("…")
.service("…")

我以为这属于过去。 我开始使用 ng-metadata 来改进旧的 angular 1 项目并为迁移做准备。 我真的很喜欢这样一个事实,它有一个依赖树,而不是一个全局的“可能出现在这个应用程序中的内容”列表。

这使得可重用组件更加困难。 我不明白这如何让一切都在全局/模块范围内变得更好,我认为 ng2 团队已经屈服于不想改变的 ng1 用户。

@DaSchTour @damiandennis我理解对该架构的批评,但是,将其称为某种全局范围是不准确的,他们建议您采用的方法是拥有功能模块: https ://angular.io/docs/ts

@qdouble好吧,所以最后它只是将所有组件更改为模块。 尽管这被宣布为减少样板代码的更改,但它引入了大量需要样板代码。

虽然在 RC4 之前,应用程序的每个“页面”/视图的组件就足够了,但知道我必须为每个视图创建一个模块、一个组件和路由。 我明白了意图。 但不知何故,我的印象是,它旨在使某些事情变得更容易,而不涉及许多其他问题。 即使使用功能模块模式,我也必须通过添加可能需要的所有内容来将它们切得很小以避免依赖地狱,因为我看不到应用程序的哪个部分需要哪些组件。

最后,模块是如此之小,以至于它们具有与当前组件相似的重复依赖列表。

最后,它并没有解决它的设计目的,只是增加了很多工作。 我觉得这里的设计和现实有些不匹配。

开发人员懒惰/缺乏时间并走捷径。 这鼓励开发人员采取将所有内容都包含在引导程序中的快速路线。 至少对于组件,需要包含您的依赖项。 是的,它们也可能是懒惰的,并为整个应用程序创建了一个组件,但这更容易修复,因为依赖项都在该组件中,而不是在引导文件和应用程序中的每个组件文件之间混合。

@DaSchTour如果每个组件都需要它自己的范围,那么是的,它会创建更多样板......但我认为 ng 团队认为对于大多数人来说,为每个功能部分创建一个新模块就足够了,一些组件将能够存在于每个特征空间中。

现在显然,没有一个适合所有解决方案的解决方案,对于某些人来说,拥有组件级指令可能更简单。 然而,这里的很多评论似乎都暗示他们希望你只创建一个带有一个巨大的 ngModule 树的应用程序......

我认为如果批评是基于他们的实际设计建议而不是他们没有建议的稻草人设计模式(即创建一个只是一个巨大的 ngModule 的企业应用程序),那么它会更有成效

@qdouble设计模式很简单。 它使用依赖树而不是将依赖关系移动到全局模块范围。 我想重点是,可重用组件现在必须是模块,即使它们非常小并且只有很少的功能。 Angular material2 就是一个很好的例子。 Button 是一个模块,包括一个组件。 也许这是许多开发人员的普遍误解,即模块不仅仅是一个按钮。 现在让我们再想一想。 只是按照这篇文章https://angularjs.blogspot.com/2016/08/angular-2-rc5-ngmodules-lazy-loading.html的想法,我发现我自己,我有很多模块导入 angular material2 模块列表,每个模块都由一个组件组成。
事实上,这正是 angular material2 所做的。

没有人真正明白这一点,为什么我们现在必须将“所有”组件包装到模块中。 或者,也许我们必须将其视为声明的拆分。 组件依赖关系现在是一个模块,组件定义和以前一样。

我想重点是,ngModules 不仅是一个让事情变得更容易的好补充,而且我们被迫改变一切。 也许有人应该明确解释为什么两者不能共存。

@DaSchTour好吧,是的,我同意,如果您创建的每个组件都需要它自己的模块,那么使用 ngModules 会创建更多样板......我只是认为团队认为大多数人并不需要每个组件都需要这种级别的分离零件。

我发现我自己,我有很多模块可以导入 angular material2 模块列表,每个模块都由一个组件组成。

您将为此使用共享模块: https://angular.io/docs/ts/latest/guide/ngmodule.html#! #共享模块

现在,我不确定他们为什么认为有必要完全删除拥有组件范围的能力,但对我来说,一些批评是使使用 ngModules 比实际更困难或使设计看起来更比实际马虎。

我认为删除组件范围指令/管道的批评是完全有效的。 但是,我认为没有必要让 ngModules 看起来没有好的设计模式。

@qdouble我认为没有人怀疑 ngModules 有很好的设计模式。 但据我了解,模块只是一组组件、指令和服务的包装器,以将它们公开是应用程序的一个单元。 这是有效的,也是一个好主意。 但是为什么我必须为模块而不是组件定义依赖关系(可能只存在于模块内部)。

以@choeller 为例
模块 TaskDashboard 有以下内容

  1. 任务列表组件
  2. 任务项组件
  3. 任务过滤器组件
  4. 任务服务

Taskitem 组件只需要在Tasklist 内部,Tasklist 依赖于Taskitem 组件。 Taskfilter 不需要 Taskitem 组件。 现在我没有将 Taskitem 作为任务列表中的依赖项。 下一步是,我创建一个 TaskSearch 模块。 我添加以下内容。

  1. 任务列表组件
  2. 任务搜索组件
  3. 任务服务

好吧,我错过了 Taskitem 组件,它坏了。 Tasklist 总是依赖于 Taskitem,但是这个依赖隐藏在模块中。 重用组件变得更加困难,并产生了额外的错误来源。

好的,在进一步阅读模块指南时,我发现了这一行

组件、指令和管道必须完全属于一个模块。

因此,该示例准确地显示了这里提出的问题。 不能有任何共享组件。 因此,从我的示例中,我必须将所有内容拆分为模块。

在处理使用 ngModule 时出现的奇怪且难以捉摸的错误时,我还发现,扩展组件现在不像以前那么好用了。 我有一组具有所需依赖项的组件,我可以简单地对其进行扩展。 现在我必须负责在包含我的扩展组件的模块中导入依赖项。

@DaSchTour在你的例子中,Taskitem 应该是任务列表模块的一部分......所以从逻辑上讲,这将是一个模块中的两个组件,而不是每个组件一个。

正如@sirajc指出的那样,典型的设计模式是拥有一个顶级智能组件,然后是额外的哑组件......所以在典型的现实世界应用程序中,大多数模块将由几个组件组成(无论是智能/哑组件模式或相关功能模式),而不是每个模块只有一个组件,除非您只是构建 3rd 方组件或其他东西。

@qdouble @DaSchTour确实,新架构并不一定意味着您将所有组件都列在一个文件中,但是看看我们正在构建的应用程序,我目前几乎同意@DaSchTour的声明

我觉得这里的设计和现实有些不匹配。

所以我们有很多组件代表页面上的小单元,比如TaskListItem ,它们 100% 绑定到一个特殊的视图。 为这些页面中的每一个创建模块将是一种过度杀伤。 在我们的案例中,拆分为多个模块的原因很少,但封装组件的原因更多。

tl;博士
能够在模块级别定义一些依赖关系很酷,但很遗憾,我们无法再在组件级别定义依赖关系

@DaSchTour ,在material2按钮组件中绑定到模块以使其独立。 需要使用按钮的可以导入ButtonsModule 。 此外,如果您看到模块代码,它包含两个组件MdButtonMdAnchor ,然后将其包装在ButtonsModule中会使事情更容易使用

我想知道是否可以创建一些混合组件/模块对象,将这两个概念合并为一个以防止文件重复。 基本上能够将组件声明为模块一次,然后根据需要将其作为模块导入。 它将尊重当前的方法,但尽量减少样板文件。

@choeller我同意拥有模块是一个很好的补充,在组件级别删除依赖项但是似乎是错误的。

总结其他人在上面写的内容,我认为这里的核心思想不是体验一个巨大的项目的痛苦,其中有一个巨大的模块,其中包含数百个组件。 相反,它是用合理数量的中型 NgModule 构建应用程序。 足够大以至于它们不是微不足道的,足够小以至于你没有大量的它们; 沿着断层线划分,这些断层线倾向于促进旧式计算机科学高内聚和低耦合意义上的重用和模块化。

通过这样做,模块应该是一个非常有用的概念,可以让它们在大型项目中发挥作用。

这很好地总结了@kylecordes。 NgModules 有助于很好地组合应用程序。 小的可重用模块构成了整个应用程序。
其他好处包括指令的环境可用性。 之前我们曾经在整个应用程序中添加 ROUTER_DIRECTIVES。 现在RouterModule.forRoot()为我们做到了。
BrowserModule、CommonModule、FormsModule 比在每个组件中包含指令更有意义。

另一方面,MaterialModule 提供了一切,如果您需要更好的控制,那么 ButtonsModule 等将帮助我们。
这就是构图之美。 拥抱它并创造你的交响乐

最重要的是 LazyLoading。 如果不是 NgModules,如何定义需要一起创建一个可路由单元的组件和服务的数量。 你不能下载松散的文件,因为这会导致大量的网络请求。 否则,您需要创建一个捆绑器,在其中列出所有依赖文件以创建一个捆绑包。 NgModule 使用直观的语法来做到这一点。

@kylecordes

总结其他人在上面写的内容,我认为这里的核心思想不是体验一个巨大的项目的痛苦,其中有一个巨大的模块,其中包含数百个组件。 相反,它是用合理数量的中型 NgModule 构建应用程序。 足够大以至于它们不是微不足道的,足够小以至于你没有大量的它们; 沿着断层线划分,这些断层线倾向于促进旧式计算机科学高内聚和低耦合意义上的重用和模块化。

对于应用程序开发人员和库开发人员来说,这些故障线不会有很大不同。 图书馆开发人员应该是少数,但这似乎是主要的抱怨。 在构建可重用的框架时,想要最小化模块大小的目标会导致很多额外的噪音。

@sirajc

最重要的是 LazyLoading。 如果不是 NgModules,如何定义需要一起创建一个可路由单元的组件和服务的数量。 你不能下载松散的文件,因为这会导致大量的网络请求。 否则,您需要创建一个捆绑器,在其中列出所有依赖文件以创建一个捆绑包。 NgModule 使用直观的语法来做到这一点。

我可以使用旧路由器轻松地做到这一点,只需使用一个组件,该组件充当我应用程序的一部分的容器。 这个模块只引入了它需要的直接组件,这些组件会引入它们自己的依赖项。 任何体面的模块加载器都能够加载该组件的依赖链(通过导入),而无需在顶层组件中包含每个子依赖。 对于应该使用某种模块解析逻辑的捆绑器也是如此。 简而言之:从延迟加载的角度来看,开发人员没有理由需要在顶级组件中声明所有包含的依赖项。

真的感觉ngModule是一个寻找问题的解决方案……

对于能够打开任何给定组件、查看它的指令数组并确切知道它所依赖的组件/指令,有一些话要说。 在使用它的众多组件中导入按钮组件是否更冗长? 是的,但我宁愿有一些额外的样板文件,并且让事情变得明确且可立即扫描,而不是搜索组件/模块树以查看组件 Y 如何在其模板中使用组件 X 而无需导入它。

上面提出的论点是,如果有人希望使组件彼此隔离,他们可以为每个组件创建一个模块——但那时你编写的样板文件甚至比原来的组件还要多。

ngModule 显然还有其他我没有提到的好处,虽然能够将应用程序的各个部分捆绑到功能模块中是有好处的,但在清晰度和将组件封装为不会到处泄漏范围的代码。

ng2 团队必须从 ng1 向社区做出的“硬推销”的一部分是“是的,你将不得不为每个组件导入和声明你的指令,但相信我们,你会喜欢更明确的随着您的应用程序的增长”。 我非常相信在大规模团队环境中为了可读性和可扫描性而经历一些重复,在这种环境中,团队成员在任​​何给定时间可能只处理非常小的组件子集。

至少,我看不出 ngModule 和 directives/pipes 属性不能共存的任何原因。 另一种选择是,为 ng2(直到 RC5)编写的每个应用程序中的每个组件现在都在使用需要重构的弃用代码。 这些真的不是我在开发后期所期望的那种变化……老实说,这很令人不安。

ngModules 本质上确实需要重写大多数应用程序,如果它们的结构正确的话……但最大的误解是,当现实是你的模块可以和你想要的一样大或一样小时,它们会强制一个特定的范围级别。是。

如果您绝对需要为每个组件创建一个新模块,则会导致更多样板 = 有效。

大多数应用程序应该要求您为每个组件创建一个模块 = 无效(特别是如果您使用智能/哑组件模式和功能模式)

今天,我第一次感受到了一些同事向我传达的客户端/JavaScript 疲劳。 在过去的几天里,我浪费了无数个小时,试图重构我们的 RC4 应用程序以使用新的 RC5 模块。

我绝对同意这个线程中的一些观点,特别是关于让开发人员在他们希望构建其组件依赖项的方式方面做出选择。 我不喜欢较大的 ngModule 会稀释组件之间清晰的依赖关系图,而较小的 ngModule 会为每个组件添加额外的样板代码。 在分析模块时,我所知道的是,至少有一个引用的组件需要模块的导入或兄弟声明中的内容。 如果我将一个模块分成多个其他模块,我基本上会在确定新模块需要哪些依赖项时反复试验(仔细检查组件模板会有所帮助)。

最后,如果我“忘记”导出组件,我的组件根本不会呈现,也不会给出错误。 如果您有一个非常平坦的模块图,它很容易找到,当您有多个级别时几乎不可能!

在这个阶段,我感到沮丧、失望和沮丧。 我已经让一个由开发人员和业务利益相关者组成的团队加入了一项我现在不知道是否可以推进的技术。 我真的希望能找到一个更好的平衡点。

在这个阶段,我感到沮丧、失望和沮丧。 我已经让一个由开发人员和业务利益相关者组成的团队加入了一项我现在不知道是否可以推进的技术。 我真的希望能找到一个更好的平衡点。

我想这里的重点是,有大量的开发人员在等待这项技术准备好投入生产,尽管它被称为候选发布,但每个新候选都会带来重大变化,感觉更像是一个 alpha。

我敢肯定,很多团队都在他们的新应用程序的架构上花费了数小时,现在一切都变得疯狂了。

下一个重大变化会是什么? 我什至无法想象,但我担心有人会找到一种方法来引入下一个重大变化。

来自 RC5 博客文章:

如果你写过任何 Angular 2 代码,你可能会问自己“但为什么我必须列出所有这些东西!?” - 特别是如果你注意到 Angular 2 中的某些指令和管道是“特殊的” - 它们可以在你的整个应用程序中使用,而无需你做任何事情(例如 *ngFor / *ngIf / *ngSwitch)。

我个人在很长一段时间内都没有看到有人问过这个问题。 直到 RC5,Angular 团队、在线学习资源、书籍等都已经非常清楚地说明了为什么需要这些声明——而且似乎每个人很久以前就接受了(并且有些人接受了)这个事实。

至于为什么有些是“特殊的”并且不需要声明的问题,我认为没有人会反对有一个关键的“低级”指令列表,这些指令无处不在,值得“祝福”由平台持有者(Angular 团队)在全球范围内提供。

如果已经存在代码来处理将指令提升到单个 ngModule 中,并且该代码对最终用户不可见,那么允许这两种方法有什么害处? 这是一个真正的问题,因为我可能不知道如果开发人员混合搭配该方法可能会发生的一些错综复杂的情况。 Angular 2 中还有很多其他“拆分选择”——模型与模板驱动的表单、3 种不同的语言选择、​​输入/输出/主机装饰器与属性方法——此时另一个有什么害处?

我可以继续说下去,但我想说的是,Angular 团队和社区已经投入了相当多的时间来强调更明确的信息是一件 _good_ 的事情,只是为了突袭——在 _release所有时间的候选人 5_——向我们展示一个在相当长一段时间内都没有成为问题的解决方案。

团队中的任何人都想插话吗? 我不希望这成为一个回声室——我很想听到争论的另一面。 看起来这么大的东西是突然出现的,没有考虑工具、培训或我们看起来离最终版本有多近。

对于许多希望使用备受期待的 Angular 2 尽早开始开发的开发人员来说,这个讨论提出了一个非常大的问题:重大更改。 从 beta 17 开始,我做了一堆基于 angular 2 的概念验证,并让重要的公司和组织采用它。 我不后悔,但我也不确定我做得是否好。 最近的一些项目是 POC,而这场战斗是针对 Vue.js。 Angular 2 显然赢得了这场战斗。 但是今天,随着所有代码的重写、重大更改、非 RC 版本的发布,人们开始追赶我,而且情况变得非常严重。 如果我提供 Vue 或 React 开发,情况就不会如此,这非常令人沮丧,因为 Angular 2 可以让人们参与其中。

在我看来,我对候选发布者的定义与 Angular 团队不同。

至于主题,NgModule,我完全共同签署@jpsfs ,如果你必须在你的模块声明中列出你所有的组件和微组件,你最好在某个地方有一个prune函数或者成为建模之王,因为它可能非常强大,但对于大型项目来说太敏感了......

我认为这种额外的更高级别的模块化几乎是不可避免的。 并且各种竞争框架最终会以类似的方式结束。

不过我想知道,是否真的有必要让这个新的粗粒度模块系统与底层模块系统完全正交,或者是否有可能隐含地制作每个顶级目录以及其中包含的所有 Es6 模块包含一个粗粒度的 NgModule。 也许这种约定驱动的机制可以添加到当前机制之上,因此为愿意遵循这种约定的项目删除 NgModule 样板。

(当然我也和这里的其他人分享了在“RC”阶段发生如此重大变化的挫败感......我相信核心团队不过,如果他们把这一切都做完,他们会攻击高水平模块化,以及驱动它的力量(延迟加载,预编译)更接近项目的开始,而不是在发布候选阶段。这就是生活,总是很容易回顾过去并认为“如果我们那时已经知道我们现在所知道的......”)

如果编译支持是 ngModule 出现的主要原因,那么它绝对不应该取代现有的基于组件的指令和管道范围。
另一方面,基于路由配置和依赖关系,角度编译器理论上可以自己将应用程序代码拆分到适当的编译单元(模块)。

就我个人而言,我觉得这种变化带来了样板的低端,没有太大的优势。

我可以争辩说,我们可以使用组件作为应用程序的单元,而不是使用模块。 无论如何,这可能是人们在 rc-5 之前所做的事情。

如果该框架的内部目的需要这些模块(例如延迟加载和相关的东西),那么它们应该以某种方式对开发人员隐藏,这样它们就不会污染代码并导致更糟糕的编码体验。 而且我什至不是在谈论可维护性......

我第一次看到 angular2 我想:'嘿,他们正朝着正确的方向前进,似乎他们从 angular 1 中学到了一些东西'。 您从 angular 1 中放弃了内部模块,而是使用了 ES 模块,也欢迎其他更改(组件、打字稿等)

但是我越是等待它变得稳定,它就越让我畏缩,因为破坏性的变化、缺乏文档和糟糕的错误消息。 现在你有了这个 NgModules 并且基本上撤消了自 angular 1 以来你改变的至少一件好事。

更糟糕的是,如果我有一个以 rc-2 或 3 开头的应用程序(理论上几乎是稳定的),我现在必须做很多工作来创建一个好的 angular2 rc-5 应用程序,并且需要以某种方式解释客户和其他开发商这个。 即使我这样做了,上帝知道你接下来会改变什么,而我的工作可能已经风向标了。

我知道 Angular 2 已经处于早期阶段有一段时间了,但我们是 RC-5,人们现在使用 Angular 2 的项目非常可靠,你仍然在做改变,不仅从 API 的角度来看,而且作为一种方式在 Angular 中思考。

ngFor 不好,一些导入发生了变化,或者注入组件在 5 个版本中动态更改了 3 次,但我可以接受和理解。 不幸的是,在开发周期这么晚的时候,带来的变化不仅会破坏应用程序,还会破坏开发人员在这个框架中的思维方式。

无论如何,正如你所看到的,很多人都同意我要说的话,有些人甚至更生气。 你有一个人们信任的庞大框架(主要是因为谷歌),但你不能把一切都押在上面,或者你可能会感到惊讶,因为每个人都有选择(在谈论 JS 时,有很多)。

如果新模块有一个模板,它可能是组件。 想想看。

下一步是将所有变量和函数从 Components 移动到 Module 类中。 严重地。 然后我们在闪亮的模块中有一个巨大的_所有可能的_动作中央库。 这太棒了! 我不知道你为什么停留在声明和依赖的集中化。 那里有更多去中心化的结构化代码行!

核心团队中的任何人都可以对这一切发表意见吗? 根据上次每周会议的记录,该团队正在继续删除已弃用的 API。

@mhevery

当我今天升级时,这整个提升的事情真的很巧妙地破坏了我的应用程序。

  • 我不得不将组件的选择器从一个类更改为另一个元素,因为在一个完全不相关的组件中,该类用于样式化并突然开始实例化该组件。
  • 我很确定 #10850 只存在,因为我有两个名为SpectrumComponent的组件(一个整个页面可以在没有选择器的情况下导航到,一个在多个组件中使用的“共享”组件用于使用选择器进行可视化)。 提升可能会因此而感到困惑 - directives:声明不会。

对于我的 SVG 组件,这现在变成了一个更大的问题,因为我不能在那里使用自定义元素。 我还不知道如何确定这些范围以确保它们的类选择器不会在应用程序的其他任何地方(!)触发组件实例化。

这些天似乎很流行在大型项目中忽略“Beta”和“Release Candidate”(ASP.NET Core 是另一个备受瞩目的例子)的含义。 但是,面对有可能在任何地方默默地破坏应用程序的破坏性更改,这比应有的更令人沮丧。 我真的希望这些变化带来的承诺很快就会开花结果。

ngModules 是有道理的。 删除在组件级别声明/提供的能力不会。

小例子

我有一个包含许多不同对话框的应用程序(一些简单的对话框 - 我们称它们为 simple-1 和 simple-2,一个复杂的对话框包含其他组件 - 我们称之为复杂的 3)。

目前我有一个简单的文件夹结构(显然应用程序不属于这个)

- dialogs
   - simple-1 (containing single component and template for simple-1 dialog)
   - simple-2 (containing single component and template for simple-2 dialog)
   - complex-3 (contains the main complex-3 component plus a number of other internal components)
      - internal-component-1
      - internal-component-2
      - internal-service-3

对我来说,这是一个完美的功能集,可以放在一个独立的功能模块中。 所以我添加

   dialogs.module.ts
   - simple-1
   - simple-2
   - complex-3
      - internal-component-1
      - internal-component-2
      - internal-service-3

我将 Simple1Component、Simple2Component 和 Complex3Component 作为声明添加到 DialogsModule。 完美,运作良好并且完全有意义。

但...

complex-3 中的所有内部组件呢? 我现在有两个选择

  • 将所有外部复杂组件和服务添加到 DialogsModule。 这是一个糟糕的决定,因为它破坏了 complex-3 的封装,因为现在 dialogs 模块中的所有内容都知道它的内部结构(internal-component-1、internal-component-2、internal-service-3)
  • 使 complex-3 成为它自己的模块。 这解决了范围问题(我相信我可以从另一个模块导出模块,因此客户端只需要导入包装器模块),但现在让我为类似组(对话框)混合了组件和模块。

这些都没有真正的意义,并且随着应用程序的扩展,两者之间的冲突只会增加。

对我来说,唯一明智/明显/可维护的解决方案是使用模块导出顶级组件(simple-1、simple-2、complex-3),但让 complex-3 _component_ 定义它自己的内部组件。

/抄送: @robwormald

我一直在思考@kylecordes之前所说的关于使用现有的 Typescript 模块系统来定义粗粒代码集合,而不是添加新的 ngModules 东西。 我想提交一个示例以供考虑,它使用 Typescript 模块以一对一的方式定义 ngModules。 欢迎评论和想法。

https://github.com/jbalbes/autoNgModule

10901

我可能错过了一些东西,但是依赖注入与非延迟加载模块一起工作的方式可能会成为“大型项目”的一个大问题(我的不是,它已经在遭受痛苦)。

这很有趣。 我们已经在 Angular1 上有了一个企业应用程序。 我们正在使用 requirejs 为各种模块定义包(每个模块包含 10-20 个指令/服务/过滤器)。 我们将这些包捆绑到单个文件中用于生产(主要指令除外)。 指令在使用时会延迟加载到应用程序中(我们有一个核心指令可以延迟加载所需的指令)。

我们的应用程序路由页面模板位于 cms 应用程序中,cms 用户可以在不同页面模板之间拖放基于主指令的 UI 小部件(这就是为什么我们需要对指令的延迟加载支持以减少浏览器加载时的主脚本文件大小我们的大多数应用程序都基于基于指令的小部件)

Angular1 允许通过获取对注册提供者的引用来延迟加载指令到模块。
这种类型的延迟加载组件到模块并在 Angular2 中编译为 dom 是可能的吗?

我同意其他人的建议,即 ngModules 是寻找问题的解决方案。 在我看来,LazyLoading 的潜在好处并不能证明强制弃用组件指令/管道依赖项是合理的。 也许我在这里错了,但我觉得大多数应用程序不会真正看到延迟加载带来的任何实际好处,对我来说,这是 ngModules 提供的唯一真正好处。

也许 ngModules 正在尝试(嘿,也许它会成功)使开发结构/模式变得更好,但基于我到目前为止所看到和讨论的 API 的一点点,它正在朝着一个更复杂的方向发展遵循和维护。

另外,我们真的需要另一个模块系统吗?

没有人会质疑 NgModule 添加了一种我们以前不必处理的新事物。 我和任何人一样非常欣赏 RC5 之前的简单性,尤其是非常小的程序,已经丢失了。 我也非常怀念能够告诉人们 Angular 1 有自己的模块系统,但在 Angular 2 中我们只使用底层语言的模块系统。 哎呀。

然而,实际上有相当多的人,包括很多技术主管和大公司的其他决策者试图构建大的东西,他们对延迟加载和模板预编译非常感兴趣......这两者都被大幅启用由 NgModule 提供。

不过还有一件事。 我最不喜欢新 Angular 模块的部分是名称。 模块这个词,实在是太重了。 我昨天在这里为其他一些开发人员谈论这个,我们有点喜欢“包”这个词,它很好地捕捉了一个类似的想法,它没有一堆同名的竞争事物,至少没有在 JavaScript 生态系统中。

@kylecordes关于名称的问题https://github.com/angular/angular/issues/10087

那里提出的逻辑是否也会取消使用“模块”一词的资格,因为它与许多(如果不是更多)概念相冲突?

@Barrirowe我自己说得再好不过了——真的似乎大多数应用程序都不会从中受益——它最终会增加复杂性吗? 我希望不是。

不过我想知道,是否真的有必要让这个新的粗粒度模块系统与底层模块系统完全正交,或者是否有可能隐含地制作每个顶级目录以及其中包含的所有 Es6 模块包含一个粗粒度的 NgModule。 也许这种约定驱动的机制可以添加到当前机制之上,因此为愿意遵循这种约定的项目删除 NgModule 样板。

这是一个关键点。 如果您正确地构建应用程序,ESModules 非常适合构建应用程序。 例如,您可以轻松地在 API 的顶层轻松使用深度嵌套的导出,只需在更高级别重新导出即可。 我也赞成在一般框架中添加更多可选的约定。

我第一次看到 angular2 我想:'嘿,他们正朝着正确的方向前进,似乎他们从 angular 1 中学到了一些东西'。 您从 angular 1 中放弃了内部模块,而是使用了 ES 模块,也欢迎其他更改(组件、打字稿等)

但是我越是等待它变得稳定,它就越让我畏缩,因为破坏性的变化、缺乏文档和糟糕的错误消息。 现在你有了这个 NgModules 并且基本上撤消了自 angular 1 以来你改变的至少一件好事。

事实上,Angular 2 应该利用在开发 AngularJS 时不可用的丰富的新兴 Web 技术堆栈。 然而,尽管为 Angular 2 做出的许多设计决策的理由是“必须支持 Web 组件”,但具有讽刺意味的是,该框架与标准的差异越来越大。 现在,当标准很差时,有充分的理由偏离标准。

我觉得该框架没有充分利用其核心工具。 例如,TypeScript 声明并不完全是惯用的,并且充斥着anyany[] 。 他们还忽略了提高 API 可用性和可工具性的强大语言特性。 它们还包含可恶的东西,例如export declare type Type = Function

有趣的是,似乎有相当多的决定与 Dart 相关。 Dart 并不是大多数 Angular 用户关心的东西。 与 TypeScript 相比,它对什么是类型以及如何使用它们有非常不同的概念。 Dart <-> JavaScript 互操作通常是一个非入门者。

@aluanhaddad ,dart 实现最近被分叉到一个单独的项目中,所以希望你的一些顾虑能很快得到解决。

这种变化在整体 LOC 和组件依赖关系的心理建模方面都增加了很大程度的复杂性。 我的组件文件稍微干净一些,但以显式和就地依赖定义为代价,并添加了多余的 .module 文件。

减少样板文件听起来是件好事,但我认为这可能是过度的。 此外,似乎将这种特定的样板减少工作更好地描述为样板的运动,而不是彻底减少。

我认为 Angular 正朝着更难的复杂快速入门方向发展。 在工具链设置和文件列表以及必须理解的内容(如果要遵守最佳实践)方面已经有很多事情需要处理,即使是最简单的应用程序也在增长。

. @jbalbes谢谢,很高兴听到。

@radusuciu ,您是绝对正确的,这不是减少样板文件,而是将该样板文件转移到应用程序的另一个区域。 正如其他人已经说过的那样,组件级别范围和 NGModule 级别范围之间没有冲突。 我们需要的是粒度范围界定,我们可以选择哪些组件、指令、管道和服务应该可用,以根据其特定于应用程序的使用和语义来降低应用程序的撕裂

对我来说,模块为应用程序作者增加了更多可疑的实际价值的复杂性对我来说并不奇怪。 它们不是原始设计的一部分,似乎是后期添加的,只是因为其他东西碰壁了,如果没有另一个重大改变就无法工作(延迟加载、编译等)。

我不认为应用程序开发与“使其他承诺的功能工作”的所有影响在添加时都在议程上,但随着越来越多的应用程序转换为 RC5,它们开始变得更加清晰。

我什至还没有开始尝试将我的应用程序转换为模块,它可能会在 RC4 上死掉。 我知道很多部分需要完全重新设计,而我所做的所有努力就是更接近具有更大运行时包的下一个重大更改 RC,因为缩小,肯定是非常基本和基本的东西,被打破了. RC 应该会变得更加稳定,并且随着他们的进步而感觉更加完成,但事实并非如此。

Gang,NgModule 是一个令人恼火的问题,正如许多人雄辩地解释的那样。 但实际上这并不是什么大不了的事情。 在这里(Oasis Digital / Angular Boot Camp)我们已经更新了很多小东西,还有一些不那么小,到 RC5,并且在短时间内完成(经过长时间的学习和理解)。

每个人的应用都会受到不同的影响。 另外,我认为如果您专注于交付应用程序而不是销售 Angular 2 的培训和支持,那么您对 ​​Angular 2 的流失和改变的态度可能会有所不同。对我来说,升级到 RC5 的所有工作都会完全是白费力气——它实际上会带来负面的好处(更大的捆绑包),所以很难证明是合理的。

只是想我会在我们的平台上推动五天以达到 RC6* 的另一面给出我的想法(我们在夜间,RC6 在撰写本文时在技术上还没有下降)。 我觉得虽然我最初的评论仍然有效,但达到这个里程碑有一种如释重负的感觉。 最大的困难仍然是使用新的路由语法——太多的空path: ''规则! (我离题了)但这是我们可能会抽象掉的东西。

对于 nightlies 上的 NgModules,我不得不说错误消息越来越好,使整个过程更容易处理。 当组件和指令没有正确配置时,通常会调用它们并提供足够的信息,以至于您没有通过大量的 NgModule 依赖树来查找问题。 我们现在遇到的主要问题是没有明显错误消息的页面上缺少功能。 这通常是因为未正确声明或导入/导出组件。 它有点棘手,但通过教育它很容易处理。 测试现在是我们的下一个推动力,它完全死在水中。 但就像功能推送一样,我们最终会到达那里。

我们深吸了一口气,做了几天别的事情,然后把它砸了。 无论是什么,我们都在紧紧抓住下一次骑行! 祝所有受此影响的人好运。 如果能与 Angular 团队就此进行某种对话会很好,我们面临的最困难的问题是缺乏沟通。 我们仍然不知道 NgModules 上周的会议记录中的内容并没有提供任何见解。 让我们看看这周会发生什么!

@SaltyDH我认为这是有价值的反馈。
我认为关于沟通的抱怨并不恰当。

实施人员构建新版本,文档人员更新文档。
由于显而易见的原因,文档通常有些落后,因为它们只能在已经编码后进行记录。 编写文档也是一项艰巨且耗时的工作。

如果您在发布当天切换到下一个版本,甚至使用 nightlies,抱怨尚未传达所有内容有点不公平。
Angular 团队也无法提前知道在所有情况下可能会给用户带来困难的原因。

Angular 团队中总是有成员为 Gitter 和问题提供支持。
您还应该意识到沟通负担是相当不对称的,只有少数 Angular 团队成员与数千名开发人员。

我完全同意,这在主构建(几乎是 RC6)上更容易,最后一年以上的弃用终于消失了。

@kylecordes以何种方式在 master 上更容易使用?

NgModules 的设计文档(在编写设计文档时称为 AppModules)在 RC5 发布之前的一个多月前发布,我已经为它的到来做好了准备。
此外,由于 NgModule 是较新的概念,因此很难完全抓住它并将您的中型应用程序迁移到大型应用程序。
相反,我所做的是,首先完全理解 NgModules 的细微差别以及如何在 NgModules 中思考以设计 Angular 2 应用程序。 然后使用 NgModules 创建了一个自学应用程序。
第二步是 - 考虑如何在模块中分解当前应用程序,然后自上而下迁移它们。 从 AppModule 开始一次创建一个模块。 这真的很有帮助,最终的应用程序看起来更有条理。
image

是的,甚至在 RC5 发布之前,Gitter 就在 NgModule 上提供了帮助。 发布 RC5 后,我们有文档和博客可供我们更好地学习。

@zoechi没有过多地破坏这个讨论,我觉得我必须澄清我的评论。 我根本没有提到文档,我的大部分理解来自于在 GitHub 中审查提交和问题,我对此很好。 我的沮丧来自于在这个问题上缺乏代表性,我们有 61 条评论和计数。 一个简单的,“我们知道每个人在这个问题上的担忧,我们真的相信这是 Angular 2 的最佳方法,我们认识到它会带来的困难,但它对产品的未来更好”。 Gitter 很棒,但个人信息经常在试图理解 Angular 2 不同方面的人海中迷失。我确实通过这种媒体接触过,但由于时区差异或其他优先事项,我无法获得权威反馈可以用来做出明智的决定。 有帮助的是角度播客中的冒险https://devchat.tv/adv-in-angular/106-aia-angular2-rc5-and-beyond。 这让我有信心继续升级 NgModule。

一切顺利。

感谢大家的反馈,很抱歉花了这么长时间才得到回复——我们上周一直很忙(将内部 Google 应用程序迁移到 NgModules,所以我们也感受到了重构的痛苦)

让我看看我是否可以澄清这里的一些问题和误解。

关于@NgModule()(以及@Component和任何其他Angukar 装饰器)首先要了解的是它们是纯粹的编译时构造——它们的存在是为了允许角度编译器在应用程序中发现依赖关系图。

我们的装饰器所做的(简化)版本如下所示:

//simplified Component decorator
export function Component(componentConfig){
  return function(componentClass){
    Reflect.defineMetadata('annotations', componentConfig, componentClass);
  }
}

它们不会以任何方式改变或修改装饰类的行为——它们只是附加一些元数据。 Angular 使用该元数据来构建您的应用程序并编译模板。

在 JiT 模式下,这发生在“及时”——在你调用 bootstrapModule 和你的第一个组件渲染之间,Angular 的编译器使用 Reflect API 检索附加到类的元数据:

let metadata = Reflect.getOwnMetadata('annotations', componentClass);

然而,在 AoT 模式下,这有点不同——在构建时,我们_静态地_(即,不执行您的代码)通过扫描装饰器从源代码中提取相同的元数据。

当您引导单个组件时,这可以正常工作,但是我们已经从正在做更复杂事情的开发人员那里听到了很多反馈 - 引导多个根组件,或基于身份验证状态引导不同的组件等。

因此,虽然@Component装饰器使我们能够静态分析组件,但我们没有能力可靠地静态分析 _Application_

属于“应用程序”的东西

  • PLATFORM_DIRECTIVES/管道/提供者
  • 你之前添加到 bootstrap() 的东西
  • 编译器级别设置
  • 多个根组件
  • 服务器端使用。

NgModules 引入了一组静态可分析特性的概念。 有趣的是,在 AoT 编译模式下,我们分析您的根模块,并为应用程序中的每个模块_生成_一个 ModuleFactory - 这是模块的预编译版本,它包含_仅_您在模板中静态引用的工厂以及您标记为“entryComponents”

因为我们已经提前提取了编译所需的信息,所以我们实际上可以对装饰器进行 tree-shake (ngc 将自动处理最终处理) - 而不是从根模块开始捆绑您的应用程序,您开始在您生成的根 Module_Factory_ 中,它仅包含应用程序中实际使用的代码,因此您无需为模块化付出代价,并且 rollup 和 webpack2 等工具可以_更_高效地工作

更多内容在下一个回复中...

@robwormald我无法在表情符号中表达对这种背景信息的欣赏程度。

谢谢!

我们遇到的另一个问题 NgModules 解决了:

考虑一个您想要构建 DialogService 或类似的情况。

从历史上看,您会执行以下操作:

@Injectable()
export class MyDialogService {
  //inject the dynamic compiler 
  constructor(private componentResolver:ComponentResolver){}

  //accept a component and a viewContainerRef
  showDialog(component:Type, target:ViewContainerRef){
    //compile the component into a factory, async
    return this.componentResolver.resolveComponent(component)
      .then(componentFactory => {
         //dynamically insert the compiled componentFactory into the view:
        return target.createComponent(componentFactory);
      })
  }
}

...您会使用

myDialogService.showDialog(MyRandomDialogComponent).then(...)

这里要注意的事情——

  • JiT 模式下的组件编译_必须_是异步的,因为外部 templateUrls 和 styleUrls
  • viewContainer 接受 _componentFactory_ - 所以现在您的代码有一个问题:您要么必须重写代码以切换模式(在 JiT 和 AoT 之间),_要么_ 您必须始终假设组件插入 API 是异步的。 在对话框的情况下这可能没问题,但是如果您正在动态构建复杂的 UI(想想仪表板或网格视图或其他),您会招致大量不必要的 Promise 调度。
  • 第 3 方或共享服务/组件(如 SharedDialogService)有同样的问题,必须接受 _either_ Components 或 ComponentFactories

对于进行动态组件插入(不一定意味着延迟加载)的 _any_ 应用程序,会出现此问题 - 对话框、路由器等都希望与 ComponentTypes(类)而不是选择器(foo-bar)进行交互。

因此,当您在 NgModule 装饰器中将 Component 添加到 entryComponents 数组时:

@NgModule({
  declarations: [ MyRandomDialogComponent ],
  entryComponents: [ MyRandomDialogComponent ]  
})
export class MyApp {}

这告诉编译器“为我生成 MyRandomDialogComponent 与其编译的 MyRandomDialogComponentNgFactory 之间的映射”,并_将其存储在声明它的 NgModuleFactory 中_

因此,上述 dialogService 的 NgModule 驱动版本如下所示:

@Injectable()
export class MyDialogService {
  //inject the component factory resolver 
  constructor(private componentFactoryResolver:ComponentFactoryResolver){}

  //accept a component and a viewContainerRef
  showDialog(component:Type, target:ViewContainerRef){
    //*retrieve* the componentFactory by component, sync
   let componentFactory = this.componentFactoryResolver.resolveComponentFactory(component)
   //add the componentFactory to the view, sync
   return target.createComponent(componentFactory);
  }
}

现在,您添加到 entryModules 的任何组件都可以同步检索并动态插入到视图中,并且您的代码_不必_必须根据您所处的模式进行更改,第三方/共享库也不必担心编译逻辑。

谢谢你的解释@robwormald

一个问题是:这是否需要删除指令/管道声明? 它们不能与 NgModule 共存有什么原因吗?

我觉得人们,包括我自己,不是对 NgModule 的“新手”或对它的任何误解感到沮丧,而是对许多人似乎“运作良好”的“旧方式”正在强行移除,尤其是在这个 RC 后期阶段。 我认为这是与对 NgModule 本身的任何阻力的重要区别,我还没有看到太多,还没有直接解决。

我还看到删除指令/管道声明的拉取请求已启动(https://github.com/angular/angular/pull/10912) - 我希望我们能在设置任何内容之前就这一点得到回应结石。

提前致谢。 毫无疑问,与 Angular 一起工作很愉快,我非常感谢团队在过去一年中的辛勤工作+

这里有一些关于“全局范围”的评论——这误解了编译器和模块的机制(这是完全可以理解的,这是复杂的东西)。

将整个应用程序转储到单个 NgModule 中是“很好”的,就像在 Angular1 中将整个应用程序的状态转储到 $rootScope 是“很好”一样(阅读:它可以工作,但它的应用程序设计很糟糕)

在 Angular 1 中,将模块引入应用程序或多或少地“污染”了整个应用程序,因为所有东西都被倾倒在一个注入器包中。

在 Angular2 中,NgModues 有一些非常强大的作用域机制,可以在没有污染的情况下进行组合。 再次,它回到编译器。

当 Angular 遍历和编译你的应用程序时,它遇到的每个组件都_在它被声明的上下文中编译_

例如 - 假设您有一个 SharedModule,其中包含一些您想在应用程序中共享的功能

@Component({
  selector: 'shared-component-one',
  template: `
    <div>Shared Component One</div>
    <shared-component-two>
  `
})
export class SharedComponentOne {}

@Component({
  selector: 'shared-component-two',
  template: `
    <div>Shared Component Two</div>
  `
})
export class SharedComponentTwo {}

@NgModule({
  declarations: [ SharedComponentOne, SharedComponentTwo ],
  exports: [ SharedComponentOne ]
})
export class SharedModule {}

SharedComponentOne 和 SharedComponentTwo 都在 SharedModule 中_declared_ - 声明意味着_ownership_ - 所以这两个组件都归 SharedModule 所有。 但是,_only_ SharedComponentOne 是从模块中_exported_ 的 - SharedComponentTwo 仍然是_private_ 到 SharedModule。

如果您要引入SharedModule并在另一个模块中使用它,如下所示:

@Component({
  selector: 'some-component',
  template: `
    <div>hello from some component</div>
    <shared-component-one></shared-component-one>
  `
})
export class SomeComponent {}

@NgModule({
  imports: [ SharedModule ],
  declarations: [ SomeComponent ]
})
export class SomeModule {}

...当编译器开始编译SomeComponent时,它会发现shared-component-one选择器,并且因为SharedModule (导出SharedComponentOne )被导入到SomeModule ,它知道 shared-component-one === SharedComponentOne。

有趣的是,当编译器实际编译 SharedComponentOne 时,它​​是在“内部”SharedModule 中编译的,这允许它使用 SharedModule 中不暴露给外部世界的东西。

这与它过去使用 Component.directives 的方式非常相似,在这种情况下它们是相等的。

考虑一下,如果你有类似标签功能的东西:

@Component({
  selector: 'my-tabs',
  template: '...'
})
export class TabsComponent {}

@Component({
  selector: 'my-tab',
  template: '...'
})
export class TabComponent {}

这两个组件都是顶级的,没有可依赖的层次关系。 这些导致了一种人们在做的事情的爆炸式增长

export const TAB_DIRECTIVES = [ TabsComponent, TabComponent ]

更复杂的选项卡功能可能具有管理多个选项卡实例的服务,因此您还必须处理这种情况......

export const TAB_PROVIDERS = [ ... ]

并使用它成为一种练习,以记住您必须导出和导入的所有内容......

import {TAB_DIRECTIVES, TAB_PROVIDERS}

然后在您想要使用它的正确位置提供所有这些东西_everywhere_。 在这一点上也很容易忘记导入功能所需的所有东西并最终导致奇怪的静默失败,或者在没有所有必需上下文的情况下编译组件。

使用 NgModule,你可以简单地将它们打包成一个单元,然后在你的应用程序中传递它,然后在相关的地方导入它。

对于像 Material Design 这样的库,它可能具有分布在多个模块中的许多特性,您可以利用相同的导入/导出语义使其更加可移植:

@NgModule({
  exports: [ TabsModule, NavbarModule ]
})
export class MaterialSharedModule {}

拉出该单个模块允许您的所有应用程序使用组件,同样,在 AoT 时间,只有您使用的那些将被导入到生成的代码中。

至于为什么我们删除了 Component.directives / 管道,我们已经听到了很多来自两个方向的反馈。 我们觉得有两种方法来完成同一件事通常是一种糟糕的模式,所以我们选择让人们通常可以编写更少的代码,而那些想要以前由 Component.directives 提供的显式作用域的人可以通过以下方式完成_same_功能范围在模块级别。

在 RC5 中,我们将 Component.directives 自动提升到“全局”范围内。 这是一种平滑过渡的尝试,虽然它对大多数人都有效,但混合的新旧行为会导致一些奇怪的错误和行为,虽然令人沮丧,但这些行为是暂时的。 这在 RC6 中消失了(并且错误消息已得到改进)

至于为什么在比赛后期发生了这种变化——我们只能道歉。 我们比你们任何人都更不喜欢在最后一刻做出改变。 归根结底,我们实际上是在以一种以前在前端领域从未做过的方式构建一个框架,因此我们会遇到意想不到的问题和设计问题。 这是其中之一,在我们做出决定之前,核心团队之间进行了激烈的辩论和调查。 再次,我们很抱歉给您带来麻烦,但我们相信最终结果是构建_应用程序_的更好方式,这就是我们都在这里的原因。

谢谢大家的耐心。 如果还有其他我没有在这里讨论的问题,我会保持开放状态。

干得好, @robwormald

我想补充几点意见:

提供者

Rob 专注于 _declarations_、_imports_ 和 _exports_。 模块_Providers_ 是不同的。

@Component.providers活着! 只有@Component.directives@Component.pipes会消失。

您可以同时使用@NgModules.providers@Component.providers 。 他们有不同的目的:

a) @NgModules.providers通过将提供程序添加到“主”注入器(用于急切加载模块的应用程序根注入器)来扩展应用程序。 这就是RouterModule向您的应用程序添加路由服务的方式,而无需您提供它。

b) @Component.providers _encapsulates_ 在组件实例(及其组件子树)范围内提供服务。

两种方法都满足不同的需求。

我的一般建议:_如果您今天在@Component上有提供者,请将其留在那里_

功能模块

它们是组织应用程序的有效方式。 Angular 模块一章有一小节介绍了从单体 Angular 模块到功能模块的重构。 这很容易(至少我已经做过几次了。)。

我在我的应用程序中寻找自然接缝。 我不发疯。 我不会模块化每个组件。

Material Design 似乎这样做的原因是他们试图让我们更容易以细粒度的方式使用我们想要的东西。

他们将有一个厨房水槽模块,可以简单地重新导出所有东西。 你可以只导入它并完成。 但如果我正在调整我自己的应用程序,我会创建一个类似的_重新导出模块_,它只导出我想要的 MD 部分。

这是任何图书馆供应商都可以遵循的模式,并且在我们将自己的公司批准的已批准小工具“套件”放在一起的企业中可能有意义。

发现声明依赖

编译器现在可以更好地告诉您什么时候丢失了。 如果您遵循推荐的做法并且_总是、总是、总是连字符_您的组件选择器名称,编译器会在您有一个未声明的组件时告诉您。 它还拾取未声明/无法识别的数据绑定和指令。

从 pre-RC5 迁移确实会增强失落感。 我们必须完成在之前的directivespipes列表中发现依赖项的繁琐工作。 我们必须消除重复数据。 这是生活在边缘的基本不愉快。

RC不应该改变这么多

对。 你不会得到团队的争论。 这不是故意的。 如果我们知道我们需要NgModule ,我们就会在测试版中拥有它。

但是 IMO,最好交付正确的产品,而不是盲目地遵循 RC 应该是什么的一些概念并交付错误的产品。 另外……说起来也不太舒服……但是如果您最近一直在关注其他一些主要的软件公司,您可能已经注意到“候选版本”的同样灵活的概念。 出于某种原因,这就是我们行业的发展方式。

我现在已经转换了许多应用程序并帮助其他人这样做。 一旦经过“_他们(再次)移动我的奶酪_”阶段,这是一个非常机械的迁移,每个人似乎都认为他们处于一个更好的位置。 也许他们只是对我很好。 OTOH,我不和那些很好的人一起出去玩 ;-)

(也,回复:名字)
我们把名字写死了。 它们最初被称为 AppModules(因为它们描述了比组件更高级别的东西,即“应用程序”),但这不是一个足够广泛的术语。 包、捆、桶、球等。

请记住,它不是 ES 模块的替代品 - 它是一种增强功能,可以让 Tree Shaking 和 ES 模块更好地为所有 Angular 开发人员工作,并且在语义上,它们类似于 ES 模块的工作方式(导入、导出、声明)

所以,起名很难。 NgModule 是。

我们已经为 2.0.0 提供了完整的 API,因此我们将在发布后的几个月内观察以了解发展出哪些模式,以及我们可以在哪些方面为 2.1 加糖和做出更好的推断。

衷心感谢@robwormald @wardbell非常有见地的评论。 我认为对我来说最大的解脱是

“我们已经为 2.0.0 提供了完整的 API,因此我们将在发布后的几个月内密切关注,看看会发展出什么样的模式,以及我们可以在哪些方面改进并为 2.1 做出更好的推断。”

几周前我们已经听说过它,但现在它专门针对 NgModules 以及所有这些反馈。 这是我需要去董事会说的信心。 我们已经完成了,是时候完成构建我们的应用程序了。 我还要祝贺整个 ng2 团队和社区达到这个里程碑! 激动人心的时刻即将到来。

老实说,我相信你可以这么说@SaltyDH。 Angular 2 的流失已经结束。

这并不意味着 Angular 2 已经完成进化。 将来会有版本。 但是导致 2.0 的流失……已经……结束了!

非常感谢你们的诚实和明确的用例,伙计们。 这真的很有帮助。

快速思考一下 NgModule 的 API 中的命名和重复,我觉得有点麻烦。

海事组织这个:

@NgModule({
  declarations: [ SharedComponentOne, SharedComponentTwo ],
  exports: [ SharedComponentOne ]
})
export class SharedModule {}

...可能更清晰,更简洁:

@NgModule({
  private: [ SharedComponentTwo ],
  public: [ SharedComponentOne ]
})
export class SharedModule {}

1)WRT到命名(公共和私有与声明和导出),本质上是上面的@robwormald ,我相信很多其他人都在解释它

2)(忽略命名)为什么需要重复SharedComponentOne ? 当然,您可以说如果它是“出口”,则必须是“声明”,因此可以这样去糖吗?

只是我在高度主观的事情上的两分钱😄 - 再次感谢您的详细解释!

@robwormald @wardbell感谢您的详细解释。

正如我之前在这个帖子中所说的, NgModules确实帮助我们更好地组织起来。 最近的一个例子是为模板驱动的表单创建验证器指令。 在 RC5 之前,我们必须在我们创建表单的组件上导入每个指令。 现在我们只需将它打包在VaildatorModule中并在需要的地方导入模块。 我们稍后添加的任何验证器都会自动用于我导入ValidatorModule的模块。 我只需要更新模板而不用担心依赖关系。

@JamesHenry declarationsimportsexports用于使 NgModules 与 ES6 模块保持一致,就像我们一样importexportdeclare ES6 模块中的东西。
我同意加糖,出口的东西可以神奇地自动脱糖成声明。

@詹姆斯亨利

1),我们坚持使用导入/导出,因为心智模型更接近 es 模块的工作方式(至少在范围方面) - 您在模块中声明事物,从其他模块导入事物,并导出事物以使其可用于其他。

2) if it is an "export" it must be a declaration, so it could just be desugared that way? -> 在您使用 ngModule 重新导出之前有效,如上面的 MaterialModule 示例 - 在很多情况下,您可以使用 ngModule 重新导出在另一个模块中声明的内容,并且然后推论就分崩离析了。

@robwormald很棒的评论! 绝对值得一篇博文。

我现在有一个使用@NgModule对话框库,我可以说我看到用户在尝试添加自定义组件时遇到困难,因为他们忘记在entryComponents中注册它们,这显然不够清楚,但是可以理解,因为它是一项高级功能...

我相信它会随着时间的推移而沉没

@shlomiassaf谢谢!

对于像您这样的情况,还应该提到:

请注意,当您使用 Angular 的路由器时,您将组件添加到声明和路由配置中,但_not_ 到 entryComponents,尽管事实上路由器是 entryComponents 的定义。 (记住 - entryComponent === 你想通过类引用的东西,而不是选择器)

任何库都可以利用一个很酷的技巧 - 有一个名为ANALYZE_FOR_ENTRY_COMPONENTS的神奇令牌 - 请参阅https://github.com/angular/angular/blob/master/modules/%40angular/router/src/ router_module.ts#L117了解路由器如何使用它。

因此,对于处理动态插入组件的库,您可以执行以下操作:

@NgModule({
  providers: [ DialogService ]
})
export class DialogModule {
  static withComponents(componentList): NgModuleWithProviders {
    return {
      ngModule: DialogModule,
      providers : [
         { provide: ANALYZE_FOR_ENTRY_COMPONENTS, useValue: componentList, multi: true }
      ]
     }
  }
}

像这样使用

@NgModule({
  declarations: [ MyConfirmDialog, MyQuestionDialog ],
  imports: [
    DialogModule.withComponents([ MyConfirmDialog, MyQuestionDialog ])
  ]
})
export class MyAppModule {}

可能适用于您的用例,可能不会。

@JamesHenry非常重要的是declarations不会与private混淆。 你是说_这个模块声明了这个组件_。 您没有就公共或私人发表任何声明。 您正在声明所有权。

这真的很像 ES6 模块中发生的事情。 您在文件中定义的任何内容都“属于”该文件定义的模块。 它是否公开取决于您对关键字export的使用。

与 ES6 模块一样,您可以重新导出您导入的内容。

我喜欢将declarations视为“技巧”,它使我不必将所有组件、指令和管道物理放置在同一个物理文件中(如果您愿意,您将不得不使用 ES6 模块所有这些类都属于同一个 ES6 模块)。

所以,对我来说, declarations可以替代将这些文件粘贴到一个可怕的大文件中。 无论如何,那是_我的_心智模型。

需要注意的一点是模块上下文中提供程序和组件之间的逻辑差异。

提供者和组件在同一个地方声明( NgModuleMetadataType ),因此开发人员可能直观地认为提供者的行为类似于组件......这认为模块中的提供者将导致该模块的实例...... .

由于提供者由 DI 管理,这当然不是真的,它们实际上是应用程序级别的。
如果不导出模块中的组件是私有的,这可能会导致混淆。

我喜欢模块的概念,IMO API 中唯一的问题是在模块内的同一位置声明提供程序和组件......对于新手来说这是一件很难掌握的事情。

@wardbell感谢沃德,这真的很棒! 只是为了澄清一下,因为这刚刚在讨论中出现,当我们说 Angular 2 是 API 完整时,我们在这里讨论的是哪些命名空间? @角/???

@robwormald谢谢! 一个很好的提示!
一颗糖味的! 将实施。

我不是谷歌员工,也许谷歌员工不能说。 我只能用我自己的两只眼睛来报告我所看到的: @angular/库集合中的一个非常严重的 API 冻结。

有一些好的想法被搁置,因为它们不够好或不够深入,不足以证明持有发布的合理性。 应该是这样的。 好主意永远不会停止。 但是是时候说_这是你的Angular 2.0_了。

我们承诺从现在到“最终”之间移除已弃用的 API。 这有一些调整......任何人都可以通过查看大师看到。 我觉得我们已经完成了。

@wardbell我赞同你对出口的看法。
index.ts 曾经充满

export * from 'a.component'
export * from 'b.component'
export * from 'c.component'
export * from 'p.directive'
export * from 'x.service'
export * from 'z.pipe'

现在减少到只有export * from my.module
其余所有东西都清理干净位于 NgModule 导出中
exports: [ AComponent, BComponent, CComponent, PDirective ]等等

@wardbell我认为@NgModules.providers应该是@NgModules.rootProviders@NgModules.appProviders

我觉得它清楚地描述了提供者的背景。

@shlomiassaf那会误导。 @NgModules.providers对于急切和懒惰的模块的效果是不同的。 延迟加载的模块有自己的子注入器,这意味着_它们的提供者_被添加到_child_注入器,而不是_root_注入器。 所以如果一个懒加载一个懒加载一个懒加载。

也许本来可以有一个不同的名字。 你知道那是怎么回事。 但是由于我刚才给出的原因, rootProviders不会是一种改进。

@wardbell同意,没有考虑过这种情况。

只是想增加一种情绪,即非常感谢彻底的答复和沟通。 如果这确实是从 rc 到决赛的成长阵痛中的最后一个,那么我们就开始比赛了。 谢谢!

正如其他人所说,感谢团队的反馈。 然而,在这个开发阶段的这种变化暗示了基本的设计错误。
老实说,我们都犯过这些错误,所以评判没有价值,但是当我犯错时,它会让我感到谦卑,并希望让我随后更加谨慎。 我不认为它对 Anagular 团队有同样的影响。

我喜欢将声明视为一种“技巧”,它使我不必将所有组件、指令和管道物理放置在同一个物理文件中(如果您打算将所有这些类都用于属于同一个 ES6 模块)。

所以,对我来说,声明可以替代将这些文件粘贴到一个可怕的大文件中。 无论如何,这就是我的心理模型。

@wardbell也许我遗漏了一些基本的东西,但我看不出使用NgModule.declarations与导入所有 ESModules 有什么根本不同,而只是重新导出一些 ESModules。 具有讽刺意味的是,ESModules 的主要限制是它们只提供物理模块的概念,而不是逻辑模块。 我看不出NgModule如何改进这一点。 它只是一种不同的聚合语法,但并没有将抽象级别提高到有意义的程度。

此外,NgModules 完全无法解决一个主要问题:

整个Function[]模式在所有错误的方面都是不透明的。 它将可发现性降至零。 必须阅读源代码以确定提供程序数组中的内容。 NgModule.exports也是如此。

但是 IMO,最好交付正确的产品,而不是盲目地遵循 RC 应该是什么的一些概念并交付错误的产品。 另外……说起来也不太舒服……但是如果您最近一直在关注其他一些主要的软件公司,您可能已经注意到“候选版本”的同样灵活的概念。 出于某种原因,这就是我们行业的发展方式。

@wardbell对我来说,对 RC 的最大挫败感是,因为 ng-conf 和 google i/o,Beta 版刚刚被推送到 RC。 为了取得进步,应该明确原因:它是_营销_,作为技术人员,我们应该与这些灵活的成熟概念的趋势作斗争。 您可能知道山谷中一家受欢迎的汽车制造商,那里的讨论是关于出售产品的测试版是否正确,因为它可能会夺走生命。 我不想夸大其词,但作为技术专家,我们必须大声疾呼,如果这就是我们行业的发展方向,因为那样它就会走向错误的方向。

我喜欢 ngmodule 方法,但我有一个问题@robwormald

我有一个共享服务,它没有组件、指令和管道。 它只有服务( forRoot )并包含所有业务模型类。 app.module进口ServerApiModule.forRoot()因此服务可用于整个应用程序。 使用服务和业务模型类的特性模块是否应该导入这个共享模块? 从技术上讲,这不是必需的(没有错误),但从语义角度来看,它是有意义的(也没有错误)。 我们应该如何处理这种模块? 是否导入它们? 我个人喜欢第二种可能性,因为它说“嘿,我是一个功能模块,我需要这个服务,我需要这个业务模型类”。

感谢您的回答!

所以我可以感受到库/模块化应用程序库和 ngModule 的痛苦。

但正如多次所说,不要混合两种可能的方式。 那是并且将会是令人困惑的。

所以正确的方法是为每个组件声明一个 ngModule。
但是由于这使得它对每个组件都有更多的锅炉编码器,因此这种方式也不适用于大规模。

所以不要添加一个名为 ComponentModule 的新装饰器,它或多或少是糖,以避免像材料一样声明组件和模块。

往这边走? 如果我看到一个组件。 我知道它必须是 ngModule 的一部分。 如果我有很多组件将它们捆绑在一起的情况,我可以使用 NgModule。 如果我只想要一个像“旧组件”这样的独立组件,我会使用 ComponentModule 并且我知道所有依赖项,它可以是其他模块的模块依赖项,但不是它们的一部分

@nathraQ如果正确的方法是对每个组件使用 NgModule,而且很可能是这样,那么 NgModule 不应该被引入,而 Component 应该刚刚得到增强。 关于样板,Angular 2 对它的要求太高了,以至于在这一点上它几乎无关紧要。

许多没有自己的模块系统的框架已经非常成功地使用约定来为结构的布局和可见性建立标准模式。 一位智者曾经说过,你不能在事后定义约定; 他们需要从一开始就在那里。 我对这种说法持怀疑态度,但这次失败证明他是对的。

@aluanhaddad抱歉,我不同意。 我在 angular 1.x 中只有一个模块的用例,带有自定义依赖管理(比如这个线程的启动器),但我也有模块的用例,用于捆绑一组控制器、指令和服务。

两者都很有用。 现在我们必须弄清楚如何将它整合为每个人都可以理解的

哇,读得很长:微笑:

我可以看到新更改的利弊,但它确实让我想到了我在 Angular 1 中遇到问题的一个场景。

@wardbell @robwormald -

如果我使用 SharedModule 模式,并且我导入了两个第 3 方库(ui-bootsrap 与 angular-strap 有人吗?),它们都对组件使用相同的选择器,比如说my-selectbox

如果我尝试将它们都导入,我会收到错误消息吗? 对?

我在这里阅读的一种解决方案是重新导出自定义模块中的一个库
但这意味着每次库升级时我都需要不断更新包装模块,不是吗?

此外,我可能有自己的同名组件选择器(并且在可能不止一次发生的大型项目中)

这个问题有推荐的解决方案吗? 还是“包装模块”?
(我希望我们不会重新使用选择器前缀,这很不好玩)

提前致谢!

无论您的项目有多大,命名空间或某些人所说的前缀通常都是一个好主意。 是的,在较小的项目中它可能更微不足道,但是一旦涉及第三方模块,我会说这几乎是一个要求 - 至少它可以让您高枕无忧,即使其他模块得到更新也不会发生冲突,但开发人员也可以轻松识别每个模块属于什么,而不必遵循依赖树。

@nathraQ所有这些模式都可以通过 ECMAScript 模块实现。 NgModule的引入让我们无处可去,而像 AngularMaterial 这样的库正在将组件转换为 NgModules 只是为了保留先前提供的封装这一事实证明了这一点。

@aluanhaddad NgModules 正在尝试做一些与 ES 模块非常不同的事情,并且也没有替换组件封装,即 ES 模块和组件都没有改变它们的核心职责。 NgModules 是您在整个应用程序中描述架构的媒介,这有助于 Angular 更好地理解您的意图并进行相应的优化,允许预先编译应用程序的某些部分、从服务器提供动态解析的模块等等。其中无法通过 ES 模块实现。 出于同样的原因,每个组件一个 NgModule 并不是要遵循的经验法则。

确实,更简单的应用程序可能不会从这些好处中受益那么多,但在这些情况下,NgModules 无论如何都应该不那么“烦人”。

@emilio-martinez,正如@aluanhaddad所写,ES6 和“旧的 Angular 2 方式”为我们提供了所需的命名空间。

我曾使用 Angular 1 处理过非常大的项目,如果它是一个大型项目(即使在 5 个以上的开发人员团队中),如果它是一个名为mb-product-list的选择器,它可能会很快与其他人发生冲突。

试图用更多的命名空间来解决它,你最终会得到: mb-somefeature-product-list这使得模板看起来很脏。

由于directives元数据,我很高兴看到它在 ng2 中得到了解决。
你可以 npm install 任何你喜欢的包,并且只从每个组件中导入你需要的东西。

ngModules 肯定有它的好处,它有助于异步加载块,还可以帮助我们减少导入并提高生产力。

但是使用SharedModule模式引入了一个全局命名空间,就像我们在 ng1 中所做的那样。

至少对于提供程序,它是由 ES6 令牌(文件位置)命名的。

对于组件,由于选择器,它会尖叫有两个组件具有相同的选择器。

我希望我们有一些简单的方法来为与 3rd 方或本地共享组件发生冲突的用例配置一个特殊的命名空间。

诸如“选择器覆盖”之类的东西。

这就是我要问的

感谢@robwormald提供的详细见解,但像我这样的新手(大多数情况下,我是后端开发人员)有一件事。 我正在学习 Angular 2 和打字稿。 所以 ES6 模块概念对我来说有点模糊
我们有一个复杂的应用程序结构。 它是一个分析应用程序,我的组件数接近 60 个。
而且我认为它在 RC4 下的结构很好。 除了 Login 我们没有使用任何类型的路由,因为 routern 重新创建了整个组件。 所以我们计划作为基于选项卡的应用程序。有两个主要选项卡(1.分析 2.仪表板)。
分析选项卡将有多个选项卡,每个选项卡下都有一个分析。 仪表板也将有
多个选项卡,但每个选项卡将包含多个分析,这些分析保存在
分析部分。 所以从多个选项卡(在仪表板和分析下)来回切换
以及在仪表板选项卡和分析选项卡之间切换,我们觉得路由不会服务
我们的目的(如果我说一些愚蠢的话,请纠正我)。
现在 RC5 NgModule 破坏了我们的应用程序。 我们真的不知道该怎么做
重新设计我们的应用程序。 我们真的可以在我们的应用程序中使用 AoT 编译吗? 不是吗
整个 AoT 都是基于路由的?

@shairez NgModules 正是提供了这种命名空间。 让我尝试进一步澄清这一点......

这里要实现的关键是一个组件或多或少是在它声明的模块“内部”编译的 - 参见https://plnkr.co/edit/9w10b1Y8Bjr5DDIxOwnC?p=preview示例。

请注意,有两个冲突的选择器( my-generic-selector ) - 每个功能模块都导入其中一个,能够在该模块内部使用它,而不会污染任何“全局”命名空间。

所以像

<my-app>
  <!-- belongs to FeatureModuleOne -->
  <feature-one></feature-one>
  <!-- belongs to FeatureModuleTwo -->
  <feature-two></feature-two>
</my-app>

扩展到

<my-app>
  <!-- belongs to FeatureModuleOne -->
  <feature-one>
    <!-- the generic imported in FeatureModuleOne -->
     <my-generic-selector></my-generic-selector>
  </feature-one>
  <!-- belongs to FeatureModuleTwo -->
  <feature-two>
    <!-- the generic imported in FeatureModuleTwo -->
    <my-generic-selector></my-generic-selector>
  </feature-two>
</my-app>

没有任何冲突,因为当编译器编译功能一和功能二时,它会在它们所属模块的上下文中这样做,从而避免污染全局范围。

@robwormald当然,这很棒,在这个意义上比 Angular 1 更好。

我写的用例是指使用docs中建议的全局 SharedModule 的模式。

如果我尝试在 SharedModule 中同时声明GenericSelectorFeatureOneGenericSelectorFeatureTwo SharedModule ,我会得到一个错误,对吧?

或者如果SharedModule有一大堆有用的通用组件,我想将它导入到每个功能模块中。

如果我的功能模块有一个碰撞选择器,或者某些第 3 方有一个碰撞选择器与SharedModule中导出的现有库之一,它会发出错误,对吗?

@shairez我认为大多数应用程序最终都会有许多“SharedModules”,并且发生冲突的可能性很低 - 模块很便宜,因此拥有很多模块的成本并不高。

如果您对如何改进文档有任何想法,那么我们很乐意欢迎 PR。

谢谢,这是我一直在寻找的答案,我会测试一下。

我对如何用新的模块编写方式解决这个问题还没有更好的想法,这就是我在这里问它的原因,但是一旦我测试了多个共享模块解决方案,我将有更多的材料与@讨论它

感谢@robwormald的帮助!

https://angular.io上提供的最新文档中,如果NgModule的组合不正确,则很容易出现关于反模式和错误的 _many_ 警告。 例如

不要在共享模块中指定应用程序范围的单例提供程序。 导入该共享模块的延迟加载模块将制作自己的服务副本。

这是相当令人不安的。 如果您遵循_首先使其工作,然后使其快速_的久经考验的真正方法,您可能需要根据经验指标以及随着应用程序或库的扩展这些模块的紧急结构来调整哪些模块是急切或延迟加载的。 基本上,一个模块为什么要关心它是延迟加载还是急切加载?

另一个看似更严重的问题来自并列文档的以下部分

假设一个模块需要一个自定义的 HttpBackend,它为所有 Http 请求添加一个特殊的标头。 如果应用程序其他地方的另一个模块也自定义了 HttpBackend 或仅导入 HttpModule,它可能会覆盖此模块的 HttpBackend 提供程序,从而丢失特殊标头。 服务器将拒绝来自该模块的 http 请求。
通过仅在应用程序根模块 AppModule 中导入 HttpModule 来避免此问题。

我可以重新导出类和模块吗?
绝对地!
模块是一种从其他模块中选择性地聚合类并将它们重新导出到一个统一的、方便的模块中的好方法。
一个模块可以重新导出整个模块,从而有效地重新导出所有导出的类。 Angular 自己的 BrowserModule 导出几个模块,如下所示:
出口:[CommonModule, ApplicationModule]
一个模块可以导出它自己的声明、选定的导入类和导入的模块的组合。

因此,一方面,文档不鼓励用户重新导出某些模块,同时指出这样做提供了很大的便利。

NgModule re-exports 的概念类似于 ES Module re-exports 的概念,只是您的控制较少。 JavaScript 是静态作用域的(是的,我知道this不是),这是它最大的优势之一,允许极其灵活的组合和信息隐藏。 JavaScript 还支持遮蔽,因此您总是有办法覆盖范围。

但最大的问题是,指南中的示例都围绕如何导入 @angular/* 框架模块。 这些自然而然地以允许直观地防止冲突的方式进行划分。 这是因为它们的存在是为了提供通常不重叠的基础设施级服务。 用户定义的模块可以跨越许多层,并且可能想要增强或改变各种行为。 例如,日志模块可能希望访问路由器和 Http 服务,同时提供多个组件用于向管理用户显示信息,并使用其中一个表单模块来定义它们。

文档没有说明重新导出是否具有传递性...模块 A 重新导出 CommonModule。 模块 B 重新导出模块 A。模块 C 导入模块 B。这是否意味着模块 C 现在可以使用来自 CommonModule 的指令?

@Martin-Wegner 它们是可传递的,如此处所述https://angular.io/docs/ts/latest/cookbook/ngmodule-faq.html#! #q-重新导出

@aluanhaddad在哪里? 我找不到任何关于超过一跳的传递再出口的消息......

阅读@aluanhaddad从 Angular 文档中获取的内容,我觉得我必须重新考虑我所学到的关于 Angular 2 的所有内容,即使没有一个框架可以更好地处理依赖关系。

据我了解。 如果我有一个导入 HttpModule 的应用程序模块,并且我有一个带有服务的功能模块,它使用 Http 服务,我不应该在功能模块级别导入 HttpModule,而是在应用程序模块级别导入。 那么,如果我创建一个在许多应用模块之间共享的功能模块呢? 我必须真正在应用程序模块中导入 HttpModule。 我不能说,我的功能模块依赖于 HttpModule。 这真的很难看,意味着 NgModule 定义缺少很多特性,例如 PeerDependecies。
好家伙。 感觉就像 angular 2 正在分裂。 我担心是时候重新开始了,放弃角 2 并从角 3 开始。

你刚才说的其实很有道理。 我开始了这个
自去年以来的 angular2 旅程,并没有任何理由感到跳动
尽管到目前为止我们已经看到了所有重大变化(从 alpha 到 beta,
和RC)。 在那些阶段这是可以理解的,因为它虔诚地坚持
它使用的哲学将我从极简主义的 Backbonejs 中转换出来。
对我来说,这个 ngModules 的整个想法被证明有点相反
富有成效的。 看着我花在传福音的整个时间,我很难过
极简主义和无臃肿的“组件”的丰富优点去
绝对浪费。
2016 年 8 月 29 日上午 9:44,“Daniel Schuba” [email protected]写道:

阅读@aluanhaddad https://github.com/aluanhaddad抓取的内容
我觉得我必须重新考虑我所拥有的一切
了解了 Angular 2,也许即使没有框架
以更好的方式处理依赖关系。

据我了解。 如果我有一个应用程序模块导入 HttpModule 并且我
有一个带有服务的功能模块,它使用 Http 服务,我不应该
在功能模块级别但在应用模块级别导入 HttpModule。 所以呢
如果我创建一个在许多应用模块之间共享的功能模块? 我有
真正在 App Module 处导入 HttpModule。 我说不出来,
我的功能模块依赖于 HttpModule。 这真的很丑而且
意味着,NgModule 定义缺少很多功能,例如
对等依赖。
好家伙。 感觉就像 angular 2 正在分裂。 我怕是时候
重新开始,放弃 Angular 2 并从 Angular 3 开始。


您收到此消息是因为您订阅了此线程。
直接回复此邮件,在 GitHub 上查看
https://github.com/angular/angular/issues/10552#issuecomment -243066961,
或使线程静音
https://github.com/notifications/unsubscribe-auth/AF675h8_np9i5cHgL8mMOu8vMMQmWKkks5qkpv8gaJpZM4Jee-o
.

如果我错了,有人可以纠正我。

我正在考虑按如下方式组织应用程序。

应用程序
|--法律/
|----许多组件。
|----法律NG模块。
|--用户/
|----多组件
|----用户NG模块
|--AppNG模块
|--SomeFirstLeve组件

假设我想使用 Angular Material。 在我有点讨厌之前,我有太多的样板来导入每个组件中的每个元素。 现在我将它添加到 NG 模块中。 但是由于我决定为每个功能使用一个 ng 模块,因此我必须为我考虑的每个 ng 模块进行导入。 不只是根。 是对的吗?

或者,也许这是您创建模块 x 以重新导出 y(Material) 另一个模块,然后将其导入所需模块的情况?

@ReneVallecillo你是对的。 您必须在每个功能模块中导入它。 或者您将它与其他模块一起添加到共享模块中(隐藏依赖项),然后重新导出它,然后使用这个共享模块。 如果您将它重新导出到另一个模块,您甚至可能在不知道并立即看到它的情况下重复导入。

@robwormald针对您的上述评论

然而,在 AoT 模式下,这有点不同——在构建时,我们静态地(即,不执行代码)通过扫描装饰器从源代码中提取相同的元数据。

这是否意味着您正在从原始源代码中静态提取模块,因此我们实际上无法动态创建模块?

为了使迁移更容易,我正在考虑创建一个可以动态创建模块的函数; 像这样的东西:

function createModule (entryComponent: Type, dependencies: Type[]) {
    @NgModule({
        imports: [CommonModule, FormsModule],
        declarations: [entryComponent, ...dependencies],
        exports: [entyComponent]
    })
    class FeatureComponent {}
    return FeatureComponent;
}

因此,虽然这(可能?)适用于 JIT 编译,但它不适用于 AoT,因为 AoT 静态解析源代码,寻找装饰器?

ES@next 时代,这种不必要的复杂性...... @aluanhaddad提出了一些非常好的观点。

就目前情况而言,如果这是预期的方向,我不可能看到我的自我/团队继续使用 Angular 2。 它似乎是,因为这已经“关闭”了。

我可能不得不像@DaSchTour一样研究其他前端框架。 太糟糕了,因为几个月前,与 NG2 一起工作非常愉快,而且我显然是从垃圾中挑选出来的。

@iyobo除了最初的转换痛苦之外,对 NgModules 的大部分反馈(我和很多开发人员交谈过)都是积极的。

在一个 hello world 应用程序中孤立地看,你可能会认为它的“复杂性”(根据上面的解释,“不必要”实际上是不正确的),但它们确实在实际应用程序中发挥了作用——组织特性、惰性路由和NgModules 让 AoT 变得简单多了。 鉴于今天的选择,我仍然会选择将 NgModules 添加到框架中。

我在这里与@robwormald 在一起,在看到不必将每个组件/管道导入新创建的页面的优势之后,从 RC4/5 迁移到 NgModules 的最初痛苦是可以控制的。
NgModules 在变得更加复杂并拥有大量共享组件后开始大放异彩。

只需导入 SharedModule 并完成它就容易多了。

@robwormald ,人们不同意 Ng2 的当前方向的观点绝不是他们只适用于“hello world”级别应用程序的事实断言。

您所说的这种“功能划分”并不是什么新鲜事。 它一直是根据需要设计的,以适应使用组件的每个单独的产品/解决方案。
也就是说,实际上没有比组件自己声明它需要什么更大的划分。

现在说到延迟加载,拉回来,鸟瞰,延迟加载的主要好处是提高应用程序的加载速度。 我认为我们大多数人都有使这项工作发挥作用的管道。 输入缩小、压缩和多个应用程序入口点 ala webpack。

在我看来,NgModules 是寻找问题的解决方案,而不是实际问题的解决方案。 但话又说回来,我不能声称知道一切......

我认为时间会证明引入 NgModules 是否是一个好主意。 我的印象是 Angular 2 目前只是为了达到某些设计目标。 没有人考虑过诸如具有不同技能水平的团队、越来越老的代码以及代代相传的代码之类的事情。 在理想的世界中,它可能看起来是个好主意。 但实际上 NgModules 引入了很多陷阱和混淆,这会在训练和引入新项目的阶段造成很多麻烦。 它根本不像组件级别的声明那么简单和直观。 依赖关系隐藏在模块中,您必须开始研究组件所在的模块只是时间问题。

最近将一个更大的应用程序转换为 NgModules,我认为它们大多是一件好事(现在我已经完成了它,这很有意义)。 实际上,我认为主要问题是它们需要不同的组件结构,这可能与您之前构建应用程序的方式不太匹配(至少对我来说是这样)。

但是,我觉得它们不应该作为唯一的解决方案存在。 如果您查看组件的构建方式非常普遍,那么您会意识到它们通常由较小的高度专业化的子组件组成。 对于这些在容器组件之外没有任何用途的子组件,必须在更高范围的模块中维护它们确实很乏味,并且很快就会产生范围问题。

如果在 NgModules 之外还有一种机制允许组件将其他组件或指令定义为仅适用于该确切组件的本地范围依赖项,我将不胜感激(就像directives列表在模块之前的工作方式一样)。

我喜欢@poke的建议,即为那些不想使用 Full NgModules 的人提供替代方案

在 RC5 中添加了 NgModules 以解决编译和延迟加载工作的问题 - 所以几乎不是原始总体规划的一部分,这就是为什么它们不适合某些用例(特别是如果你正在构建一个基于原来的“组件为王”设计)。

当然,他们修复或启用了一些东西,但也带来了自己的复杂性——更多的东西供人们学习和设计,当所有最佳实践和方法都还没有制定出来时,这尤其具有挑战性。 早期采用会带来很多痛苦,我从骑到 RC4 中了解到这一点。

我更喜欢最初的以组件为中心的计划,这可能是我现在发现 Polymer / Web Components 更适合的原因。 模块感觉就像是回到 Angular 1 的半步。

起初我对移除管道和指令的这种改变持抗拒态度,但现在习惯了它,它似乎并没有我最初想象的那么糟糕。

谁能告诉我 NgModules 在一个非常复杂的应用程序中是如何工作的,其中多个组件需要多个级别的多个其他组件? 我看到的所有教程、示例和存储库都只展示了模块封装它自己的东西并将其中一些发布(导出)给其他人的简单方法。

在实际项目中,有许多交叉需要的依赖项,通常具有层次链。 通过专门为此指定的示例来证明某事物的概念在某种程度上是错误的。

@对于 Angular2的所有作者:您能否诚实地告诉我们 NgModules 的负面部分是什么? 我知道你可以写一本关于所有美好事物的书。 没关系。 但我总是需要处理隐藏的不好的事情,如果你不谈论它们就不清楚。

谁能告诉我 NgModules 在一个非常复杂的应用程序中是如何工作的,其中多个组件需要多个级别的多个其他组件?

一种方法是为这些组件创建一个依赖模块:假设您有三组组件ABC ,它们包含多个组件每一套都有一些相关的。 所以这三组适用于三个独立的模块。

现在,每个集合中的组件都需要来自集合D的多个组件。 D中的那些组件仅用于这三组中的组件。 由于它们在所有模块中都使用,因此您不能只将组件添加到这些模块中(因为组件可能只是单个模块的一部分)。 此时,您可以将ABCD合并到一个巨大的模块中,因此所有依赖项都在那里。 但这当然非常混乱。 相反,您只需为D创建一个仅包含这些依赖项的新模块。 除了提供对这些模块的访问之外,该模块不做任何事情。 现在,您可以在这三个其他模块中的每一个中导入该模块,并且能够使用这些组件。 但是因为组件仍然是“私有的”,所以您不需要重新导出模块或在其他模块中导入模块D

由于模块导入仅影响模块本身,这允许某种范围界定而不会污染其他模块。 当然,它需要您创建更多模块,但这就是它的工作原理。

我可以分享我当前的设置。 我在应用程序中使用了 3 个模块:CommonModule、AppModule 和 TestModule。

  • CommonModule 导入和导出最常见的东西,如 HttpModule、FormsModule、MdInputModule 等
  • AppModule 导入 BrowserModule、CommonModule、app.routing 和单个组件
  • TestModule 导入和导出 BaseModule,但覆盖了一些提供程序,例如带有 MockBackend 的 XHRBackend

我引入了这个设置来简化 TestBed.configureTestingModule,
所以我必须导入 TestModule ,然后只导入单个组件,例如:

TestBed.configureTestingModule({
  imports: [ TestModule ],
  declarations: [ MyFormComponent ]
});

当我从 RC-4 迁移到发布版本时,另一个明显的缺点是 NgModules 对只需要拆分组件的简单重构施加了沉重的惩罚。 使用 NgModules,您必须更改包含模块,它可能位于概念组件树的几个级别,或者通过将组件包装在 NgModule 中然后将其从其父 NgModule 中删除来提升组件。 重构组件是必不可少的。

这与@poke的观点直接相关

如果除了 NgModules 之外还有一种机制允许组件将其他组件或指令定义为仅适用于该确切组件的本地范围依赖项,我将非常感激(就像指令列表在模块之前的工作方式一样)。

我强烈反对。 Angular 2 提出的模块化架构更容易在必要时进行扩展、调整和重构。 当然,从 RC4 到 RC5 有一些调整,但如果有的话,对我来说,NgModules 已经证明可以实现更灵活的应用程序。

Angular 是固执己见的,它肯定不是万能的,但 NgModules 肯定不是构建智能、现代和高性能应用程序的失败点。

@emilio-martinez:在我看来,NgModule 永远不会被引入,如果 Angular 2 在涉及 JiT 时引导不那么慢的话。 正如本讨论所示,所有其他“改进”,如“缩放、调整和重构”都是有争议的。

现在已经有一段时间了,我们中的许多人都有时间将 NgModule 完全吸收到我们的工作中。 我认为现在很清楚,正如很多人所描述的那样,一旦您克服了升级到新模块系统的障碍,它就会实现一些非常棒的事情。 对于任何在这个线程中难以吸收 NgModule 的人,我建议一直滚动并阅读来自@robwormald@wardbell的所有内容。

我相信一年后我们都会发现,许多 Angular 2+ 应用程序普遍、无缝地使用模块、延迟加载和 AOT。 我相信对于大多数或几乎所有应用程序来说,使用这些东西来实现“渐进式应用程序”愿景将是完全常规的,即使是大型复杂应用程序也具有近乎即时的初始加载时间,然后延迟加载(或乐观地延迟预加载)它们的功能需要。 结果实际上非常漂亮,并且对于单个应用程序开发人员而言以非常低的成本实现:NgModule 基本上就是这样的成本,这是一个令人讨厌的过渡,但只有非常少量的正在进行的工作可以使用。

@kylecordes我希望你是对的,我认为这是正确的态度。

@iurii-kyrylenko 这是非常正确的。

我确实有角度编译我的 JavaScript 的问题,因为它应该由 TypeScript 编译我的 JavaScript,但这是一个单独的问题。

@kylecordes我认为除了过渡之外,还有更多需要考虑的事情。 模块引入了许多复杂性和许多将错误添加到自己的应用程序的额外可能性。 最大的问题是依赖关系的混淆。 这将在未来几年用 Angular 2 开发时造成很多麻烦。

@aluanhaddad我相信 Angular 使用 tsc 包装器进行编译。 这很好,因为您甚至可以将它实现到任务运行器工作流程中,例如。

@iurii-kyrylenko 引导速度也很难根据 RC4 确定。 从那时到最终版本所做的许多工作都是清理和优化。 以我的经验,Angular 编译的 JIT 比 RC4 运行得更快。

@DaSchTour你能详细说明你在使用 NgModule 时发现的错误吗?

@emilio-martinez 这不是 NgModule 中的错误,而是由于缺少导入或重复服务实例而出现的错误,这些错误在开发的早期阶段会被忽略或发现。 这是关于在我使用它们的地方导入东西,而不是在我看不到它是否需要或使用以及在什么地方需要和使用它的地方。

想想 TypeScript 以这种方式工作。 我的模块有一个基础文件,我们称它为 _index.ts_ 它看起来像这样。

import {foo} from bar;
import {StartComp} from start;

StartComp.boot();

比我们有一个看起来像这样的名为 start.ts 的文件。

export class StartComp {
   public static boot() {
      foo()
   }
}

这就是 Angular 对 NgModules 所做的事情。 通过一些魔法,我将一些东西导入了一个模块,它出现在我的应用程序的另一端。 您必须知道 foo 是在 index.ts 中导入的,并且通过从 index 运行 StartComp,可以在组件中使用那里的导入。

依赖项是隐藏的,需要额外调查才能找到它们。

@emilio-martinez

以我的经验,Angular 编译的 JIT 比 RC4 运行得更快。

我有一个中等复杂度的项目,基于 MEAN 堆栈和 Angular 2 final。 在我的 Android 设备上以 JIT 模式完成引导大约需要10 秒。 我认为这是一个重大的延迟。 目前我不能在不修改我的源代码的情况下使用 AOT 编译(私有成员的问题,elvis 运算符......)。

有没有人有关于现实生活项目的性能 AOT + 延迟负载的任何信息?

我相信 Angular 使用 tsc 包装器进行编译。 这很好,因为您甚至可以将它实现到任务运行器工作流程中,例如。

@emilio-martinez 这正是我不想要的。 我想使用我喜欢的任何版本的 TypeScript 编译我的代码,例如 2.1.0-dev,它具有用于 async/await 的低级发射。 我不希望 Angular 负责编译我的 TypeScript 文件,这不应该委托给框架,它是一种语言的角色。 如果不进一步偏离主题,我就不会用十英尺长的杆子触及@Script ,谢天谢地,它已经死了。
工作流程方面,我使用 JSPM,有时使用 Webpack,而不是传统的任务运行程序,我让我的 IDE 处理我的 linter。

另一个问题是我已经编写了抽象的装饰器,而我现在明白 aot 会忽略它们。 考虑到 Angular 的装饰器有多么繁重,我很失望地得知该框架不会完全支持装饰器,在 AOT 期间将它们视为静态注释,而实际上它们是底层语言中的运行时构造。

@aluanhaddad重要的是要了解在 AoT 编译期间发生了两个不同的步骤 - 第一个是生成 _new_ typescript 代码,第二个是将代码转换为 ES5/6。 ngc两者都是为了方便,但 AoT 中没有任何固有的要求。 在谷歌内部,我们两者都做,所以这种情况是优先考虑的。 任何人都可以实现编译器主机以支持在他们想要的任何环境中生成代码。

我们(还)不支持在 Angular 的内置装饰器之上进行抽象,但如果你想使用类似https://www.npmjs.com/package/core-decorators的东西,那很好。

@iurii-kyrylenko 建议您观看 AngularConnect 的第一天主题演讲,其中 LucidCharts 谈到了他们在 AoT 方面的经验。 见https://youtu.be/xQdV7q3e_2w?t=1411

恕我直言 - 每个人的第一目标应该是尽快进行 AoT 编译。 性能简直无与伦比。

@robwormald我还没有查看调用ngc时可用的选项 - 所以这可能已经被覆盖了。 我认为这些选项可以缓解这里表达的担忧,如果它们清楚地表明 NGC 正在实现第一个目的,这是它存在于第二个目的中作为便利/优化的主要原因。 如果文档或帮助显示如何为喜欢这样做的人单独运行现成的tsc ,那可以进一步缓解担忧吗?

@kylecordes我认为文档不会很快涵盖如何实现您自己的编译器主机。 它是一个高级用例,因此需要一些自主学习来实现。 我们在这里为 CLI 实现了类似的东西https://github.com/angular/angular-cli/tree/master/packages/webpack

@robwormald啊,我不是说实现你自己的编译器主机。 我的意思是一个两行的“构建过程”——首先调用ngc来发出一些生成的打字稿,然后自己调用tsc来编译所有打字稿(你的源代码加上生成的源代码) JavaScript。 这提供了一个快速的保证,即 typescript 代码只是由现成的 typescript 编译器编译为 JS。

@robwormald谢谢你的回复。
关于NGC,我想知道的是能否控制TS -> TS pass中的TypeScript编译器版本和设置。 我可以将 TypeScript 传递给 NGC,还是必须使用包含特定 TypeScript 版本的特定版本? 它们的耦合度如何?

关于装饰器,是否存在对在 Angular 装饰器上抽象的用户定义装饰器的跟踪支持问题? https://www.npmjs.com/package/core-decorators是一组正交的装饰器,但我有装饰器,它们通过包装 Angular 装饰器在我的 Angular 应用程序中强制执行模式和约定。 一个明显的用例是自动创建和强制执行包范围的组件名称前缀,但还有其他用例。

由于 NGC 不支持这一点,它如何知道哪些装饰器是 Angular 特定的?
它是否按名称匹配角度装饰器?
我希望不会因为这会违反 JavaScript 词法作用域。
一个简单的场景
_awesome-component-decorators.ts_

import { Component } from '@angular/core';
import template from './awesome-component.html';
import style from './awesome-component.less';

export const awesomeComponet = <T extends new (...args) => any>(target: T) =>
  Component({template, styles: [style], selector: snakeCase(target.name) })(target);

_consumer.ts_

import { awesomeComponet } 'app/shared/awesome-component-decorators';

<strong i="19">@awesomeComponent</strong> 
export class AnAwesomeComponent { }

<strong i="20">@awesomeComponent</strong> 
export class AnotherAwesomeComponent { }

@jpsfs您是否找到任何解决方案来动态加载组件而不向根应用程序模块添加组件声明? .

我也是 Angular 1.X 到 Angular 4 迁移项目的一部分。 该项目有大量组件可在不同应用程序中重用,这些组件基于应用程序上下文进行延迟加载。

根据我对 Angular 4 的理解

我们必须将组件依赖项添加到根@NgModule声明中,如下所示。

从“@angular/platform-b​​rowser-dynamic”导入{platformBrowserDynamic};
从“@angular/core”导入 {Component, NgModule};
...
...
@NgModule({
imports: [BrowserModule ], // 导入 Angular 的 BrowserModule
bootstrap: [BootStrapComp], // 表示引导组件
declarations: [com1, comp2 , comp5 ...... Comp n ] // 向模块注册我们的组件
})
导出类 AppModule {}

platformBrowserDynamic().bootstrapModule(AppModule);

但在我们的例子中,我们不想根 NgModule 来了解 compile time 中的组件依赖关系。 相反,我们希望组件在运行时动态加载。
您是否找到任何可以引导应用程序而无需将所有组件添加到根 NgModule 声明中的好的解决方案(而且我们也不希望每个组件都有一个 NgModule :))

@DaSchTour

还记得您的应用程序入口点看起来像这样的时代吗?

好吧,我们中的一些人使用构建脚本来自动要求这些模块并将它们添加到应用程序模块中。

我正在 angular2 中寻找类似且简单的解决方案。

@samudrak如果您想使用 Angular aot 支持,您不能只延迟加载组件。 您需要为每个组件设置延迟模块并延迟加载模块。 我们在我们的应用程序中使用类似的方法......

在 ECMAScript 和现在的 TypeScript 中支持动态导入(与加载正交的完整类型检查),延迟加载用例是相当随意的。 当然,事后看来是 20/20,没有办法知道会发生这种情况。

由于不活动,此问题已自动锁定。
如果您遇到类似或相关的问题,请提交新问题。

阅读更多关于我们的自动对话锁定政策

_此操作已由机器人自动执行。_

此页面是否有帮助?
0 / 5 - 0 等级