From 3cd3235e77839ab8d67e533bd79cd9f78267f10a Mon Sep 17 00:00:00 2001 From: Harry Chen Date: Sat, 28 Dec 2024 23:15:30 +0800 Subject: [PATCH] docs: add some docs --- .../version-3.0.0/deployment.md | 1 - site/docs/container.md | 417 ++++++++++++++---- site/docs/intro.md | 5 +- site/docs/midway_component.md | 25 +- site/docs/sidebars.json | 2 +- site/docs/upgrade_v4.md | 177 ++++++++ site/i18n/en/code.json | 122 ++--- .../current.json | 2 +- .../current/container.md | 336 ++++++++++++-- .../current/intro.md | 9 +- .../current/midway_component.md | 20 +- .../current/upgrade_v4.md | 168 +++++++ 12 files changed, 1101 insertions(+), 183 deletions(-) delete mode 100644 i18n/en/docusaurus-plugin-content-docs/version-3.0.0/deployment.md create mode 100644 site/docs/upgrade_v4.md create mode 100644 site/i18n/en/docusaurus-plugin-content-docs/current/upgrade_v4.md diff --git a/i18n/en/docusaurus-plugin-content-docs/version-3.0.0/deployment.md b/i18n/en/docusaurus-plugin-content-docs/version-3.0.0/deployment.md deleted file mode 100644 index 0519ecba6ea9..000000000000 --- a/i18n/en/docusaurus-plugin-content-docs/version-3.0.0/deployment.md +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/site/docs/container.md b/site/docs/container.md index 60ce90d9aaeb..0b2c238fc58b 100644 --- a/site/docs/container.md +++ b/site/docs/container.md @@ -1,3 +1,6 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + # 依赖注入 Midway 中使用了非常多的依赖注入的特性,通过装饰器的轻量特性,让依赖注入变的优雅,从而让开发过程变的便捷有趣。 @@ -131,7 +134,7 @@ await userController.handler(); // output 'world' ## 依赖注入作用域 -默认的未指定或者未声明的情况下,所有的 `@Provide` 出来的 Class 的作用域都为 **请求作用域**。这意味着这些 Class ,会在**每一次请求第一次调用时被实例化(new),请求结束后实例销毁。**我们默认情况下的控制器(Controller)和服务(Service)都是这种作用域。 +默认的未指定或者未声明的情况下,所有的 `@Provide` 出来的 Class 的作用域都为 **请求作用域**。这意味着这些 Class ,会在 **每一次请求第一次调用时被实例化(new),请求结束后实例销毁。** 我们默认情况下的控制器(Controller)和服务(Service)都是这种作用域。 在 Midway 的依赖注入体系中,有三种作用域。 @@ -141,7 +144,7 @@ await userController.handler(); // output 'world' | Request | **默认**,请求作用域,生命周期绑定 **请求链路**,实例在请求链路上唯一,请求结束立即销毁 | | Prototype | 原型作用域,每次调用都会重复创建一个新的对象 | -不同的作用域有不同的作用,**单例 **可以用来做进程级别的数据缓存,或者数据库连接等只需要执行一次的工作,同时单例由于全局唯一,只初始化一次,所以调用的时候速度比较快。而 **请求作用域 **则是大部分需要获取请求参数和数据的服务的选择,**原型作用域 **使用比较少,在一些特殊的场景下也有它独特的作用。 +不同的作用域有不同的作用,**单例** 可以用来做进程级别的数据缓存,或者数据库连接等只需要执行一次的工作,同时单例由于全局唯一,只初始化一次,所以调用的时候速度比较快。而 **请求作用域** 则是大部分需要获取请求参数和数据的服务的选择,**原型作用域** 使用比较少,在一些特殊的场景下也有它独特的作用。 @@ -172,7 +175,7 @@ export class UserService { ### 单例作用域 -在显式配置后,某个类的作用域就可以变成单例作用域。。 +在显式配置后,某个类的作用域就可以变成单例作用域。 ```typescript // service @@ -476,10 +479,17 @@ export class HomeController { Midway 支持多种方式的注入。 -### 基于 Class 的注入 +### 基于类型的注入 导出一个 Class,注入的类型使用 Class,这是最简单的注入方式,大部分的业务和组件都是使用这样的方式。 +标准的注入有两种形式,**属性注入** 以及 **构造器注入**。 + + + + +Midway 会自动使用 B 作为 b 这个属性的类型,在容器中实例化它。 + ```typescript import { Provide, Inject } from '@midwayjs/core'; @@ -498,15 +508,61 @@ export class A { } ``` -Midway 会自动使用 B 作为 b 这个属性的类型,在容器中实例化它。 + + + -在这种情况下,Midway 会自动创建一个唯一的 uuid 关联这个 Class,同时这个 uuid 我们称为 **依赖注入标识符**。 +在 `4.0.0` 版本中,我们恢复了构造器注入的特性,用户可以显式的在构造器中注入依赖。 + +```typescript +import { Provide, Inject } from '@midwayjs/core'; + +@Provide() +export class B { + //... +} + +@Provide() +export class A { + private b: B; + + constructor(@Inject() b: B) { + this.b = b; + } +} +``` + +Midway 会自动使用 B 作为 b 这个参数类型,在容器中实例化它。 + +在 TypeScript 中,你也可以使用简化的写法,带有访问修饰符的参数会自动创建对应的属性。 + +```typescript +@Provide() +export class A { + constructor(protected @Inject() b: B) {} +} +``` + +:::tip + +仅有 `@Inject` 装饰器支持构造器注入。 + +::: + + + + + +### 依赖注入标识符 + + +不管哪种注入方式,Midway 都会自动创建一个唯一的 uuid 关联这个 Class,同时这个 uuid 我们称为 **依赖注入标识符**。 默认情况: -- 1、 `@Provide` 会自动生成一个 uuid 作为依赖注入标识符 +- 1、 `@Provide` 会自动生成一个 uuid 作为 **依赖注入标识符** - 2、 `@Inject` 根据类型的 uuid 来查找 如果要获取这个 uuid,可以使用下面的 API。 @@ -518,14 +574,12 @@ const uuid = getProviderUUId(B); // ... ``` - - -### 基于固定名字的注入 +可以通过 `@Provide` 装饰器和 `@Inject` 装饰器的参数指定 **依赖注入标识符**,比如指定一个字符串。 ```typescript import { Provide, Inject } from '@midwayjs/core'; -@Provide('bbbb') // <------ 暴露一个 Class +@Provide('bbbb') export class B { //... } @@ -534,69 +588,25 @@ export class B { export class A { @Inject('bbbb') - b: B; // <------ 这里的属性使用 Class + b: B; //... } ``` -Midway 会将 `bbbb` 作为 B 这个 Class 的依赖注入标识符,在容器中实例化它。这种情况下,即使写了类型 B,依赖注入容器依旧会查找 `bbbb` 。 +Midway 会将 `bbbb` 作为 B 这个 Class 的依赖注入标识符,在容器中实例化它。这种情况下,即使写了类型 B,依赖注入容器依旧会优先使用 `bbbb` 。 `@Provide` 和 `@Inject` 装饰器的参数是成对出现。 规则如下: -- 1、如果装饰器包含参数,则以 **参数 **作为依赖注入标识符 +- 1、如果装饰器包含参数,则以 **参数** 作为依赖注入标识符 - 2、如果没有参数,标注的 TS 类型为 Class,则将类 `@Provide` 的 key 作为 key,如果没有 key,默认取 uuid - 3、如果没有参数,标注的 TS 类型为非 Class,则将 **属性名** 作为 key -### 基于属性名的注入 - -Midway 也可以基于接口进行注入,但是由于 Typescirpt 编译后会移除接口类型,不如使用类作为定义好用。 - -比如,我们定义一个接口,以及它的实现类。 - -```typescript -export interface IPay { - payMoney() -} - -@Provide('APay') -export class A implements IPay { - async payMoney() { - // ... - } -} - -@Provide('BPay') -export class B implements IPay { - async payMoney() { - // ... - } -} -``` - -这个时候,如果有个服务需要注入,可以使用下面显式声明的方式。 - -```typescript -@Provide() -export class PaymentService { - - @Inject('APay') - payService: IPay; // 注意,这里的类型是接口,编译后类型信息会被移除 - - async orderGood() { - await this.payService.payMoney(); - } - -} -``` - -由于接口类型会被移除,Midway 只能通过 `@Inject` 装饰器的 **参数** 或者 **属性名** 类来匹配注入的对象信息,类似 Java Spring 中的 `Autowire by name` 。 - ### 注入已有对象 @@ -623,7 +633,7 @@ export class MainConfiguration { async onReady(applicationContext: IMidwayContainer) { // 向依赖注入容器中添加一些全局对象 - applicationContext.registerObject('lodash', lodash); + applicationContext.registerObject('lodash', lodash); } } @@ -648,10 +658,10 @@ export class BaseService { -### 注入默认标识符 +### 内置的默认标识符 -Midway 会默认注入一些值,方便业务直接使用。 +Midway 会默认注入一些标识符,方便业务直接使用。 | **标识符** | **值类型** | **作用域** | **描述** | | ---------- | ---------- | ---------- | ------------------------------------------------------------ | @@ -680,6 +690,76 @@ export class BaseService { } ``` +## 循环依赖 + +如果项目较大,则可能会出现循环依赖的问题。 + +比如 A 依赖 B,B 依赖 C,C 依赖 A。 + +```typescript +@Provide() +export class A { + @Inject() + b: B; +} + +@Provide() +export class B { + @Inject() + c: C; +} + +@Provide() +export class C { + @Inject() + a: A; +} +``` + +以上代码会报错,因为 A 依赖 B,B 依赖 C,C 依赖 A,形成了循环依赖。 + +### 识别循环依赖 + +在 `4.0.0` 版本中,Midway 会自动识别循环依赖,并抛出错误。 + +你会看到类似下面的错误信息。 + +```typescript +Circular dependency detected: A -> B -> C -> A +``` + +### 解决循环依赖 + +可以通过框架提供的 `@LazyInject` 装饰器来解决循环依赖的问题。 + +```typescript +import { LazyInject, Inject, Provide } from '@midwayjs/core'; + +@Provide() +export class A { + @Inject() + b: B; +} + +@Provide() +export class B { + @Inject() + c: C; +} + +@Provide() +export class C { + @LazyInject(() => A) + a: A; +} + +``` + +:::tip + +仅在需要解决循环依赖的时候,才使用 `@LazyInject` 装饰器。 + +::: ## 获取依赖注入容器 @@ -761,7 +841,7 @@ export class BootApp { ``` -除了普通的依赖注入容器之外,Midway 还提供了一个 **请求链路的依赖注入容器,**这个请求链路的依赖注入容器和全局的依赖注入容器关联,共享一个对象池。但是两者还是有所区别的。 +除了普通的依赖注入容器之外,Midway 还提供了一个 **请求链路的依赖注入容器** ,这个请求链路的依赖注入容器和全局的依赖注入容器关联,共享一个对象池。但是两者还是有所区别的。 请求链路的依赖注入容器,是为了获取特有的请求作用域的对象,这个容器中获取的对象,都是**和请求绑定**,关联了当前的上下文。这意味着,**如果 Class 代码和请求关联,必须要从这个请求链路的依赖注入容器中获取**。 @@ -824,7 +904,7 @@ export class MainConfiguration { ### 动态获取实例 -拿到 **依赖注入容器 **或者 **请求链路的依赖 **注入容器之后,才可以通过容器的 API 获取到对象。 +拿到 **依赖注入容器** 或者 **请求链路的依赖** 注入容器之后,才可以通过容器的 API 获取到对象。 我们可以使用标准的依赖注入容器 API 来获取实例。 @@ -1079,7 +1159,7 @@ export const getGlobalConfig = () => { ## 启动行为 -### 自动扫描绑定 +### 自动扫描和绑定 上面提到,在容器初始化之后,我们会将现有的 class 注册绑定到容器中。 @@ -1089,38 +1169,217 @@ container.bind(UserController); container.bind(UserService); ``` -Midway 在启动过程中会自动扫描整个项目目录,自动处理这个行为,使得用户无需手动执行绑定的操作。 +在 `4.0.0` 版本中,我们引入了 `detector` 的概念,用于扫描文件并进行绑定。 简单的来说,框架默认会递归扫描整个 `src` 目录下的 ts/js 文件,然后进行 require 操作,当文件导出的为 class,且显式或隐式包含 `@Provide()` 装饰器时,会执行 `container.bind` 逻辑。 +下面的逻辑显式的声明了文件的加载行为,用户可以自定义文件探测器,来实现不同的文件加载行为。 +```typescript +// src/configuration.ts +import { Configuration, CommonjsFileDetector } from '@midwayjs/core'; + +@Configuration({ + detector: new CommonjsFileDetector(), +}) +export class MainConfiguration {} +``` + + +### 文件探测器 -### 忽略扫描 +通过 `detector` 这个配置,我们可以自定义文件的加载行为。 -一般情况下,我们不应该把非 ts 文件放在 src 下(比如前端代码),特殊场景下,我们可以忽略某些目录,可以在 `@Configuration` 装饰器中配置。 +框架提供了 `CommonJSFileDetector` 和 `ESModuleFileDetector` 两个文件加载器,分别用于加载 Commonjs 和 ESM 格式的文件。 -示例如下: + + + +如果你希望加载 Commonjs 格式的文件,那么可以这样配置: ```typescript // src/configuration.ts -import { App, Configuration, Logger } from '@midwayjs/core'; -// ... +import { Configuration, CommonJSFileDetector } from '@midwayjs/core'; @Configuration({ - // ... - detectorOptions: { + detector: new CommonJSFileDetector(), +}) +export class MainConfiguration {} +``` + + + + + +如果你希望加载 ESM 格式的文件,那么可以这样配置: + +```typescript +// src/configuration.ts +import { Configuration, ESModuleFileDetector } from '@midwayjs/core'; + +@Configuration({ + detector: new ESModuleFileDetector(), +}) +export class MainConfiguration {} +``` + + + + +默认情况下,会扫描 `src` 目录下的以下文件,并进行自动处理和绑定。 + +```typescript +export const DEFAULT_PATTERN = [ + '**/**.tsx', + '**/**.ts', + '**/**.js', + '**/**.mts', + '**/**.mjs', + '**/**.cts', + '**/**.cjs', +]; +``` + +同时忽略以下目录和文件: + +```typescript +export const DEFAULT_IGNORE_PATTERN = [ + '**/logs/**', + '**/run/**', + '**/public/**', + '**/app/view/**', + '**/app/views/**', + '**/app/extend/**', + '**/node_modules/**', + '**/**.test.ts', + '**/**.test.js', + '**/__test__/**', + '**/**.d.ts', + '**/**.d.mts', + '**/**.d.cts' +]; +``` + +在以上默认配置的基础上,我们可以通过一些配置来调整文件扫描行为。 + +如果我们希望扫描忽略某些目录或者类型,那么可以配置 `ignore` 来实现。 + + + + +```typescript +// src/configuration.ts +import { Configuration, CommonJSFileDetector } from '@midwayjs/core'; + +@Configuration({ + detector: new CommonJSFileDetector({ ignore: [ - '**/web/**' - ] - } + '**/logs/**', + ], + }), }) -export class MainConfiguration { - // ... -} +export class MainConfiguration {} +``` + + + + + +```typescript +// src/configuration.ts +import { Configuration, ESModuleFileDetector } from '@midwayjs/core'; + +@Configuration({ + detector: new ESModuleFileDetector({ + ignore: [ + '**/logs/**', + ], + }), +}) +export class MainConfiguration {} +``` + + + + +可以通过配置 `pattern` 增加扫描的文件后缀和格式。 + + + + +```typescript +// src/configuration.ts +import { Configuration, CommonJSFileDetector } from '@midwayjs/core'; + +@Configuration({ + detector: new CommonJSFileDetector({ + pattern: [ + '**/**.jsx' + ], + }), +}) +export class MainConfiguration {} +``` + + + + +```typescript +// src/configuration.ts +import { Configuration, ESModuleFileDetector } from '@midwayjs/core'; + +@Configuration({ + detector: new ESModuleFileDetector({ + pattern: [ + '**/**.jsx' + ], + }), +}) +export class MainConfiguration {} +``` + + + + + +通过配置 `conflictCheck` 来处理导出的类名检查,默认情况下,如果检测到导出相同的名字,会抛出错误。 + +可以通过配置 `conflictCheck` 来关闭这个检查。 + + + + +```typescript +// src/configuration.ts +import { Configuration, CommonJSFileDetector } from '@midwayjs/core'; + +@Configuration({ + detector: new CommonJSFileDetector({ + conflictCheck: false, + }), +}) +export class MainConfiguration {} ``` + + + + +```typescript +// src/configuration.ts +import { Configuration, ESModuleFileDetector } from '@midwayjs/core'; + +@Configuration({ + detector: new ESModuleFileDetector({ + conflictCheck: false, + }), +}) +export class MainConfiguration {} +``` + + @@ -1234,7 +1493,7 @@ export class UserService { ### 错误:构造器中获取注入属性 -**请不要在构造器中 **获取注入的属性,这会使得拿到的结果为 undefined。原因是装饰器注入的属性,都在实例创建后(new)才会赋值。这种情况下,请使用 `@Init` 装饰器。 +**请不要在构造器中** 获取注入的属性,这会使得拿到的结果为 undefined。原因是装饰器注入的属性,都在实例创建后(new)才会赋值。这种情况下,请使用 `@Init` 装饰器。 ```typescript @Provide() export class UserService { diff --git a/site/docs/intro.md b/site/docs/intro.md index ad2548a0a195..ce23553da86c 100644 --- a/site/docs/intro.md +++ b/site/docs/intro.md @@ -90,16 +90,17 @@ export default async function home () { ## 环境准备工作 -Midway 运行请预先安装 Node.js 环境和 npm,在国内可以使用 cnpm。 +Midway 运行请预先安装 Node.js 环境和 npm,在国内可以修改 npm 源,使用 pnpm 等。 - 操作系统:支持 macOS,Linux,Windows -- 运行环境:建议选择 [LTS 版本](http://nodejs.org/),最低要求 **12.11.0**。 +- 运行环境:建议选择 [LTS 版本](http://nodejs.org/),最低要求 **18.0.0**。 在经过不断迭代之后,Midway 的版本要求如下: | Midway 版本 | 开发环境 Node.js 版本要求 | 部署环境 Node.js 版本要求 | | ------------- | ------------------------- | ------------------------- | +| >=v4.0.0 | >= v18,推荐 LTS 版本 | >= v18.0.0 | | >=v3.9.0 | >= v14,推荐 LTS 版本 | >= v12.11.0 | | 3.0.0 ~ 3.9.0 | >= v12,推荐 LTS 版本 | >= v12.0.0 | | 2.x | >= v12,推荐 LTS 版本 | >= v10.0.0 | diff --git a/site/docs/midway_component.md b/site/docs/midway_component.md index d6e51ac23f34..475e29fd3e71 100644 --- a/site/docs/midway_component.md +++ b/site/docs/midway_component.md @@ -2,11 +2,28 @@ 组件是 Midway 的扩展机制,我们会将复用的业务代码,或者逻辑,抽象的公共的能力开发成组件,使得这些代码能够在所有的 Midway 场景下复用。 +## 组件结构 + +普通项目本身也是一个组件,组件的入口文件一般是 `src/index.ts` 或者 `src/configuration.ts`。 + +其中会导出一个带有 `@Configuration` 装饰器的类。 + +比如: + +```typescript +// 应用或者函数的 src/configuration.ts +import { Configuration } from '@midwayjs/core'; + +@Configuration({ + // ... +}) +export class MainConfiguration {} +``` ## 启用组件 -组件一般以 npm 包形式进行复用。每个组件都是一个可以被直接 `require` 的代码包。我们以 `@midwayjs/validate` 组件为例。 +组件一般以 npm 包形式进行复用。每个组件都是一个可以被直接 `require` 或者 `import` 的代码包。我们以 `@midwayjs/validate` 组件为例。 首先,在应用中加入依赖。 @@ -14,7 +31,7 @@ // package.json { "dependencies": { - "@midwayjs/validate": "^3.0.0" + "@midwayjs/validate": "^4.0.0" } } ``` @@ -32,14 +49,13 @@ import * as validate from '@midwayjs/validate'; export class MainConfiguration {} ``` - - ## 不同环境启用组件 有时候,我们需要在特殊环境下才使用组件,比如本地开发时。 `imports` 属性可以传入对象数组,我们可以在对象中针对组件启用的环境进行配置。 比如常用的 `info` 组件,为了安全考虑,我们就可以只让他在本地环境启用。 + ```typescript // 应用或者函数的 src/configuration.ts import { Configuration } from '@midwayjs/core'; @@ -60,7 +76,6 @@ export class MainConfiguration {} - `enabledEnvironment` 组件启用的环境数组 - ## 开发组件 参见文档:[组件开发](component_development)。 diff --git a/site/docs/sidebars.json b/site/docs/sidebars.json index 388d406a299f..06bb30c4adf8 100644 --- a/site/docs/sidebars.json +++ b/site/docs/sidebars.json @@ -9,7 +9,7 @@ "intro", "quick_guide", "how_to_update_midway", - "upgrade_v3" + "upgrade_v4" ] }, { diff --git a/site/docs/upgrade_v4.md b/site/docs/upgrade_v4.md new file mode 100644 index 000000000000..123a2b3cb285 --- /dev/null +++ b/site/docs/upgrade_v4.md @@ -0,0 +1,177 @@ + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# 3.x 升级指南 + +本篇将介绍从 midway v3 升级为 midway v4 的方式。 + +从 Midway v3 升级到 Midway v4,会有一些 Breaking Change。本篇文档会详细列出这些 Breaking 的地方,让用户可以提前知道变化,做出应对。 + + + +:::tip + +midway v4 支持从 node v18 起。 + +::: + + +## 包版本更新 + +所有的组件包,核心包都将升级为 4.x 版本。 + +```diff +{ + "dependencies": { +- "@midwayjs/bootstrap": "^3.0.0", +- "@midwayjs/core": "^3.0.0", +- "@midwayjs/koa": "^3.0.0", ++ "@midwayjs/bootstrap": "^4.0.0", ++ "@midwayjs/core": "^4.0.0", ++ "@midwayjs/koa": "^4.0.0", + }, + "devDependencies": { +- "@midwayjs/mock": "^3.0.0", ++ "@midwayjs/mock": "^4.0.0", + // ... + } +} + +``` + +`@midwyajs/luckeye`, `@midwayjs/logger` 的版本除外。 + + +## 移除 `@midwayjs/decorator` + +从 v4 开始,移除了 `@midwayjs/decorator` 包,用户可以直接使用 `@midwayjs/core` 来代替。 + + +## 入口自动扫描能力 + +从 v4 开始,框架移除了隐式的自动扫描能力,建议用户显式的声明需要扫描的目录。 + +```typescript +import { Configuration, CommonJSFileDetector } from '@midwayjs/core'; + +@Configuration({ + detector: new CommonJSFileDetector(), + // ... +}) +export class MainContainer { + // ... +} +``` + +## 主框架日志配置 + +从 v4 开始,主框架日志格式回归到 midwayLogger 配置中。 + + + + +```diff +// *.config.ts +export default { + koa: { +- contextLoggerFormat: info => { +- const ctx = info.ctx; +- return `${info.timestamp} ${info.LEVEL} ${info.pid} [${ctx.userId} - ${Date.now() - ctx.startTime}ms ${ctx.method}] ${info.message}`; +- } + }, + midwayLogger: { + clients: { + appLogger: { ++ contextFormat: info => { ++ const ctx = info.ctx; ++ return `${info.timestamp} ${info.LEVEL} ${info.pid} [${ctx.userId} - ${Date.now() - ctx.startTime}ms ${ctx.method}] ${info.message}`; ++ } + } + } + }, +} as MidwayConfig; +``` + + + + + +```diff +// *.config.ts +export default { + express: { +- contextLoggerFormat: info => { +- const ctx = info.ctx; +- return `${info.timestamp} ${info.LEVEL} ${info.pid} [${ctx.userId} - ${Date.now() - ctx.startTime}ms ${ctx.method}] ${info.message}`; +- } + }, + midwayLogger: { + clients: { + appLogger: { ++ contextFormat: info => { ++ const ctx = info.ctx; ++ return `${info.timestamp} ${info.LEVEL} ${info.pid} [${ctx.userId} - ${Date.now() - ctx.startTime}ms ${ctx.method}] ${info.message}`; ++ } + } + } + }, +} as MidwayConfig; +``` + + + + + +```diff +// *.config.ts +export default { + egg: { +- contextLoggerFormat: info => { +- const ctx = info.ctx; +- return `${info.timestamp} ${info.LEVEL} ${info.pid} [${ctx.userId} - ${Date.now() - ctx.startTime}ms ${ctx.method}] ${info.message}`; +- } + }, + midwayLogger: { + clients: { + appLogger: { ++ contextFormat: info => { ++ const ctx = info.ctx; ++ return `${info.timestamp} ${info.LEVEL} ${info.pid} [${ctx.userId} - ${Date.now() - ctx.startTime}ms ${ctx.method}] ${info.message}`; ++ } + } + } + }, +} as MidwayConfig; +``` + + + + + +```diff +// *.config.ts +export default { + bull: { +- contextLoggerFormat: info => { +- const { jobId, from } = info.ctx; +- return `${info.timestamp} ${info.LEVEL} ${info.pid} [${jobId} ${from.name}] ${info.message}`; +- } + }, + midwayLogger: { + clients: { + bullLogger: { ++ contextFormat: info => { ++ const { jobId, from } = info.ctx; ++ return `${info.timestamp} ${info.LEVEL} ${info.pid} [${jobId} ${from.name}] ${info.message}`;`; ++ } + } + } + }, +} as MidwayConfig; +``` + + + + +其余的 `cron`、`mqtt`, `kafka` 等组件,如有相关配置,均和上述类似。 diff --git a/site/i18n/en/code.json b/site/i18n/en/code.json index 9d2cff36d73b..158f5a000a07 100644 --- a/site/i18n/en/code.json +++ b/site/i18n/en/code.json @@ -144,39 +144,39 @@ "message": "Version: {versionLabel}" }, "theme.docs.versions.unreleasedVersionLabel": { - "message": "Ceci est la documentation de la prochaine version {versionLabel} de {siteTitle}.", + "message": "This is the documentation for the upcoming {versionLabel} version of {siteTitle}.", "description": "The label used to tell the user that he's browsing an unreleased doc version" }, "theme.docs.versions.unmaintainedVersionLabel": { - "message": "Ceci est la documentation de {siteTitle} {versionLabel}, qui n'est plus activement maintenue.", + "message": "This is the documentation for {siteTitle} {versionLabel}, which is no longer actively maintained.", "description": "The label used to tell the user that he's browsing an unmaintained doc version" }, "theme.docs.versions.latestVersionSuggestionLabel": { - "message": "Pour une documentation à jour, consultez la {latestVersionLink} ({versionLabel}).", + "message": "For up-to-date documentation, see the {latestVersionLink} ({versionLabel}).", "description": "The label used to tell the user to check the latest version" }, "theme.docs.versions.latestVersionLinkLabel": { - "message": "dernière version", + "message": "latest version", "description": "The label used for the latest version suggestion link label" }, "theme.common.editThisPage": { - "message": "Éditer cette page", + "message": "Edit this page", "description": "The link label to edit the current page" }, "theme.common.headingLinkTitle": { - "message": "Lien direct vers le titre", + "message": "Direct link to heading", "description": "Title for link to heading" }, "theme.lastUpdated.atDate": { - "message": " le {date}", + "message": " on {date}", "description": "The words used to describe on which date a page has been last updated" }, "theme.lastUpdated.byUser": { - "message": " par {user}", + "message": " by {user}", "description": "The words used to describe by who the page has been last updated" }, "theme.lastUpdated.lastUpdatedAtBy": { - "message": "Dernière mise à jour{atDate}{byUser}", + "message": "Last updated{atDate}{byUser}", "description": "The sentence used to display when a page has been last updated, and by who" }, "theme.navbar.mobileVersionsDropdown.label": { @@ -184,210 +184,210 @@ "description": "The label for the navbar versions dropdown on mobile view" }, "theme.common.skipToMainContent": { - "message": "Aller au contenu principal", + "message": "Skip to main content", "description": "The skip to content label used for accessibility, allowing to rapidly navigate to main content with keyboard tab/enter navigation" }, "theme.tags.tagsListLabel": { - "message": "Tags :", + "message": "Tags:", "description": "The label alongside a tag list" }, "theme.AnnouncementBar.closeButtonAriaLabel": { - "message": "Fermer", + "message": "Close", "description": "The ARIA label for close button of announcement bar" }, "theme.blog.sidebar.navAriaLabel": { - "message": "Navigation article de blog récent", + "message": "Recent blog posts navigation", "description": "The ARIA label for recent posts in the blog sidebar" }, "theme.CodeBlock.copied": { - "message": "Copié", + "message": "Copied", "description": "The copied button label on code blocks" }, "theme.CodeBlock.copyButtonAriaLabel": { - "message": "Copier le code", + "message": "Copy code", "description": "The ARIA label for copy code blocks button" }, "theme.CodeBlock.copy": { - "message": "Copier", + "message": "Copy", "description": "The copy button label on code blocks" }, "theme.CodeBlock.wordWrapToggle": { - "message": "Activer/désactiver le retour à la ligne", + "message": "Toggle word wrap", "description": "The title attribute for toggle word wrapping button of code block lines" }, "theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": { - "message": "Plier/Déplier la catégorie '{label}' de la barre latérale", + "message": "Toggle the collapsible sidebar category '{label}'", "description": "The ARIA label to toggle the collapsible sidebar category" }, "theme.TOCCollapsible.toggleButtonLabel": { - "message": "Sur cette page", + "message": "On this page", "description": "The label used by the button on the collapsible TOC component" }, "theme.blog.post.readMore": { - "message": "Lire plus", + "message": "Read More", "description": "The label used in blog post item excerpts to link to full blog posts" }, "theme.blog.post.readMoreLabel": { - "message": "En savoir plus sur {title}", + "message": "Read more about {title}", "description": "The ARIA label for the link to full blog posts from excerpts" }, "theme.navbar.mobileLanguageDropdown.label": { - "message": "Langues", + "message": "Languages", "description": "The label for the mobile language switcher dropdown" }, "theme.blog.post.readingTime.plurals": { - "message": "Une minute de lecture|{readingTime} minutes de lecture", + "message": "One minute read|{readingTime} minute read", "description": "Pluralized label for \"{readingTime} min read\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" }, "theme.docs.sidebar.collapseButtonTitle": { - "message": "Réduire le menu latéral", + "message": "Collapse sidebar", "description": "The title attribute for collapse button of doc sidebar" }, "theme.docs.sidebar.collapseButtonAriaLabel": { - "message": "Réduire le menu latéral", + "message": "Collapse sidebar", "description": "The title attribute for collapse button of doc sidebar" }, "theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": { - "message": "← Retour au menu principal", + "message": "← Back to main menu", "description": "The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)" }, "theme.docs.sidebar.expandButtonTitle": { - "message": "Déplier le menu latéral", + "message": "Expand sidebar", "description": "The ARIA label and title attribute for expand button of doc sidebar" }, "theme.docs.sidebar.expandButtonAriaLabel": { - "message": "Déplier le menu latéral", + "message": "Expand sidebar", "description": "The ARIA label and title attribute for expand button of doc sidebar" }, "theme.SearchBar.seeAll": { - "message": "Voir les {count} résultats" + "message": "See all {count} results" }, "theme.SearchBar.label": { - "message": "Chercher", + "message": "Search", "description": "The ARIA label and placeholder for search button" }, "theme.SearchModal.searchBox.resetButtonTitle": { - "message": "Effacer la requête", + "message": "Clear the query", "description": "The label and ARIA label for search box reset button" }, "theme.SearchModal.searchBox.cancelButtonText": { - "message": "Annuler", + "message": "Cancel", "description": "The label and ARIA label for search box cancel button" }, "theme.SearchModal.startScreen.recentSearchesTitle": { - "message": "Récemment", + "message": "Recent", "description": "The title for recent searches" }, "theme.SearchModal.startScreen.noRecentSearchesText": { - "message": "Aucune recherche récente", + "message": "No recent searches", "description": "The text when no recent searches" }, "theme.SearchModal.startScreen.saveRecentSearchButtonTitle": { - "message": "Sauvegarder cette recherche", + "message": "Save this search", "description": "The label for save recent search button" }, "theme.SearchModal.startScreen.removeRecentSearchButtonTitle": { - "message": "Supprimer cette recherche de l'historique", + "message": "Remove this search from history", "description": "The label for remove recent search button" }, "theme.SearchModal.startScreen.favoriteSearchesTitle": { - "message": "Favoris", + "message": "Favorites", "description": "The title for favorite searches" }, "theme.SearchModal.startScreen.removeFavoriteSearchButtonTitle": { - "message": "Supprimer cette recherche des favoris", + "message": "Remove this search from favorites", "description": "The label for remove favorite search button" }, "theme.SearchModal.errorScreen.titleText": { - "message": "Impossible de récupérer les résultats", + "message": "Unable to fetch results", "description": "The title for error screen of search modal" }, "theme.SearchModal.errorScreen.helpText": { - "message": "Vous pouvez vérifier votre connexion réseau.", + "message": "You might want to check your network connection.", "description": "The help text for error screen of search modal" }, "theme.SearchModal.footer.selectText": { - "message": "sélectionner", + "message": "select", "description": "The explanatory text of the action for the enter key" }, "theme.SearchModal.footer.selectKeyAriaLabel": { - "message": "Touche Entrée", + "message": "Enter key", "description": "The ARIA label for the Enter key button that makes the selection" }, "theme.SearchModal.footer.navigateText": { - "message": "naviguer", + "message": "navigate", "description": "The explanatory text of the action for the Arrow up and Arrow down key" }, "theme.SearchModal.footer.navigateUpKeyAriaLabel": { - "message": "Flèche vers le haut", + "message": "Arrow up", "description": "The ARIA label for the Arrow up key button that makes the navigation" }, "theme.SearchModal.footer.navigateDownKeyAriaLabel": { - "message": "Flèche vers le bas", + "message": "Arrow down", "description": "The ARIA label for the Arrow down key button that makes the navigation" }, "theme.SearchModal.footer.closeText": { - "message": "fermer", + "message": "close", "description": "The explanatory text of the action for Escape key" }, "theme.SearchModal.footer.closeKeyAriaLabel": { - "message": "Touche Echap", + "message": "Escape key", "description": "The ARIA label for the Escape key button that close the modal" }, "theme.SearchModal.footer.searchByText": { - "message": "Recherche via", + "message": "Search by", "description": "The text explain that the search is making by Algolia" }, "theme.SearchModal.noResultsScreen.noResultsText": { - "message": "Aucun résultat pour", + "message": "No results for", "description": "The text explains that there are no results for the following search" }, "theme.SearchModal.noResultsScreen.suggestedQueryText": { - "message": "Essayez de chercher", + "message": "Try searching for", "description": "The text for the suggested query when no results are found for the following search" }, "theme.SearchModal.noResultsScreen.reportMissingResultsText": { - "message": "Vous pensez que cette requête doit donner des résultats ?", + "message": "Think this query should return results?", "description": "The text for the question where the user thinks there are missing results" }, "theme.SearchModal.noResultsScreen.reportMissingResultsLinkText": { - "message": "Faites-le nous savoir.", + "message": "Let us know.", "description": "The text for the link to report missing results" }, "theme.SearchModal.placeholder": { - "message": "Rechercher des docs", + "message": "Search docs", "description": "The placeholder of the input of the DocSearch pop-up modal" }, "theme.SearchPage.documentsFound.plurals": { - "message": "Un document trouvé|{count} documents trouvés", + "message": "One document found|{count} documents found", "description": "Pluralized label for \"{count} documents found\". Use as much plural forms (separated by \"|\") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)" }, "theme.SearchPage.existingResultsTitle": { - "message": "Résultats de recherche pour « {query} »", + "message": "Search results for \"{query}\"", "description": "The search page title for non-empty query" }, "theme.SearchPage.emptyResultsTitle": { - "message": "Rechercher dans la documentation", + "message": "Search the documentation", "description": "The search page title for empty query" }, "theme.SearchPage.inputPlaceholder": { - "message": "Tapez votre recherche ici", + "message": "Type your search here", "description": "The placeholder for search page input" }, "theme.SearchPage.inputLabel": { - "message": "Chercher", + "message": "Search", "description": "The ARIA label for search page input" }, "theme.SearchPage.algoliaLabel": { - "message": "Recherche par Algolia", + "message": "Search by Algolia", "description": "The ARIA label for Algolia mention" }, "theme.SearchPage.noResultsText": { - "message": "Aucun résultat trouvé", + "message": "No results found", "description": "The paragraph for empty search result" }, "theme.SearchPage.fetchingNewResults": { - "message": "Chargement de nouveaux résultats...", + "message": "Loading new results...", "description": "The paragraph for fetching new search results" }, "theme.tags.tagsPageTitle": { diff --git a/site/i18n/en/docusaurus-plugin-content-docs/current.json b/site/i18n/en/docusaurus-plugin-content-docs/current.json index 32cc5f86ba28..44b373bc96cc 100644 --- a/site/i18n/en/docusaurus-plugin-content-docs/current.json +++ b/site/i18n/en/docusaurus-plugin-content-docs/current.json @@ -1,6 +1,6 @@ { "version.label": { - "message": "3.0.0", + "message": "4.0.0", "description": "The label for version current" }, "sidebar.common.category.新手指南": { diff --git a/site/i18n/en/docusaurus-plugin-content-docs/current/container.md b/site/i18n/en/docusaurus-plugin-content-docs/current/container.md index dc56f307975f..7289ae03f44c 100644 --- a/site/i18n/en/docusaurus-plugin-content-docs/current/container.md +++ b/site/i18n/en/docusaurus-plugin-content-docs/current/container.md @@ -1,3 +1,6 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + # Dependency injection Midway uses a lot of dependency injection features. Through the lightweight features of decorators, dependency injection becomes elegant, thus making the development process convenient and interesting. @@ -476,10 +479,17 @@ Midway supports injection in many ways. Export a Class, the type of injection uses Class, which is the simplest way to inject, and most businesses and components use this way. +There are two standard forms of injection, **property injection** and **constructor injection**. + + + + +Midway will automatically use B as the type of property b and instantiate it in the container. + ```typescript import { Provide, Inject } from '@midwayjs/core'; -@Provide() // <------ Expose a Class +@Provide() // <------ Expose a Class export class B { //... } @@ -488,32 +498,56 @@ export class B { export class A { @Inject() - B: B; // <------ The attribute here uses Class + b: B; // <------ The attribute here uses Class //... } ``` -Midway will automatically use B as the type of the attribute B and instantiate it in the container. + -In this case, Midway automatically creates a unique uuid to associate with the class. This uuid is called a **dependency injection identifier**. + +In version `4.0.0`, we restored the constructor injection feature, allowing users to explicitly inject dependencies in the constructor. -Default: +```typescript +import { Provide, Inject } from '@midwayjs/core'; +@Provide() +export class B { + //... +} -- 1. `@Provide` will automatically generate a uuid as the dependency injection identifier -- 2. `@Inject` searches for the uuid of the type. +@Provide() +export class A { + private b: B; + + constructor(@Inject() b: B) { + this.b = b; + } +} +``` -If you want to get this uuid, you can use the following API. +Midway will automatically use B as the type of parameter b and instantiate it in the container. -```typescript -import { getProviderUUId } from '@midwayjs/core'; +In TypeScript, you can also use a simplified syntax where parameters with access modifiers automatically create corresponding properties. -const uuid = getProviderUUId(B); -// ... +```typescript +@Provide() +export class A { + constructor(protected @Inject() b: B) {} +} ``` +:::tip + +Only the `@Inject` decorator supports constructor injection. + +::: + + + + ### Injection Based on fixed name @@ -677,6 +711,77 @@ export class BaseService { ``` +## Circular Dependencies + +If the project is large, circular dependencies may occur. + +For example, A depends on B, B depends on C, and C depends on A. + +```typescript +@Provide() +export class A { + @Inject() + b: B; +} + +@Provide() +export class B { + @Inject() + c: C; +} + +@Provide() +export class C { + @Inject() + a: A; +} +``` + +The above code will throw an error because A depends on B, B depends on C, and C depends on A, forming a circular dependency. + +### Detecting Circular Dependencies + +In version `4.0.0`, Midway will automatically detect circular dependencies and throw an error. + +You will see an error message similar to the following: + +```typescript +Circular dependency detected: A -> B -> C -> A +``` + +### Resolving Circular Dependencies + +You can resolve circular dependencies using the `@LazyInject` decorator provided by the framework. + +```typescript +import { LazyInject, Inject, Provide } from '@midwayjs/core'; + +@Provide() +export class A { + @Inject() + b: B; +} + +@Provide() +export class B { + @Inject() + c: C; +} + +@Provide() +export class C { + @LazyInject(() => A) + a: A; +} +``` + +:::tip + +Only use the `@LazyInject` decorator when you need to resolve circular dependencies. + +::: + + ## Get the dependency injection container @@ -1073,7 +1178,7 @@ export const getGlobalConfig = () => { ## Start Behavior -### Automatic scan binding +### Automatic scanning and binding As mentioned above, after the container is initialized, we will bind the existing class registration to the container. @@ -1083,38 +1188,215 @@ container.bind(UserController); container.bind(UserService); ``` -Midway will automatically scan the entire project directory during the startup process and automatically process this behavior, so that the user does not need to manually perform binding operations. +In version `4.0.0`, we introduced the concept of `detector` for scanning files and binding. Simply put, the framework will recursively scan the ts/js files in the entire `src` directory by default, and then perform require operations. When the file is exported as a class and explicitly or implicitly contains the `@Provide()` decorator, it will execute the `container.bind` logic. -### Ignore scanning +The following logic explicitly declares the file loading behavior, and users can customize the file detector to implement different file loading behaviors. -In general, we should not put non-ts files under src (such as front-end code). In special scenarios, we can ignore some directories and configure them in the `@Configuration` decorator. +```typescript +// src/configuration.ts +import { Configuration, CommonjsFileDetector } from '@midwayjs/core'; -An example is as follows: +@Configuration({ + detector: new CommonjsFileDetector(), +}) +export class MainConfiguration {} +``` + +### File Detector + +Through the `detector` configuration, we can customize the file loading behavior. + +The framework provides two file loaders, `CommonJSFileDetector` and `ESModuleFileDetector`, for loading Commonjs and ESM format files respectively. + + + + +If you want to load Commonjs format files, you can configure it like this: ```typescript // src/configuration.ts -import { App, Configuration, Logger } from '@midwayjs/core'; -// ... +import { Configuration, CommonJSFileDetector } from '@midwayjs/core'; @Configuration({ - // ... - detectorOptions: { + detector: new CommonJSFileDetector(), +}) +export class MainConfiguration {} +``` + + + + + +If you want to load ESM format files, you can configure it like this: + +```typescript +// src/configuration.ts +import { Configuration, ESModuleFileDetector } from '@midwayjs/core'; + +@Configuration({ + detector: new ESModuleFileDetector(), +}) +export class MainConfiguration {} +``` + + + + +By default, the following files in the `src` directory will be scanned and automatically processed and bound. + +```typescript +export const DEFAULT_PATTERN = [ + '**/**.tsx', + '**/**.ts', + '**/**.js', + '**/**.mts', + '**/**.mjs', + '**/**.cts', + '**/**.cjs', +]; +``` + +At the same time, the following directories and files are ignored: + +```typescript +export const DEFAULT_IGNORE_PATTERN = [ + '**/logs/**', + '**/run/**', + '**/public/**', + '**/app/view/**', + '**/app/views/**', + '**/app/extend/**', + '**/node_modules/**', + '**/**.test.ts', + '**/**.test.js', + '**/__test__/**', + '**/**.d.ts', + '**/**.d.mts', + '**/**.d.cts' +]; +``` + +Based on the above default configuration, we can adjust the file scanning behavior through some configurations. + +If we want to ignore certain directories or types when scanning, we can configure `ignore` to achieve this. + + + + +```typescript +// src/configuration.ts +import { Configuration, CommonJSFileDetector } from '@midwayjs/core'; + +@Configuration({ + detector: new CommonJSFileDetector({ ignore: [ - '**/web/**' - ] - } + '**/logs/**', + ], + }), }) -export class MainConfiguration { - // ... -} +export class MainConfiguration {} +``` + + + + + +```typescript +// src/configuration.ts +import { Configuration, ESModuleFileDetector } from '@midwayjs/core'; +@Configuration({ + detector: new ESModuleFileDetector({ + ignore: [ + '**/logs/**', + ], + }), +}) +export class MainConfiguration {} ``` + + + +You can add file extensions and formats to scan through the `pattern` configuration. + + +```typescript +// src/configuration.ts +import { Configuration, CommonJSFileDetector } from '@midwayjs/core'; + +@Configuration({ + detector: new CommonJSFileDetector({ + pattern: [ + '**/**.jsx' + ], + }), +}) +export class MainConfiguration {} +``` + + + + + +```typescript +// src/configuration.ts +import { Configuration, ESModuleFileDetector } from '@midwayjs/core'; + +@Configuration({ + detector: new ESModuleFileDetector({ + pattern: [ + '**/**.jsx' + ], + }), +}) +export class MainConfiguration {} +``` + + + + +Use `conflictCheck` to handle exported class name checks. By default, if the same name is detected for export, an error will be thrown. + +You can turn off this check by configuring `conflictCheck`. + + + + +```typescript +// src/configuration.ts +import { Configuration, CommonJSFileDetector } from '@midwayjs/core'; + +@Configuration({ + detector: new CommonJSFileDetector({ + conflictCheck: false, + }), +}) +export class MainConfiguration {} +``` + + + + + +```typescript +// src/configuration.ts +import { Configuration, ESModuleFileDetector } from '@midwayjs/core'; + +@Configuration({ + detector: new ESModuleFileDetector({ + conflictCheck: false, + }), +}) +export class MainConfiguration {} +``` + + ## Object lifecycle diff --git a/site/i18n/en/docusaurus-plugin-content-docs/current/intro.md b/site/i18n/en/docusaurus-plugin-content-docs/current/intro.md index dc2826b4c31d..246d8e018484 100644 --- a/site/i18n/en/docusaurus-plugin-content-docs/current/intro.md +++ b/site/i18n/en/docusaurus-plugin-content-docs/current/intro.md @@ -94,15 +94,16 @@ Please install Node.js environment and npm in advance to run Midway. cnpm can be - Operating system: supports macOS,Linux,Windows -- Running environment: We recommend that you select [LTS](http://nodejs.org/). The minimum requirement is **12.11.0**. +- Running environment: We recommend that you select [LTS](http://nodejs.org/). The minimum requirement is **18.0.0**. After continuous iteration, Midway's version requirements are as follows: | Midway Version | Development environment Node.js version requirements | Deployment environment Node.js version requirements | | -------------- | ---------------------------------------------------- | --------------------------------------------------- | -| >=v3.9.0 | >= v14, LTS version recommended | >= v12.11.0 | -| 3.0.0 ~ 3.9.0 | >= v12, LTS version recommended | >= v12.0.0 | -| 2.x | >= v12, LTS version recommended | >= v10.0.0 | +| >=v4.0.0 | >= v18, LTS version recommended | >= v18.0.0 | +| >=v3.9.0 | >= v14, LTS version recommended | >= v12.11.0 | +| 3.0.0 ~ 3.9.0 | >= v12, LTS version recommended | >= v12.0.0 | +| 2.x | >= v12, LTS version recommended | >= v10.0.0 | For more information, see [How to install the Node.js environment](how_to_install_nodejs). diff --git a/site/i18n/en/docusaurus-plugin-content-docs/current/midway_component.md b/site/i18n/en/docusaurus-plugin-content-docs/current/midway_component.md index 1a1a8dc2948b..a9207519ba75 100644 --- a/site/i18n/en/docusaurus-plugin-content-docs/current/midway_component.md +++ b/site/i18n/en/docusaurus-plugin-content-docs/current/midway_component.md @@ -2,11 +2,27 @@ Components are Midway's extension mechanism. We will develop reusable business code, or logical and abstract common capabilities into components, so that these codes can be reused in all Midway scenarios. +## Component Structure +The normal project itself is also a component, and the entry file of the component is generally `src/index.ts` or `src/configuration.ts`. + +It will export a class with the `@Configuration` decorator. + +For example: + +```typescript +// src/configuration.ts of application or function +import { Configuration } from '@midwayjs/core'; + +@Configuration({ + // ... +}) +export class MainConfiguration {} +``` ## Enable components -Components are generally reused in the form of npm packages. Each component is a package of code that can be `required` directly. Let's take the `@midwayjs/validate` component as an example. +Components are generally reused in the form of npm packages. Each component is a package of code that can be `required` or `imported` directly. Let's take the `@midwayjs/validate` component as an example. First, add dependencies to the application. @@ -14,7 +30,7 @@ First, add dependencies to the application. // package.json { "dependencies": { - "@midwayjs/validate": "^3.0.0" + "@midwayjs/validate": "^4.0.0" } } ``` diff --git a/site/i18n/en/docusaurus-plugin-content-docs/current/upgrade_v4.md b/site/i18n/en/docusaurus-plugin-content-docs/current/upgrade_v4.md new file mode 100644 index 000000000000..966e265e5fd1 --- /dev/null +++ b/site/i18n/en/docusaurus-plugin-content-docs/current/upgrade_v4.md @@ -0,0 +1,168 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Upgrading to v4 + +This guide will help you upgrade from Midway v3 to Midway v4. + +There are some breaking changes when upgrading from Midway v3 to Midway v4. This document will list these breaking changes in detail so users can be aware of the changes and prepare accordingly. + +:::tip +Midway v4 supports Node.js v18 and above. +::: + +## Package Version Updates + +All component packages and core packages will be upgraded to version 4.x. + +```diff +{ + "dependencies": { +- "@midwayjs/bootstrap": "^3.0.0", +- "@midwayjs/core": "^3.0.0", +- "@midwayjs/koa": "^3.0.0", ++ "@midwayjs/bootstrap": "^4.0.0", ++ "@midwayjs/core": "^4.0.0", ++ "@midwayjs/koa": "^4.0.0", + }, + "devDependencies": { +- "@midwayjs/mock": "^3.0.0", ++ "@midwayjs/mock": "^4.0.0", + // ... + } +} +``` + +Except for `@midwyajs/luckeye` and `@midwayjs/logger` versions. + +## Removal of `@midwayjs/decorator` + +Starting from v4, the `@midwayjs/decorator` package has been removed. Users can directly use `@midwayjs/core` instead. + +## Automatic Entry Scanning Capability + +Starting from v4, the framework removes implicit automatic scanning capability. It is recommended that users explicitly declare the directories that need to be scanned. + +```typescript +import { Configuration, CommonJSFileDetector } from '@midwayjs/core'; + +@Configuration({ + detector: new CommonJSFileDetector(), + // ... +}) +export class MainContainer { + // ... +} +``` + +## Main Framework Log Configuration + +Starting from v4, the main framework log format returns to the midwayLogger configuration. + + + + +```diff +// *.config.ts +export default { + koa: { +- contextLoggerFormat: info => { +- const ctx = info.ctx; +- return `${info.timestamp} ${info.LEVEL} ${info.pid} [${ctx.userId} - ${Date.now() - ctx.startTime}ms ${ctx.method}] ${info.message}`; +- } + }, + midwayLogger: { + clients: { + appLogger: { ++ contextFormat: info => { ++ const ctx = info.ctx; ++ return `${info.timestamp} ${info.LEVEL} ${info.pid} [${ctx.userId} - ${Date.now() - ctx.startTime}ms ${ctx.method}] ${info.message}`; ++ } + } + } + }, +} as MidwayConfig; +``` + + + + + +```diff +// *.config.ts +export default { + express: { +- contextLoggerFormat: info => { +- const ctx = info.ctx; +- return `${info.timestamp} ${info.LEVEL} ${info.pid} [${ctx.userId} - ${Date.now() - ctx.startTime}ms ${ctx.method}] ${info.message}`; +- } + }, + midwayLogger: { + clients: { + appLogger: { ++ contextFormat: info => { ++ const ctx = info.ctx; ++ return `${info.timestamp} ${info.LEVEL} ${info.pid} [${ctx.userId} - ${Date.now() - ctx.startTime}ms ${ctx.method}] ${info.message}`; ++ } + } + } + }, +} as MidwayConfig; +``` + + + + + +```diff +// *.config.ts +export default { + egg: { +- contextLoggerFormat: info => { +- const ctx = info.ctx; +- return `${info.timestamp} ${info.LEVEL} ${info.pid} [${ctx.userId} - ${Date.now() - ctx.startTime}ms ${ctx.method}] ${info.message}`; +- } + }, + midwayLogger: { + clients: { + appLogger: { ++ contextFormat: info => { ++ const ctx = info.ctx; ++ return `${info.timestamp} ${info.LEVEL} ${info.pid} [${ctx.userId} - ${Date.now() - ctx.startTime}ms ${ctx.method}] ${info.message}`; ++ } + } + } + }, +} as MidwayConfig; +``` + + + + + +```diff +// *.config.ts +export default { + bull: { +- contextLoggerFormat: info => { +- const { jobId, from } = info.ctx; +- return `${info.timestamp} ${info.LEVEL} ${info.pid} [${jobId} ${from.name}] ${info.message}`; +- } + }, + midwayLogger: { + clients: { + bullLogger: { ++ contextFormat: info => { ++ const { jobId, from } = info.ctx; ++ return `${info.timestamp} ${info.LEVEL} ${info.pid} [${jobId} ${from.name}] ${info.message}`;`; ++ } + } + } + }, +} as MidwayConfig; +``` + + + + +Similar changes apply to other components like `cron`, `mqtt`, `kafka`, etc., if they have related configurations. \ No newline at end of file