为什么必须提供Angular2组件构造函数参数

3

我是Angular的新手。在做英雄教程时,我注意到组件构造函数参数的类型必须是依赖项。换句话说,我必须将其包含在providers数组中。

例如:

import { SomeServiceClass } from '...';
@Component({
  ...
})
export class MyComponent implements OnInit {

  constructor(input1: SomeServiceClass) { }

  ngOnInit() {
  }
}

如果我不在providers数组中包含SomeServiceClass,那么这段代码是无法工作的。我尝试过input1: string,但它也不起作用!我相信这是完全有效的typescript代码。问题一定是angular2实例化组件类的方式。

问题1:Angular如何实际实例化组件类?

另外,由于我从未实例化服务类,我无法想象哪个服务类实例会传递给组件构造函数。

问题2:Angular是为每个组件生成一个服务实例,还是为所有组件使用同一个服务实例?

1个回答

6

如果我没有在providers数组中包含SomeServiceClass,这段代码将无法工作。我尝试了input1:string,它也不起作用!我相信这是完全有效的typescript代码。问题一定是angular2实例化组件类的方式。

你需要将SomeServiceClass添加到某个ngModuleproviders数组中的原因很简单。Angular选择不基于代码结构执行任何依赖性分析。他们可以使用注入组件中的ES模块导入来解决依赖关系并自动注册它。

证据是,Aurelia正是这样做的。

换句话说,在Aurelia中,以下内容足以注入和使用依赖项:

import {autoinject} from 'aurelia-framework';
import {SomeServiceClass} from '...';

@autoinject export class MyComponent {
  constructor(input1: SomeServiceClass) {}
}

帮助翻译:

它有效。

Angular如何实际实例化组件类?

由于我从未实例化过服务类,我无法想象哪个服务类实例会传递给组件构造函数。

在JavaScript中,类实际上是构造函数,而构造函数实际上是一个工厂函数,而工厂函数实际上是一个普通函数。

这意味着Angular(和大多数DI框架)只需通过使用new调用提供的类来创建服务的实例,当该服务首次被请求时。

这样的代码的简化版本可能如下所示

function instantiateComponent<T>(Component: new (...args: any[]) => T) {
  const requiredDependencies = resolveDependencies(Component);

  return new Component(...requiredDependencies);
}

function resolveDependencies(Dep: new (...args: any[]) => any): object[] {
  const requiredDependencies = metaDataUtils.getDependencies(Dep);
  
  return requiredDependencies.map(Dep => {
     const alreadyCreatedDependency = injectorCache.find(dep => dep instanceof Dep);
     
     if(alreadyCreatedDependency !== undefined) {
       return alreadyCreatedDependency;
     }
     else {
       const requiredDependencies = metaDataUtils.getDependencies(Dep);
       
       // resolution is recursive
       const newDependency = new Dep(...requiredDependencies.map(resolveDependencies));

       injectorCache.push(newDependency);

       return newDependency;
     }
  });
}

Angular会为每个组件生成一个服务实例还是只创建一个实例供所有组件使用?这有点棘手,默认情况下,每个应用程序只创建一个实例。这是应用程序级别的单例,所有请求它的组件都会收到相同的实例。但是,Angular也有不同的隐式和显式行为。如果一个懒加载的ngModule在其提供商数组中列出了相同的服务,则会创建第二个实例,并且该实例将由懒加载模块的子代共享。这实际上是一种过度简化,但已经令人困惑,并且由于模块是否被延迟加载是由其消费者确定的,因此可能会导致严重而微妙的错误。最后,组件可以在@Component元数据中显式声明自己的提供商。任何这样做的组件都将创建一个新的服务实例,并由该组件的所有实例共享。注意:当您说“我注意到组件构造函数参数的类型必须是依赖项”时,请具体说明。
你的理解不完全正确。依赖项不是“类型”,它们是“值”。TypeScript类型在编译时完全被擦除,实际注入的是值。类是一个值。
class C {}

但 TypeScript 也会从声明中推断出一些类型的存在。但注入的是值 C,而不是类型 C

1
非常好的回答!它也让我思考了一下Angular应用程序如何初始化。现在我只知道模块、服务和组件。您能否解释一下它们的运行顺序? - zhangjinzhou
由于Angular有很多组件(不是指Angular组件),因此必须有特定的顺序来运行代码的这些部分。例如,它可以首先运行服务以创建依赖项,然后运行组件以使用该服务。或者它可以首先运行组件,当需要服务时再运行服务。这是我提到的两种运行顺序的情况。这有意义吗? - zhangjinzhou
是的,它确实有一部分。还有其他组件,如模块和路由。我想了解Angular应用程序的整个流程。也许这个问题太广泛了,无法回答。 - zhangjinzhou
@zhangjinzhou,不是太宽泛,但这不是你所问的问题,需要解释的内容更多,而且坦率地说,我并不知道所有的细节。 - Aluan Haddad
你说得对,这与这个问题完全无关。我找到了其他的资料。谢谢你,Aluan! - zhangjinzhou
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接