• Stars
    star
    185
  • Rank 202,686 (Top 5 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created about 6 years ago
  • Updated 12 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Strong Type framework with eggjs.

@eggjs/tegg

Install

# tegg 注解
npm i --save @eggjs/tegg
# tegg 插件
npm i --save @eggjs/tegg-plugin

Config

// config/plugin.js
exports.tegg = {
  package: '@eggjs/tegg-plugin',
  enable: true,
};
// config/config.default.js
{
  tegg: {
    // 读取模块支持自定义配置,可用于扩展或过滤不需要的模块文件
    readModuleOptions: {
      extraFilePattern: ['!**/dist', '!**/release'],
    },
  };
}

Usage

原型

module 中的对象基本信息,提供了

  • 实例化方式:每个请求实例化/全局单例/每次注入都实例化
  • 访问级别:module 外是否可访问

ContextProto

每次请求都会实例化一个 ContextProto,并且只会实例化一次

定义
@ContextProto(params: {
  // 原型的实例化名称
  // 默认行为:会把 Proto 的首字母转为小写
  // 如 UserAdapter 会转换为 userAdapter
  // 如果有不符合预期的可以手动指定,比如
  // @ContextProto({ name: 'mistAdapter' })
  // MISTAdapter
  // MISTAdapter 的实例名称即为 mistAdapter
  name?: string;

  // 对象是在 module 内可访问还是全局可访问
  // PRIVATE: 仅 module 内可访问
  // PUBLIC: 全局可访问
  // 默认值为 PRIVATE
  accessLevel?: AccessLevel;
})
示例
简单示例
import { ContextProto } from '@eggjs/tegg';

@ContextProto()
export class HelloService {
  async hello(): Promise<string> {
    return 'hello, module!';
  }
}
复杂示例
import { ContextProto, AccessLevel } from '@eggjs/tegg';

@ContextProto({
  accessLevel: AccessLevel.PUBLIC,
  name: 'helloInterface',
})
export default class HelloService {
  async hello(user: User): Promise<string> {
    const echoResponse = await this.echoAdapter.echo({ name: user.name });
    return `hello, ${echoResponse.name}`;
  }
}

SingletonProto

整个应用声明周期只会实例化一个 SingletonProto

定义
@SingletonProto(params: {
  // 原型的实例化名称
  // 默认行为:会把 Proto 的首字母转为小写
  // 如 UserAdapter 会转换为 userAdapter
  // 如果有不符合预期的可以手动指定,比如
  // @SingletonProto({ name: 'mistAdapter' })
  // MISTAdapter
  // MISTAdapter 的实例名称即为 mistAdapter
  name?: string;

  // 对象是在 module 内可访问还是全局可访问
  // PRIVATE: 仅 module 内可访问
  // PUBLIC: 全局可访问
  // 默认值为 PRIVATE
  accessLevel?: AccessLevel;
})
示例
简单示例
import { SingletonProto } from '@eggjs/tegg';

@SingletonProto()
export class HelloService {
  async hello(): Promise<string> {
    return 'hello, module!';
  }
}
复杂示例
import { SingletonProto, AccessLevel } from '@eggjs/tegg';

@SingletonProto({
  accessLevel: AccessLevel.PUBLIC,
  name: 'helloInterface',
})
export class HelloService {
  async hello(user: User): Promise<string> {
    const echoResponse = await this.echoAdapter.echo({ name: user.name });
    return `hello, ${echoResponse.name}`;
  }
}

生命周期 hook

由于对象的生命周期交给了容器来托管,代码中无法感知什么时候对象初始化,什么时候依赖被注入了。所以提供了生命周期 hook 来实现这些通知的功能。

定义
/**
 * lifecycle hook interface for egg object
 */
interface EggObjectLifecycle {
  /**
   * call after construct
   */
  postConstruct?(): Promise<void>;

  /**
   * call before inject deps
   */
  preInject?(): Promise<void>;

  /**
   * call after inject deps
   */
  postInject?(): Promise<void>;

  /**
   * before object is ready
   */
  init?(): Promise<void>;

  /**
   * call before destroy
   */
  preDestroy?(): Promise<void>;

  /**
   * destroy the object
   */
  destroy?(): Promise<void>;
}
示例
import { EggObjectLifecycle } from '@eggjs/tegg';

@ContextProto()
export class Foo implements EggObjectLifecycle {
  // 构造函数
  constructor() {
  }

  async postConstruct(): Promise<void> {
    console.log('对象构造完成');
  }

  async preInject(): Promise<void> {
    console.log('依赖将要注入');
  }

  async postInject(): Promise<void> {
    console.log('依赖注入完成');
  }

  async init(): Promise<void> {
    console.log('执行一些异步的初始化过程');
  }

  async preDestroy(): Promise<void> {
    console.log('对象将要释放了');
  }

  async destroy(): Promise<void> {
    console.log('执行一些释放资源的操作');
  }
}

注入

Proto 中可以依赖其他的 Proto,或者 egg 中的对象。

定义

@Inject(param?: {
  // 注入对象的名称,在某些情况下一个原型可能有多个实例
  // 比如说 egg 的 logger
  // 默认为属性名称
  name?: string;
  // 注入原型的名称
  // 在某些情况不希望注入的原型和属性使用一个名称
  // 默认为属性名称
  proto?: string;
})

示例

简单示例
import { EggLogger } from 'egg';
import { Inject } from '@eggjs/tegg';

@ContextProto()
export class HelloService {
  @Inject()
  logger: EggLogger;

  async hello(user: User): Promise<string> {
    this.logger.info(`[HelloService] hello ${user.name}`);
    const echoResponse = await this.echoAdapter.echo({ name: user.name });
    return `hello, ${echoResponse.name}`;
  }
}
复杂示例
import { EggLogger } from 'egg';
import { Inject } from '@eggjs/tegg';

@ContextProto()
export class HelloService {
  // 在 config.default.js 中
  // 配置了 customLogger,
  // 名称为 bizLogger
  @Inject({ name: 'bizLogger' })
  logger: EggLogger;

  async hello(user: User): Promise<string> {
    this.logger.info(`[HelloService] hello ${user.name}`);
    const echoResponse = await this.echoAdapter.echo({ name: user.name });
    return `hello, ${echoResponse.name}`;
  }
}

限制

  • ContextProto 可以注入任何 Proto 但是 SingletonProto 不能注入 ContextProto
  • 原型之间不允许有循环依赖,比如 Proto A - inject -> Proto B - inject- > Proto A,这种是不行的
  • 类似原型之间不允许有循环依赖,module 直接也不能有循环依赖
  • 一个 module 内不能有实例化方式和名称同时相同的原型
  • 不可以注入 ctx/app,用什么注入什么

兼容 egg

egg 中有 extend 方式,可以将对象扩展到 Context/Application 上,这些对象均可注入。Context 上的对象类比为 ContextProto,Application 上的对象类比为 SingletonProto。

进阶

module 内原型名称冲突

一个 module 内,有两个原型,原型名相同,实例化不同,这时直接 Inject 是不行的,module 无法理解具体需要哪个对象。这时就需要告知 module 需要注入的对象实例化方式是哪种。

定义
@InitTypeQualifier(initType: ObjectInitType)
示例
import { EggLogger } from 'egg';
import { Inject, InitTypeQualifier, ObjectInitType } from '@eggjs/tegg';

@ContextProto()
export class HelloService {
  @Inject()
  // 明确指定实例化方式为 CONTEXT 的 logger
  @InitTypeQualifier(ObjectInitType.CONTEXT)
  logger: EggLogger;
}
module 间原型名称冲突

可能多个 module 都实现了名称为 HelloAdapter 的原型, 且 accessLevel = AccessLevel.PUBLIC,需要明确的告知 module 需要注入的原型来自哪个 module.

定义
@ModuleQualifier(moduleName: string)
示例
import { EggLogger } from 'egg';
import { Inject, InitTypeQualifier, ObjectInitType } from '@eggjs/tegg';

@ContextProto()
export class HelloService {
  @Inject()
  // 明确指定使用来自 foo module 的 HelloAdapter
  @ModuleQualifier('foo')
  helloAdapter: HelloAdapter;
}

egg 内 ctx/app 命名冲突

egg 内可能出现 ctx 和 app 上有同名对象的存在,我们可以通过使用 EggQualifier 来明确指定注入的对象来自 ctx 还是 app。不指定时,默认注入 app 上的对象。

定义
@EggQualifier(eggType: EggType)
示例
import { EggLogger } from 'egg';
import { Inject, EggQualifier, EggType } from '@eggjs/tegg';

@ContextProto()
export class HelloService {
  @Inject()
  // 明确指定注入 ctx 上的 foo 而不是 app 上的 foo
  @EggQualifier(EggType.CONTEXT)
  foo: Foo;
}

单测

单测 Context

在单测中需要获取 egg Context 时,可以使用以下 API。

export interface Application {
  /**
   * 创建 module 上下文 scope
   */
  mockModuleContextScope<R=any>(this: MockApplication, fn: (ctx: Context) => Promise<R>, data?: any): Promise<R>;
}

获取对象实例

在单测中需要获取 module 中的对象实例时,可以使用以下 API。

export interface Application {
  /**
   * 通过一个类来获取实例
   * 注:app.getEggObject 只能获取 Singleton 实例
   */
  getEggObject<T> (clazz: EggProtoImplClass<T> ): Promise <T>;
}

export interface Context {
  /**
   * 通过一个类来获取实例
   * 注:ctx.getEggObject 可以获取 Singleton/Context 实例
   */
  getEggObject<T> (clazz: EggProtoImplClass<T> ): Promise <T>;
}

Egg 兼容性

目前 module 尚未实现所有 egg 的特性,如果需要使用 egg 的功能,可以通过一些方式来兼容。

Schedule

目前 Schedule 尚未实现注解,仍然需要使用 egg 的目录和继承方式,在这种场景下如果需要使用 module 的实现,需要使用 ctx.beginModuleScope。举个例子:

// notify/EC_FOO.js

import { Subscription, Context } from 'egg;

class FooSubscriber extends Subscription {
  private readonly ctx: Context;

  constructor(ctx: Context) {
    super(ctx);
  }

  async subscribe(msg) {
    await ctx.beginModuleScope(async () => {
      await ctx.module.fooService.hello(msg);
    });
  }
}

module.exports = Subscription;

注入 egg 对象

module 会自动去遍历 egg 的 Application 和 Context 对象,获取其所有的属性,所有的属性都可以进行无缝的注入。举个例子,如何注入现在的 egg proxy:

import { IProxy } from 'egg'

@ContextProto()
class FooService {
  @Inject()
  private readonly proxy: IProxy;

  get fooFacade() {
    return this.proxy.fooFacade;
  }
}
注入 logger

专为 logger 做了优化,可以直接注入 custom logger。

举个例子:

有一个自定义的 fooLogger

// config.default.js
exports.customLogger = {
  fooLogger: {
    file: 'foo.log',
  },
};

代码中可以直接注入:

import { EggLogger } from 'egg';

class FooService {
  @Inject()
  private fooLogger: EggLogger;
}

注入 egg 方法

由于 module 注入时,只可以注入对象,不能注入方法,如果需要使用现有 egg 的方法,就需要对方法进行一定的封装。

举个例子:假设 context 上有一个方法是 getHeader,在 module 中需要使用这个方法需要进行封装。

// extend/context.ts

export default {
  getHeader() {
    return '23333';
  }
}

先将方法封装成一个对象。

// HeaderHelper.ts

class HeaderHelper {
  constructor(ctx) {
    this.ctx = ctx;
  }

  getHeader(): string {
    return this.ctx.getHeader();
  }
}

再将对象放到 Context 扩展上即可。

// extend/context.ts

const HEADER_HELPER = Symbol('context#headerHelper');

export default {
  get headerHelper() {
    if (!this[HEADER_HELPER]) {
      this[HEADER_HELPER] = new HeaderHelper(this);
    }
    return this[HEADER_HELPER];
  }
}

生命周期

在 module 中,每个对象实例都有自己的生命周期,开发者可以对每个对象进行细致的控制。只要为对象实现 module 定义好的接口即可。所有生命周期 hook 均为可选方法,不需要的可以不实现。

接口定义

interface EggObjectLifecycle {
  /**
   * 在对象的构造函数执行完成之后执行
   */
  async postConstruct?();

  /**
   * 在注入对象依赖之前执行
   */
  async preInject?();

  /**
   * 在注入对象依赖之后执行
   */
  async postInject?();

  /**
   * 执行对象自定义异步初始化函数
   */
  async init?();

  /**
   * 在对象释放前执行
   */
  async preDestroy?();

  /**
   * 释放对象依赖的底层资源
   */
  async destroy?();
}

实现

import { EggObjectLifecycle } from '@eggjs/tegg';

@SingletonProto()
class FooService implement EggObjectLifecycle {
  @Inject()
  cacheService: CacheService;

  cache: Record<string, string>;

  async init() {
    this.cache = await this.cacheService.get(key);
  }
}

异步任务

module 在请求结束后会把请求相关的对象释放,所以在请求中使用 process.nextTicksetTimeoutsetInterval这类接口并不安全,可能导致一些错误。因此需要使用框架提供的接口,以便框架了解当前请求有哪些异步任务在执行,等异步任务执行完成后再释放对象。

安装

npm i --save @eggjs/tegg-background-task

使用

import { BackgroundTaskHelper } from '@eggjs/tegg-background-task';

@ContextProto()
export default class BackgroundService {
  @Inject()
  private readonly backgroundTaskHelper: BackgroundTaskHelper;

  async backgroundAdd() {
    this.backgroundTaskHelper.run(async () => {
      // do the background task
    });
  }
}

超时时间

框架不会无限的等待异步任务执行,在默认 5s 之后如果异步任务还没有完成则会放弃等待开始执行释放过程。如果需要等待更长的时间,建议有两种方式:

  • 推荐方式:将异步任务转发给单例对象(SingletonProto)来执行,单例对象永远不会释放
  • 调整超时时间,对 backgroundTaskHelper.timeout 进行赋值即可
  • 如果将超时时间设置为 Infinity,框架将不会超时
  • 可以在 config 文件中指定 backgroundTask.timeout 来全局覆盖默认的超时时间

动态注入

使用

定义一个抽象类和一个类型枚举。

export enum HelloType {
  FOO = 'FOO',
  BAR = 'BAR',
}

export abstract class AbstractHello {
  abstract hello(): string;
}

定义一个自定义枚举。

import { ImplDecorator, QualifierImplDecoratorUtil } from '@eggjs/tegg';
import { ContextHelloType } from '../FooType';
import { AbstractContextHello } from '../AbstractHello';

export const HELLO_ATTRIBUTE = 'HELLO_ATTRIBUTE';

export const Hello: ImplDecorator<AbstractHello, typeof HelloType> =
  QualifierImplDecoratorUtil.generatorDecorator(AbstractHello, HELLO_ATTRIBUTE);

实现抽象类。

import { ContextProto } from '@eggjs/tegg';
import { ContextHello } from '../decorator/Hello';
import { ContextHelloType } from '../FooType';
import { AbstractContextHello } from '../AbstractHello';

@ContextProto()
@Hello(HelloType.BAR)
export class BarHello extends AbstractHello {
  hello(): string {
    return `hello, bar`;
  }
}

动态获取实现。

import { EggObjectFactory } from '@eggjs/tegg';

@ContextProto()
export class HelloService {
  @Inject()
  private readonly eggObjectFactory: EggObjectFactory;
  
  async hello(): Promise<string> {
    const helloImpl = await this.eggObjectFactory.getEggObject(AbstractHello, HelloType.BAR);
    return helloImpl.hello();
  }
}

配置项目 module

一般情况下,无需在项目中手动声明包含了哪些 module,tegg 会自动在项目目录下进行扫描。但是会存在一些特殊的情况,tegg 无法扫描到,比如说 module 是通过 npm 包的方式来发布。因此 tegg 支持通过 config/module.json 的方式来声明包含了哪些 module。

支持通过 path 引用 app/modules/foo 目录下的 module。

[
  {"path":  "../app/modules/foo"}
]

支持通过 pkg 引用使用 npm 发布的 module。

[
  {"pkg": "foo"}
]

More Repositories

1

egg

🥚 Born to build better enterprise frameworks and apps with Node.js & Koa
JavaScript
18,650
star
2

examples

Store all egg examples in one place
JavaScript
1,748
star
3

awesome-egg

Awesome Egg.js Web Framework and Plugin.
869
star
4

egg-sequelize

Sequelize for Egg.js
JavaScript
609
star
5

egg-mongoose

JavaScript
437
star
6

egg-graphql

JavaScript
371
star
7

egg-mysql

MySQL plugin for egg
TypeScript
328
star
8

egg-ant-design-pro

showcase for Egg loves Ant Design
JavaScript
318
star
9

egg-redis

redis plugin for egg
JavaScript
267
star
10

egg-validate

validate plugin for egg
JavaScript
259
star
11

egg-security

Security plugin for egg, force performance too.
JavaScript
237
star
12

egg-socket.io

socket.io plugin for eggjs.
JavaScript
237
star
13

egg-cluster

cluster manager for egg
JavaScript
218
star
14

egg-core

A core Pluggable framework based on koa.
JavaScript
217
star
15

egg-bin

egg developer tool
TypeScript
183
star
16

egg-multipart

multipart plugin for egg
JavaScript
166
star
17

egg-ts-helper

🍳 Generate TypeScript definition files(d.ts) for Egg
TypeScript
157
star
18

egg-init

Init egg app helper tools
JavaScript
151
star
19

egg-view-vue

vue view plugin for egg
JavaScript
150
star
20

egg-cors

CORS plugin for egg
JavaScript
150
star
21

egg-logger

Egg logger
JavaScript
150
star
22

vscode-eggjs

vscode extension for https://eggjs.org/
TypeScript
145
star
23

egg-mock

Mock library for egg testing.
JavaScript
144
star
24

egg-router-plus

The missing router feature for eggjs
JavaScript
140
star
25

egg-scripts

deploy tool for egg projects
JavaScript
112
star
26

egg-rest

Restful API plugin for egg
JavaScript
107
star
27

egg-passport

passport plugin for egg
JavaScript
106
star
28

egg-view-react

egg view plugin for react
JavaScript
89
star
29

egg-view-nunjucks

nunjucks view plugin for egg
JavaScript
89
star
30

egg-static

static server plugin for egg
JavaScript
89
star
31

egg-schedule

Schedule plugin for egg
JavaScript
88
star
32

egg-oss

aliyun oss plugin for egg
JavaScript
87
star
33

egg-grpc

grpc plugin for egg
JavaScript
81
star
34

egg-sofa-rpc

SOFARPC plugin for egg
JavaScript
80
star
35

egg-userrole

user role plugin for egg
JavaScript
77
star
36

eslint-config-egg

Node Style Guide for Egg.
JavaScript
67
star
37

egg-view-ejs

egg view plugin for ejs.
JavaScript
64
star
38

egg-onerror

error handler for egg
Mustache
56
star
39

egg-alinode

alinode plugin for egg
JavaScript
54
star
40

egg-logrotator

Log rotate plugin for egg
JavaScript
53
star
41

egg-view-assets

Manage frontend assets in development and production.
JavaScript
52
star
42

egg-router

router for eggjs, fork from koa-router with some additional features
JavaScript
50
star
43

egg-orm

Object relational mapping for Egg framework
JavaScript
49
star
44

aliyun-egg

node web framework for aliyun, base on eggjs
JavaScript
49
star
45

egg-cancan

cancancan like authorization plugin for Egg.js
JavaScript
48
star
46

egg-session

session plugin for egg
JavaScript
47
star
47

egg-healthy

Liveness and Readiness health check for egg application
JavaScript
45
star
48

egg-passport-local

wrap passport-local strategy for egg-passport
JavaScript
45
star
49

egg-aop

AOP plugin for eggjs, add DI, AOP support.
TypeScript
44
star
50

egg-session-redis

redis store for egg session
JavaScript
43
star
51

egg-showcase-aliyun-blog

A blog showcase for aliyun-egg
JavaScript
42
star
52

egg-view

JavaScript
41
star
53

jar2proxy

transfer java facade jar to proxyjs
Java
41
star
54

egg-dubbo-rpc

dubbo rpc plugin for egg
JavaScript
40
star
55

egg-boilerplate-ts

Boilerplate for egg typescript project
TypeScript
40
star
56

egg-i18n

i18n plugin for egg
JavaScript
39
star
57

doctools

doctools for eggjs
JavaScript
38
star
58

egg-prometheus

Prometheus plugin for Egg.js
JavaScript
38
star
59

egg-http-proxy

http proxy for egg
JavaScript
38
star
60

egg-cloud

egg cloud provider tools for developers to quickly build some of the common patterns in distributed systems
JavaScript
38
star
61

egg-rpc-generator

RPC tools for egg framework
JavaScript
37
star
62

egg-errors

Errors for Egg.js.
TypeScript
31
star
63

egg-dingtalk

egg plugin for dingtalk
JavaScript
30
star
64

egg-cookies

cookies module for egg, base on pillarjs/cookies
JavaScript
30
star
65

egg-rpc

rpc plugin for eggjs
JavaScript
29
star
66

egg-tracer

tracer plugin for egg
JavaScript
29
star
67

create-egg

so you could use `npm init egg showcase` to init egg project
JavaScript
28
star
68

docker

Egg official docker image
JavaScript
27
star
69

egg-websocket

egg plugin for websocket
JavaScript
26
star
70

egg-path-matching

url path match/ignore support string, regexp and function
JavaScript
26
star
71

egg-lru

egg lru-cache plugin
JavaScript
25
star
72

egg-boilerplate-microservice

Boilerplate for egg microservice project
JavaScript
25
star
73

egg-watcher

Watcher plugin for egg
JavaScript
25
star
74

egg-parameters

Merge all parameters (ctx.params, ctx.request.query, ctx.request.body) into ctx.params like Rails application.
JavaScript
24
star
75

egg-development-proxyagent

[DEPRECATED] A proxy adapter for debugging httpclient on egg.
JavaScript
21
star
76

egg-userservice

userservice plugin for egg
JavaScript
21
star
77

egg-zookeeper

egg plugin for zookeeper
JavaScript
20
star
78

benchmark

benchmark for egg
JavaScript
20
star
79

egg-opentracing

Implementation of opentracing in Egg.js
JavaScript
19
star
80

egg-datahub

Macaca DataHub plugin for Egg.js
JavaScript
19
star
81

egg-ons

aliyun ons plugin for egg
JavaScript
18
star
82

egg-validate-schema

validate by json-schema plugin for egg
JavaScript
17
star
83

egg-development-proxyworker

**[DEPRECATED]** A proxy worker for debugging worker on egg
JavaScript
17
star
84

egg-boilerplate-simple

Boilerplate for egg simple project
JavaScript
16
star
85

eggjs.github.io

eggjs docs site
JavaScript
16
star
86

egg-logger-sls

Logger transport for aliyun sls.
JavaScript
15
star
87

egg-logview

Provide a log files viewer for development purpose.
JavaScript
15
star
88

egg-ci

Auto gen ci config file
JavaScript
15
star
89

egg-jsonp

jsonp support for egg, with security check inside
JavaScript
14
star
90

egg-boilerplate-plugin

Boilerplate for egg plugin
JavaScript
14
star
91

koa-express-adapter

The adapter for the migration from express to koa
JavaScript
14
star
92

egg-boilerplate-alipay-tiny

支付宝小程序服务端脚手架
JavaScript
14
star
93

egg-view-handlebars

egg view plugin for handlebars
JavaScript
12
star
94

egg-passport-github

github passport plugin for egg
JavaScript
11
star
95

egg-leancloud

leancloud plugin for Egg.js
JavaScript
11
star
96

egg-instrument

Compute the duration of an operation in local environment.
JavaScript
11
star
97

egg-utils

Utils for all egg projects.
TypeScript
11
star
98

egg-tslint-to-eslint

Migrate tslint-config-egg to eslint-config-egg in ts project.
JavaScript
9
star
99

egg-lookout

SOFALookout plugin for egg
JavaScript
9
star
100

egg-host

A host plugin for egg. It supports modify the host config, just like edit the /etc/hosts.
JavaScript
9
star