ES5 和 ES6 中的 Angular 2 依赖注入

Posted

技术标签:

【中文标题】ES5 和 ES6 中的 Angular 2 依赖注入【英文标题】:Angular 2 dependency injection in ES5 and ES6 【发布时间】:2016-12-15 23:58:38 【问题描述】:

这是一个基本的 TypeScript/ES.next 示例,它使用 DI 装饰器并遵循框架手册建议的语法:

import Component, Inject, Injectable, NgModule, OpaqueToken from '@angular/core';
import BrowserModule from '@angular/platform-browser';
import platformBrowserDynamic from '@angular/platform-browser-dynamic';

const CONSTANT =  value: 'constant' ;
const CONSTANT_TOKEN = new OpaqueToken;
const CONSTANT_PROVIDER =  provide: CONSTANT_TOKEN, useValue: CONSTANT ;

@Injectable()
class Service 
  constructor(@Inject(CONSTANT_TOKEN) constant) 
    console.log('Service constructor', constant);
  


@Component(
  selector: 'app',
  template: '...',
  providers: [Service, CONSTANT_PROVIDER]
)
class AppComponent 
  constructor(@Inject(Service) service: Service, @Inject(CONSTANT_TOKEN) constant) 
    console.log('AppComponent constructor', service, constant);    
  


@NgModule(
  imports: [BrowserModule],
  declarations: [AppComponent],
  bootstrap: [AppComponent]
)
class AppModule 

platformBrowserDynamic().bootstrapModule(AppModule);

在 ES5 中如何编写?

untranspiled ES6/ES2015 中如何做同样的事情?

InjectableInject 装饰器在这些情况下是如何翻译的?

这个问题尤其适用于具有类但可能使用requireSystem.import 而不是 ES6 导入的实际 ES6 浏览器实现。

【问题讨论】:

你为什么要这样写?谷歌正在努力。他们将支持 ES6。只是好奇。 @ArnoldB Babel/TS/Dart 元语言工作流程并不适合每个项目。 A2 开发显然目前主要集中在 TS 和 Dart 上,我不确定原始 JS 是否会在 A2 发布后不再是灰姑娘。知道你的选择永远不会有坏处。 我明白了。好吧,我喜欢 TS 只是因为作为 JS 的超集,你真的可以编写任何有效的 JS 并且它会运行。但是为了更多地关注你的问题,“@Injectable”装饰器这样做:“@Injectable() 将一个类标记为可用于注入器进行实例化。一般来说,注入器在尝试实例化一个类时会报告错误未标记为@Injectable()”。我假设要翻译“@Injectable”,您需要翻译他们正在谈论的 Injector。 @ArnoldB 这并不完全正确,刚刚才弄清楚Injectable 的事情。看起来它使一个类能够通过 TS 类型注释使用隐式注入,并且对于仅依赖于 Inject 的注入(如在 JS 中)是不必要的。我想我稍后会发布我自己的答案。 【参考方案1】:

Injectable 装饰器特定于 Angular 2 的 TypeScript 风格。它允许通过 TypeScript 类型注释为 DI 隐式注释类构造函数。对于使用Inject 注释的注入依赖项,它在 TS 中是多余的,而在 JS 中则不需要。

Angular 2 可注入对象(类和构造函数)应该在底层使用 annotationsparameters 静态属性进行注释。

annotations 是一个数组,其中包含用于可注入类的 newed 装饰器:

function SomeComponent(...) 
SomeComponent.annotations = [new Componenent(...)];

parameters 是一个数组,其中包含构造函数参数的装饰器,每个元素都是一个数组,其中包含newed 装饰器列表,用于各个构造函数属性(类似于 Angular 1.x 中的$inject 属性显式注释) :

function Service(someService, anotherService) 
Service.parameters = [
  [new Inject(SomeService)],
  [new Inject(AnotherService), new Optional, new SkipSelf]
];

所有类装饰器都是从TypeDecorator 扩展而来的,这意味着它们可以作为函数调用。在这种情况下,使用了所谓的 DSL 语法,它允许使用 Class helper function 链接装饰器:

var SomeComponent = Componenent(...).Class(...);

Class 也可以单独使用,它从给定的definition object 构造一个新类,并允许用数组注释constructor 方法(类似于Angular 1.x 中的内联数组显式注释):

var SomeService = Class(
  constructor: [[new Inject(SomeService)], function (someService) ]
);

Class helper 在最新的框架版本中已被弃用。它应该被 ES5 中的原始函数或第三方类助手替换。装饰器支持与类函数直接链接,Componenent(...)(ComponentClass)

Angular 2/4 ES6 和 System.import

example:

