Skip to content
我们的赞助商

关键概念 MUST READ

Elysia 有一些非常重要的概念,您需要理解才能使用它。

本页涵盖了您在开始使用前应该了解的大多数概念。

封装 MUST READ

Elysia 的生命周期方法仅封装在其自己的实例中。

这意味着如果您创建一个新实例,它不会与其他实例共享生命周期方法。

ts
import { Elysia } from 'elysia'

const profile = new Elysia()
	.onBeforeHandle(({ cookie }) => {
		throwIfNotSignIn(cookie)
	})
	.get('/profile', () => 'Hi there!')

const app = new Elysia()
	.use(profile)
	// ⚠️ 这将没有登录检查
	.patch('/rename', ({ body }) => updateProfile(body))

在这个示例中,isSignIn 检查仅适用于 profile,而不适用于 app

localhost

GET

尝试在 URL 栏中更改路径为 /rename 并查看结果


Elysia 默认隔离生命周期,除非明确指定。这类似于 JavaScript 中的 export,您需要导出函数才能使其在模块外部可用。

要将生命周期**“导出”**到其他实例,您必须指定作用域。

ts
import { Elysia } from 'elysia'

const profile = new Elysia()
	.onBeforeHandle(
		{ as: 'global' }, 
		({ cookie }) => {
			throwIfNotSignIn(cookie)
		}
	)
	.get('/profile', () => 'Hi there!')

const app = new Elysia()
	.use(profile)
	// 这有登录检查
	.patch('/rename', ({ body }) => updateProfile(body))
localhost

GET

将生命周期转换为 "global" 将将其导出到每个实例

了解更多信息,请参阅 scope

方法链 Important

Elysia 代码应始终使用方法链。

这对于确保类型安全非常重要。

typescript
import { 
Elysia
} from 'elysia'
new
Elysia
()
.
state
('build', 1)
// Store is strictly typed .
get
('/', ({
store
: {
build
} }) =>
build
)
.
listen
(3000)

在上面的代码中,state 返回一个新的 ElysiaInstance 类型,添加了一个类型化的 build 属性。

没有方法链的情况

由于 Elysia 类型系统复杂,Elysia 中的每个方法都会返回一个新的类型引用。

如果不使用方法链,Elysia 不会保存这些新类型,导致没有类型推断。

typescript
import { 
Elysia
} from 'elysia'
const
app
= new
Elysia
()
app
.
state
('build', 1)
app
.
get
('/', ({
store
: { build } }) =>
build
)
Property 'build' does not exist on type '{}'.
app
.
listen
(3000)

我们推荐始终使用方法链 以提供准确的类型推断。

依赖 MUST READ

每个插件在应用到另一个实例时都会被重新执行

如果插件被多次应用,它会导致不必要的重复。

重要的是,某些方法如生命周期路由,应该只调用一次。

为了防止这种情况,Elysia 可以使用唯一标识符来去重生命周期。

ts
import { 
Elysia
} from 'elysia'
// `name` 是一个唯一标识符 const
ip
= new
Elysia
({
name
: 'ip' })
.
derive
(
{
as
: 'global' },
({
server
,
request
}) => ({
ip
:
server
?.
requestIP
(
request
)
}) ) .
get
('/ip', ({
ip
}) =>
ip
)
const
router1
= new
Elysia
()
.
use
(
ip
)
.
get
('/ip-1', ({
ip
}) =>
ip
)
const
router2
= new
Elysia
()
.
use
(
ip
)
.
get
('/ip-2', ({
ip
}) =>
ip
)
const
server
= new
Elysia
()
.
use
(
router1
)
.
use
(
router2
)

name 属性添加到实例将使其成为唯一标识符,防止其被多次调用。

了解更多信息,请参阅 plugin deduplication

服务定位器 Important

当您将插件应用到一个实例时,该实例将获得类型安全。

但如果您不将插件应用到另一个实例,它将无法推断类型。

