Angular中注解的实现原理

在试用Angular进行开发的时候,我们很多时间都是在设计Component、Module。在定义一个Component的时候我们使用的是@Component进行注解声明的,如下代码:

@Component({
    selector: "my-app",
    template: "<div></div>"
})
export class AppComponent {}

这里以@开始的语句和java中的注解的使用方式和作用都非常的类似。@Component的作用就是把相应的元数据添加到AppComponent中去。那么Angular里的注解是怎么实现的呢?

TypeScript中的装饰器

要知道Angular中注解实现的原理,首先就必须分析TypeScript中装饰器的原理,下面的代码就实现了一个简单的装饰器:

function f() {  //当调用C类method方法时其执行顺序是
    console.log("f(): evaluated");
    return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
        console.log("f(): called");
    }
}

function g() {
    console.log("g(): evaluated");
    return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
        console.log("g(): called");
    }
}

class C {
    @f()
    @g()
    method() { }
}

这段代码在调用C类的method方法的时候,输出的结果为:

f(): evaluated
g(): evaluated
g(): called
f(): called

其中f,g函数为装饰器工厂,他们主要作用是根据参数生成特定的装饰器(示例中没有参数,返回的也是特定的装饰器)。f,g函数内部定义的就是具体的装饰器方法,在这些方法中就可以对目标进行各种操作了。

Angular注解的原理

我们选用Component来分析Angular注解的原理,其它类如:Module,Input也都是类似的。
首先我们找到Component注解的实现的源码:

export const Component: ComponentDecorator = <ComponentDecorator>makeDecorator(
    'Component', {
      selector: undefined,
      inputs: undefined,
      outputs: undefined,
      host: undefined,
      exportAs: undefined,
      moduleId: undefined,
      providers: undefined,
      viewProviders: undefined,
      changeDetection: ChangeDetectionStrategy.Default,
      queries: undefined,
      templateUrl: undefined,
      template: undefined,
      styleUrls: undefined,
      styles: undefined,
      animations: undefined,
      encapsulation: undefined,
      interpolation: undefined,
      entryComponents: undefined
    },
    Directive);

makeDecorator的源码精简后如下:

export function makeDecorator(
    name: string, props: {[name: string]: any}, parentClass?: any,
    chainFn: (fn: Function) => void = null): (...args: any[]) => (cls: any) => any {

    // ...

    function DecoratorFactory(objOrType: any): (cls: any) => any {

        // ...

        const TypeDecorator: TypeDecorator = <TypeDecorator>function TypeDecorator(cls: Type<any>) {

            // ...

        };

        // ...

       return TypeDecorator;
    }

    // ...

    return DecoratorFactory;
}

从代码我们可以发现makeDecorator返回的是一个装饰器工厂,这个装饰器工厂会返回一个装饰器TypeDecorator,从而可以知道TypeDecorator中干的就是实现注解的工作:把元数据写入Component修饰的类中去(比如:AppComponent类)。
接下来我们进一步分析TypeDecorator的代码:

const TypeDecorator: TypeDecorator = <TypeDecorator>function TypeDecorator(cls: Type<any>) {
  const annotations = Reflect.getOwnMetadata('annotations', cls) || [];
  annotations.push(annotationInstance);
  Reflect.defineMetadata('annotations', annotations, cls);
  return cls;
};

这里试用到了一个第三方的库Reflect,其具体作用就是操作目标类上的元数据。TypeDecorator方法内容非常的简单,把@Component传入的参数作为元数据添加到目标类中去。
最后我们可以总结整个注解的实现过程:
1、试用makeDecorator生成装饰器工厂。
2、目标类中使用装饰器工厂,并传入相应的参数。
3、自动调用装饰器方法,把对应的元数据写入annotations属性中去。

动态修改注解

根据源码我们可以知道注解的元数据都是存放在annotations属性中的,我们可以动态的获取并修改他们。具体事例如下:

@Component({
    selector: "my-app",
    template: "<div></div>"
})
export class AppComponent {}

const annotations = Reflect.getOwnMetadata('annotations', AppComponent);
annotations[0].template = "<div>This is test!</div>"

这段代码就可以修改AppComponent中的template元数据的内容,达到动态修改的效果。但是要注意任何元数据的修改,改变的只是元数据本身,任何以元数据为基础产生的内容不会修改,比如事例中的代码如果在AppComponent实例化后的页面内容是没有作用的。这种动态修改的Component只能影响修改后动态创建的组件。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容

  • 版本:4.0.0+2 有一些英雄指南应用的新需求: 添加一个仪表盘 视图。 添加在英雄 视图和 仪表盘 视图之间导...
    soojade阅读 1,262评论 0 0
  • 仿制Reddit网站 读完本章之后, 你将掌握如何构建基本的Angular应用。 简单的应用将涵盖Angular中...
    东东少将阅读 3,231评论 1 28
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,274评论 25 707
  • 范老师在处理羞愧情绪时反应激烈,不愿意进行下去,看到自己的羞愧情绪是炸弹,她的反应撕心裂肺,她害怕把她炸坏,元老师...
    元柳青阅读 267评论 0 1
  • 我常常在怀疑自己坚持到底有没有意义,那么多的无奈和困难、质问和指责,其实他们是在担心你失败了怎么办,只有自己在想着...
    坚持在路上阅读 327评论 6 15