Promise.all([
  System.import('@angular/core'),
  System.import('@angular/platform-browser'),
  System.import('@angular/platform-browser-dynamic')
])
.then(([
  Component, Inject, Injectable, Optional, NgModule, OpaqueToken,
  BrowserModule,
  platformBrowserDynamic
]) => 

  const CONSTANT =  value: 'constant' ;
  const CONSTANT_TOKEN = new OpaqueToken;
  const CONSTANT_PROVIDER =  provide: CONSTANT_TOKEN, useValue: CONSTANT ;

  class Service 
    constructor(constant) 
  
  Service.parameters = [[new Inject(CONSTANT_TOKEN)]];

  class AppComponent 
    constructor(service, constant) 
  
  AppComponent.annotations = [new Component(
    selector: 'app',
    template: '...',
    providers: [Service, CONSTANT_PROVIDER]
  )];
  AppComponent.parameters = [[new Inject(Service)], [new Inject(CONSTANT_TOKEN)]];

  class AppModule 
  AppModule.annotations = [new NgModule(
    imports: [BrowserModule],
    declarations: [AppComponent],
    bootstrap: [AppComponent]
  )];

  platformBrowserDynamic().bootstrapModule(AppModule);

)
.catch((err) => console.error(err));

带有 UMD 模块和 ng 全局的 Angular 2/4 ES5

example:

var Class = ng.core.Class;
var Component = ng.core.Component;
var Inject = ng.core.Inject;
var Injectable = ng.core.Injectable;
var NgModule = ng.core.NgModule;
var OpaqueToken = ng.core.OpaqueToken;

var BrowserModule = ng.platformBrowser.BrowserModule;
var platformBrowserDynamic = ng.platformBrowserDynamic.platformBrowserDynamic;

var CONSTANT =  value: 'constant' ;
var CONSTANT_TOKEN = new OpaqueToken;
var CONSTANT_PROVIDER =  provide: CONSTANT_TOKEN, useValue: CONSTANT ;

// Class helper function that uses A1-flavoured inline array DI annotations
// and creates an annotated constructor
var Service = Class(
  constructor: [[new Inject(CONSTANT_TOKEN)], function (constant) 
    console.log('Service constructor', constant);
  ]
);
// can also be
// function Service(constant) ;
// Service.parameters = [[new Inject(...)], ...];

// when not being `new`ed, Component is a chainable factory that has Class helper method
var AppComponent = Component(
  selector: 'app', 
  template: '...',
  providers: [Service, CONSTANT_PROVIDER]
)
.Class(
  constructor: [
    [new Inject(Service)],
    [new Inject(CONSTANT_TOKEN)],
    function (service, constant) 
      console.log('AppComponent constructor', service, constant);
    
  ]
);
// can also be
// function AppComponent(...) ;
// AppComponent.annotations = [new Component(...)];
// AppComponent.parameters = [[new Inject(...)], ...];

var AppModule = NgModule(
  imports: [BrowserModule],
  declarations: [AppComponent],
  bootstrap: [AppComponent]
)
.Class( constructor: function ()  );
// can also be
// function AppModule() ;
// AppModule.annotations = [new NgModule(...)];

platformBrowserDynamic().bootstrapModule(AppModule);

【讨论】:

【参考方案2】:

要将 Angular 2 与 ES5 一起使用,您需要以下脚本:

<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/2.0.0-beta.3/angular2-all.umd.js"></script>

这提供了一个包含所有 Angular 2 的全局变量。现在您可以编写 ng.core.Component 而不是 @Component 注释。构造函数的第一个参数是可注入的。

var Component = ng.core
  Component(
    selector: 'hello-cmp',
    template: 'Hello World!',
    viewProviders: [Service]
  .Class(
    constructor: [Service, function (service)  
      ...
    ],
  );

并告诉注入器我们的服务参数是Service的实例

Component.parameters = [[new ng.core.Inject(Service)]];

以下示例显示了 angular2 与 ES6 的用法:

import Component from 'angular2/core';
import Service from './example.service';

let componentAnnotation = new Component(
  selector: 'world-time',
  inputs: ['timeZones'],
  providers: [Service],
  template: `
    ...
  `
);
export class ComponentExample 
   constructor(service) 
    this._service = service;

   
...



WorldTimeComponent.annotations = [componentAnnotation];
WorldTimeComponent.parameters = [[Service]];

In this plunkr 你可以找到一个有效的 ES6 示例。

但是你可以通过 Babel 来使用装饰器。启用 optional[]=es7.decorators(在 webpack 中)或将您的配置设置为 stage:1

【讨论】:

ES2015 不包括装饰器,问题是关于使用 Angular 2 和未编译的代码。我之前遇到过这种 ES5 语法,但我不确定它应该如何应用于 ES6 类。 我更新了我的答案,让 es5/es6 之间的区别更加清晰 谢谢。请考虑问题中的示例,而不是为了保持一致性,Serviceconstant 一起注入,这可能是一个重要的细节。您所指的示例使用 2.0.0.beta-11,从那时起发生了一系列重大变化。这是遵循建议语法的a plunker with 'es6' TS target,它会抛出"Can't resolve all parameters for Service: (?) angular2-all 脚本为 1.5 MB。对于 Web 应用程序来说太多了。

以上是关于ES5 和 ES6 中的 Angular 2 依赖注入的主要内容,如果未能解决你的问题,请参考以下文章

ES6+转ES5

ES5 与 ES6 承诺

ES5和ES6中继承的不同之处

ES5和ES6中的继承 图解

手动es6编译es5(命令行)

ES5和ES6中的继承