typescript
import { 
Elysia
} from 'elysia'
const
child
= new
Elysia
()
// ❌ 'a' is missing .
get
('/', ({ a }) =>
a
)
Property 'a' does not exist on type '{ body: unknown; query: Record<string, string>; params: {}; headers: Record<string, string | undefined>; cookie: Record<string, Cookie<unknown>>; server: Server | null; ... 6 more ...; status: <const Code extends number | keyof StatusMap, const T = Code extends 100 | ... 59 more ... | 511 ? { ...; }[Code] : Code>(co...'.
const
main
= new
Elysia
()
.
decorate
('a', 'a')
.
use
(
child
)

Elysia 引入了服务定位器模式来抵消这种情况。

只需为 Elysia 提供插件引用,即可找到服务以添加类型安全。

typescript
import { 
Elysia
} from 'elysia'
const
setup
= new
Elysia
({
name
: 'setup' })
.
decorate
('a', 'a')
// Without 'setup', type will be missing const
error
= new
Elysia
()
.
get
('/', ({ a }) =>
a
)
Property 'a' does not exist on type '{ body: unknown; query: Record<string, string>; params: {}; headers: Record<string, string | undefined>; cookie: Record<string, Cookie<unknown>>; server: Server | null; ... 6 more ...; status: <const Code extends number | keyof StatusMap, const T = Code extends 100 | ... 59 more ... | 511 ? { ...; }[Code] : Code>(co...'.
// With `setup`, type will be inferred const
child
= new
Elysia
()
.
use
(
setup
)
.
get
('/', ({
a
}) =>
a
)

这相当于 TypeScript 的 type import,您导入类型而不实际导入要运行的代码。

如前所述,Elysia 已经处理了去重,因此这不会有任何性能损失或生命周期重复。

代码顺序 Important

Elysia 生命周期代码的顺序非常重要。

因为事件仅适用于注册之后的路由。

如果您将 onError 放在插件之前,插件将无法继承 onError 事件。

typescript
import { Elysia } from 'elysia'

new Elysia()
 	.onBeforeHandle(() => {
        console.log('1')
    })
	.get('/', () => 'hi')
    .onBeforeHandle(() => {
        console.log('2')
    })
    .listen(3000)

控制台应记录以下内容:

bash
1

请注意,它没有记录 2,因为事件在路由之后注册,因此不适用于该路由。

了解更多信息,请参阅 order of code

类型推断

Elysia 有一个复杂类型系统,可以从实例推断类型。

ts
import { 
Elysia
,
t
} from 'elysia'
const
app
= new
Elysia
()
.
post
('/', ({
body
}) =>
body
, {
body
:
t
.
Object
({
name
:
t
.
String
()
}) })

您应该始终使用内联函数来提供准确的类型推断。

如果需要应用单独的函数,例如 MVC 的控制器模式,建议从内联函数中解构属性,以防止不必要的类型推断,如下所示:

ts
import { 
Elysia
,
t
} from 'elysia'
abstract class
Controller
{
static
greet
({
name
}: {
name
: string }) {
return 'hello ' +
name
} } const
app
= new
Elysia
()
.
post
('/', ({
body
}) =>
Controller
.
greet
(
body
), {
body
:
t
.
Object
({
name
:
t
.
String
()
}) })

参阅 最佳实践:MVC Controller

TypeScript

我们可以通过访问 static 属性来获取 Elysia/TypeBox 的每个类型的类型定义,如下所示:

ts
import { 
t
} from 'elysia'
const
MyType
=
t
.
Object
({
hello
:
t
.
Literal
('Elysia')
}) type
MyType
= typeof
MyType
.
static



这允许 Elysia 自动推断并提供类型,减少声明重复 schema 的需求。

单个 Elysia/TypeBox schema 可以用于:

  • 运行时验证
  • 数据强制转换
  • TypeScript 类型
  • OpenAPI schema

这使我们可以将 schema 作为单一真相来源