概要
Midway 是阿里巴巴 - 淘宝前端架构团队,基于渐进式理念研发的 Node.js 框架,通过自研的依赖注入容器,搭配各种上层模块,组合出适用于不同场景的解决方案。 Midway 基于 TypeScript 开发,结合了面向对象(OOP + Class + IoC)与函数式(FP + Function + Hooks)两种编程范式。
细节
编程范式
- Spring IOC模式:midway1.0/2.0范式,基于Typescript的注解,以class+依赖注入方式使用;
- 函数式:Function + Hook,前端可在源码中直接调用后端函数,打包器会自动转化为HTTP调用,缺点是目前后端接口的默认范式无法自定义,如果后端接口还要提供给其他服务使用,则不适合用此方式。
- Faas模式:兼容了3个云服务商的函数计算,阿里云、腾讯云、AWS。
前端Hooks
为了支持全栈开发,创建了hooks项目,但已不再维护,examples可直接使用:github.com/midwayjs/ho…
bootstrap
midway的启动需要准备好container管理系统。
// packages/bootstrap/src/bootstrap.ts
export class BootstrapStarter
// packages/core/src/setup.ts
export async function initializeGlobalApplicationContext
{
// bind a class/value as a dependency, then use get() to get it
applicationContext.bindClass(MidwayFrameworkService);
await applicationContext.getAsync(MidwayFrameworkService, [
applicationContext,
globalOptions,
]);
await applicationContext.getAsync(MidwayAspectService, [applicationContext]);
await applicationContext.getAsync(MidwayDecoratorService, [
applicationContext,
]);
await applicationContext.getAsync(MidwayFrameworkService, [
applicationContext,
globalOptions,
]);
await applicationContext.getAsync(MidwayLifeCycleService, [
applicationContext,
]);
}
// bind
bindClass(exports, options?: Partial<IObjectDefinition>)
protected bindModule(module: any, options: Partial<IObjectDefinition>)
{
if (definition) {
this.registry.registerDefinition(definition.id, definition);
}
}
// get
get(identifier: any, args?: any[], objectContext?: ObjectContext): any
async getAsync(
identifier: any,
args?: any[],
objectContext?: ObjectContext
): Promise<any>
// create
create(opt: IManagedResolverFactoryCreateOptions): any
async createAsync(opt: IManagedResolverFactoryCreateOptions): Promise<any>
decorator
- @Provide 暴露class
- @Inject 注入class
// packages/decorator/src/decoratorManager.ts
export function Provide(identifier?: ObjectIdentifier) {
return function (target: any) {
return saveProviderId(identifier, target);
};
}
export function Inject(identifier?: ObjectIdentifier) {
return function (target: any, targetKey: string): void {
savePropertyInject({ target, targetKey, identifier });
};
}
// attach data to class or property
attachMetadata(
decoratorNameKey: ObjectIdentifier,
data,
target,
propertyName?: string,
groupBy?: string,
groupMode?: GroupModeType
)
// write decorator into metadata, relies on Reflect(reflect-metadata)
static attachMetadata(
metaKey: string,
target: any,
dataKey: string,
data: any,
groupBy?: string,
groupMode: GroupModeType = 'one'
)
@midway/hooks
api
export function Api<
Operators extends Operator<any>[],
Handler extends AsyncFunction
>(
...args: [...operators: Operators, handler: Handler]
): ApiRunner<
ExtractInputType<Operators> extends void[]
? void
: ArrayToObject<ExtractInputType<Operators>>,
Handler
> {
const metadataHelper: MetadataHelper = {
getMetadata(key: any) {
return Reflect.getMetadata(key, runner)
},
setMetadata(key: any, value: any) {
return Reflect.defineMetadata(key, value, runner)
},
}
for (const operator of operators) {
operator.metadata(metadataHelper)
}
const executors = operators
.filter((operator) => typeof operator.execute === 'function')
.map((operator) => operator.execute)
async function runner(...args: any[]) {
await compose(stack)(executeHelper)
}
Reflect.defineMetadata(USE_INPUT_METADATA, useInputMetadata, runner)
return runner as any
}
export function Query<T extends Record<string, string>>(): Operator<{
query: T
}> {
return {
name: HttpMetadata.QUERY,
input: true,
metadata({ setMetadata }) {
setMetadata(HttpMetadata.QUERY, true)
},
}
}
rpc(client)
function createHttpMethodOperator(method: HttpMethod) {
return (path?: string) => {
return {
name: method,
metadata({ setMetadata }) {
setMetadata<HttpTrigger>(OperatorType.Trigger, {
type: HttpTriggerType,
method,
path,
requestClient: {
fetcher: 'http',
client: '@midwayjs/rpc',
},
})
},
} as Operator<void>
}
}
export const Get = createHttpMethodOperator(HttpMethod.GET)
// packages/hooks-bundler/src/index.ts
const plugin = createBundlerPlugin(new MidwayBundlerAdapter())
export const vite = plugin.vite
// packages/bundler/src/index.ts
export function createBundlerPlugin(adapter: AbstractBundlerAdapter)
docs
site/docs/aspect.md
总结
midway.js 开发时间较早,但是开发思想并不落后,先后支持了IOC模式和hook模式,但源码阅读起来还是有一些历史债的,3.0版本的TS代码达到了252631行。目前v3的发展方向个人感觉不是很清晰,可能3.0是集大成的最好版本了。