- 后端
- 微服务
- 云原生
【免费下载链接】midway
🍔 A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 🌈
组件是 Midway 的核心扩展机制,它把可复用的业务代码与通用能力(如配置、生命周期、拦截器、ORM、Swagger 等)抽象为独立的代码包,让同一份能力能够在所有 Midway 场景(Web 应用、Serverless 函数、多框架项目)中反复复用。本文将以@midwayjs/validation、@midwayjs/info等真实组件为例,完整讲解组件的结构、如何在src/configuration.ts中启用组件、如何按环境条件启用组件,并结合 packages/core 源码剖析组件加载的底层原理,读完即可在自己的 Midway 项目中自如地接入与管控组件。
组件是什么
Midway 将扩展能力统一抽象为“组件”(Component)。凡是需要跨项目复用的业务代码或公共能力,都可以封装成组件,使这些代码能够在所有 Midway 场景下复用。典型的组件场景包括:
- 包装下游调用、简化三方模块使用,例如 ORM 数据库调用、Swagger 文档生成、参数校验等;
- 沉淀可复用的业务逻辑,例如公共的 Controller、Service、拦截器、中间件等。
从 v3 开始,框架(Framework)本身也成为了组件的一部分,使用方式与普通组件保持统一——可以说,应用本身就是一个“大组件”。这一设计让组件化贯穿了 Midway 的整个体系:项目依赖、启动入口、生命周期、配置加载都遵循同一套约定。
组件结构:入口与 @Configuration
普通项目本身也是一个组件。组件的入口文件一般是src/index.ts或者src/configuration.ts,其中会导出一个带有@Configuration装饰器的类:
// 应用或者函数的 src/configuration.ts import { Configuration } from '@midwayjs/core'; @Configuration({ // ... }) export class MainConfiguration {}@Configuration装饰器定义在 packages/core/src/decorator/common/configuration.ts,从源码可以看到它做了三件事:
- 将
namespace默认设置为MAIN_MODULE_KEY,即应用主模块命名空间; - 通过
MetadataManager.defineMetadata将配置选项写入CONFIGURATION_KEY元数据; - 同时为被装饰的类追加
Singleton作用域和Provide标记,使其进入依赖注入容器管理。
也就是说,@Configuration不只是声明,它同时完成了类的注册与元数据的收集,是组件能够被框架识别与加载的关键。
启用组件
组件一般以 npm 包形式复用,每个组件都是一个可以被直接require或import的代码包。下面以@midwayjs/validation(参数校验组件)为例演示完整接入流程。
首先,在应用中加入依赖:
// package.json { "dependencies": { "@midwayjs/validation": "^4.0.0" } }然后在代码中启用该组件。Midway 的组件加载能力设计在src/configuration.ts文件中,通过@Configuration的imports属性传入组件对象:
// 应用或者函数的 src/configuration.ts import { Configuration } from '@midwayjs/core'; import * as validation from '@midwayjs/validation'; @Configuration({ imports: [validation], }) export class MainConfiguration {}imports是数组,支持同时引入多个组件,例如:
@Configuration({ imports: [validation, swagger, orm], }) export class MainConfiguration {}imports 支持的其他导入方式
从 packages/core/src/interface.ts 中InjectionConfigurationOptions的定义可见,imports数组的每一项可以是:
IComponentInfo:带enabledEnvironment等控制信息的组件对象(见下文“不同环境启用组件”);{ Configuration: ClassType }:一个导出了Configuration属性的组件包对象;FunctionalConfiguration:函数式(functional)写法下的配置对象。
除此之外,@Configuration还支持importConfigs(引入配置)、importObjects(注册对象)、importConfigFilter(配置过滤器)、namespace(命名空间)与detector(文件探测器)等选项,组件的配置与生命周期能力由此得以完整注入主应用。
不同环境启用组件
有些组件只需要在特定环境下生效,例如本地开发时才需要的信息展示组件@midwayjs/info(用于输出当前环境、路由等信息,出于安全考虑生产环境通常应关闭)。此时imports属性可以传入对象数组,在对象中针对组件启用的环境进行配置:
// 应用或者函数的 src/configuration.ts import { Configuration } from '@midwayjs/core'; import * as info from '@midwayjs/info'; @Configuration({ imports: [ { component: info, enabledEnvironment: ['local'], }, ], }) export class MainConfiguration {}其中两个关键字段的语义如下:
component:指定组件对象,组件对象必须包含一个Configuration导出的属性;enabledEnvironment:组件启用的环境数组,数组中的环境名与当前运行环境匹配时组件才会被加载。
IComponentInfo接口在 packages/core/src/interface.ts 中有明确约束:component为导出了Configuration属性的类或函数式配置,enabledEnvironment为字符串数组。
环境判断的底层原理
环境过滤并非魔法,其逻辑位于组件加载器 packages/core/src/context/componentLoader.ts 的addImports方法中。当imports中的某一项带有component字段时,加载器会先检查该项是否配置了enabledEnvironment:
if ((importPackage as IComponentInfo)?.enabledEnvironment) { if ( (importPackage as IComponentInfo)?.enabledEnvironment?.includes( this.container.get(MidwayEnvironmentService).getCurrentEnvironment() ) ) { this.load((importPackage as IComponentInfo).component); } } else { this.load((importPackage as IComponentInfo).component); }也就是说:配置了enabledEnvironment的组件,只有当前环境命中数组中的任意一个环境名时才会真正加载;未配置该字段的组件则无条件加载。当前环境由MidwayEnvironmentService.getCurrentEnvironment()提供(见 packages/core/src/service/environmentService.ts),其值最终来自运行时的环境判断工具(支持local、test、prod等环境约定)。因此enabledEnvironment: ['local']意味着仅在本地开发环境加载该组件,生产环境自动跳过。
组件加载流程的源码级解析
理解组件如何被“装入”应用,有助于排查组件不生效的问题。核心类是 packages/core/src/context/componentLoader.ts 中的ComponentConfigurationLoader,其加载流程如下:
- 查找 Configuration 导出:
getConfigurationExport会从模块导出中筛选出带有@Configuration元数据的类(支持一个模块导出多个配置的情况,数据交给第一个保存); - 防重复加载:通过
loadedMap(WeakMap)记录已加载的配置类,命中即跳过,防止组件循环引用导致死循环; - 读取配置元数据:优先读取函数式写法的
CONFIGURATION_OBJECT_KEY元数据,否则读取普通类写法的CONFIGURATION_KEY元数据; - 处理配置选项:依次调用
addImports(加载 imports 中的子组件)、addImportObjects(注册 importObjects)、addImportConfigs(合并 importConfigs 配置)、addImportConfigFilter(注册配置过滤器),并执行detector探测器; - 保存模块并绑定容器:通过
DecoratorManager.saveModule保存配置模块信息,最后bindClass将配置类绑定到容器(绑定到对应的namespace)。
值得注意的是,imports中如果直接传入字符串,加载器会通过require自动解析(见 componentLoader.ts),因此除了import * as xxx的写法外,也支持以包名字符串的方式声明依赖。整个加载过程同时提供异步load与同步loadSync两种形式,以适应不同的启动时序。
真实组件入口示例:@midwayjs/info
以文档示例中的info组件为例,其真实入口 packages/info/src/configuration.ts 展示了标准组件的形态:
import { Configuration, Inject, MidwayApplicationManager, } from '@midwayjs/core'; import { InfoMiddleware } from './middleware/info.middleware'; import * as DefaultConfig from './config.default'; @Configuration({ namespace: 'info', importConfigs: [ { default: DefaultConfig, }, ], }) export class InfoConfiguration { @Inject() applicationManager: MidwayApplicationManager; async onReady() { this.applicationManager .getApplications(['koa', 'faas', 'express', 'egg']) .forEach(app => { app.useMiddleware(InfoMiddleware); }); } }从这个组件可以学到组件开发的关键模式:
- 通过
namespace声明自己的命名空间,避免与其他组件同名冲突; - 通过
importConfigs引入自己的默认配置(config.default.ts); - 通过实现生命周期方法(如
onReady)在应用就绪时注入中间件; - 通过
@Inject()获取MidwayApplicationManager等容器内对象,从而面向多框架(koa / faas / express / egg)统一生效。
继续深入:组件开发
本文聚焦于组件的“使用”,若希望把公共能力沉淀为组件并发布成 npm 包,可阅读仓库中的 组件开发文档。其中要点包括:组件结构与应用保持一致(src/index.ts导出、src/configuration.ts配置入口)、通过namespace隔离作用域、尽量只依赖@midwayjs/core以面向所有上层框架场景,以及通过npm init midway@latest -y脚手架(选择component-v4模板)快速生成示例组件。
小结
- 组件是 Midway 复用的核心载体,应用本身也是一个组件,入口统一为
src/configuration.ts中带@Configuration装饰器的类; - 在
@Configuration的imports数组中直接引入组件 npm 包即可启用组件; - 需要环境隔离时,将 imports 项改写为
{ component, enabledEnvironment }对象,组件仅会在指定环境下加载; - 底层由
ComponentConfigurationLoader统一解析元数据、防重复加载、递归处理子 imports,并通过MidwayEnvironmentService判断当前环境,理解这些实现细节能帮助你在真实项目中精准控制组件行为。
掌握这套组件机制后,你就可以像搭积木一样组合 Midway 官方组件与自研组件,让业务代码与通用能力清晰分层、跨项目复用。
- 后端
- 微服务
- 云原生
【免费下载链接】midway
🍔 A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 🌈
相关推荐
AXWebViewController导航模式全攻略:微信风格与工具栏模式切换技巧
AXWebViewController导航模式全攻略:微信风格与工具栏模式切换技巧 AXWebViewController是一款专为iOS平台设计的轻量级网页浏
React Native组件化开发实战:构建可复用UI组件库
React Native组件化开发实战:构建可复用UI组件库 引言:组件化开发的痛点与解决方案 你是否还在为React Native项目中重复编写相似UI代码而
移动开发原生移动跨平台前端cocotb扩展开发指南:构建可复用的硬件验证组件
cocotb扩展开发指南:构建可复用的硬件验证组件 概述 cocotb作为一个强大的硬件验证框架,允许工程师使用Python编写测试平台来验证硬件设计。但在实际
测试硬件开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考