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 中如何做同样的事情?
Injectable
和 Inject
装饰器在这些情况下是如何翻译的?
这个问题尤其适用于具有类但可能使用require
或System.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 可注入对象(类和构造函数)应该在底层使用 annotations
和 parameters
静态属性进行注释。
annotations
是一个数组,其中包含用于可注入类的 new
ed 装饰器:
function SomeComponent(...)
SomeComponent.annotations = [new Componenent(...)];
parameters
是一个数组,其中包含构造函数参数的装饰器,每个元素都是一个数组,其中包含new
ed 装饰器列表,用于各个构造函数属性(类似于 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 之间的区别更加清晰 谢谢。请考虑问题中的示例,而不是为了保持一致性,Service
与constant
一起注入,这可能是一个重要的细节。您所指的示例使用 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 依赖注入的主要内容,如果未能解决你的问题,请参考以下文章