Skip to content

处理程序

¥Handler

一个处理程序(handler)是一个响应每个路由请求的函数。

¥A handler is a function that responds to the request for each route.

接受请求信息并返回响应给客户端。

¥Accepting request information and returning a response to the client.

或者,在其他框架中,处理程序也称为控制器。

¥Alternatively, a handler is also known as a Controller in other frameworks.

typescript
import { Elysia } from 'elysia'

new Elysia()
    // the function `() => 'hello world'` is a handler
    .get('/', () => 'hello world')
    .listen(3000)

处理程序可以是文字值,并且可以内联。

¥A handler may be a literal value, and can be inlined.

typescript
import { Elysia, file } from 'elysia'

new Elysia()
    .get('/', 'Hello Elysia')
    .get('/video', file('kyuukurarin.mp4'))
    .listen(3000)

使用内联值始终返回相同的值,这对于优化文件等静态资源的性能非常有用。

¥Using an inline value always returns the same value which is useful to optimize performance for static resources like files.

这允许 Elysia 提前编译响应以优化性能。

¥This allows Elysia to compile the response ahead of time to optimize performance.

提示

提供内联值并非缓存。

¥Providing an inline value is not a cache.

静态资源值、标头和状态可以使用生命周期进行动态变异。

¥Static resource values, headers and status can be mutated dynamically using lifecycle.

上下文

¥Context

Context 包含每个请求独有的请求信息,除 store (全局可变状态) 外,其他请求信息均不共享。

¥Context contains request information which is unique for each request, and is not shared except for store (global mutable state).

typescript
import { 
Elysia
} from 'elysia'
new
Elysia
()
.
get
('/', (
context
) =>
context
.
path
)
// ^ This is a context

Context 只能在路由处理程序中检索。它包含:

¥Context can only be retrieved in a route handler. It consists of:

  • path - 请求的路径名

  • body - HTTP 消息,表单或文件上传。

  • query - 查询字符串,以 JavaScript 对象的形式包含用于搜索查询的附加参数。(查询从路径名后从 '?' 问号开始的值中提取)

  • params - Elysia 的路径参数解析为 JavaScript 对象

  • headers - HTTP 标头,请求的附加信息,例如 User-Agent、Content-Type、缓存提示。

  • request - Web 标准请求

  • redirect - 一个用于重定向响应的函数

  • store - 一个用于 Elysia 实例的全局可变存储

  • cookie - 一个用于与 Cookie 交互的全局可变信号存储(包括 get/set 方法)

  • set - 应用于响应的属性:

    • status - HTTP 状态,如果未设置,则默认为 200。

    • headers - 响应头

    • redirect - 响应作为重定向到的路径

  • error - 一个用于返回自定义状态码的函数

  • server - Bun 服务器实例

设置

¥Set

set 是一个可变属性,它形成一个可通过 Context.set 访问的响应。

¥set is a mutable property that form a response accessible via Context.set.

  • set.status - 设置自定义状态码

  • set.headers - 附加自定义标头

  • set.redirect - 附加重定向

ts
import { 
Elysia
} from 'elysia'
new
Elysia
()
.
get
('/', ({
set
,
status
}) => {
set
.
headers
= { 'X-Teapot': 'true' }
return
status
(418, 'I am a teapot')
}) .
listen
(3000)

status

我们可以使用以下任一方式返回自定义状态码:

¥We can return a custom status code by using either:

  • 状态函数(推荐)

  • set.status (遗留)

typescript
import { Elysia } from 'elysia'

new Elysia()
	.get('/error', ({ error }) => error(418, 'I am a teapot'))
	.get('/set.status', ({ set }) => {
		set.status = 418
		return 'I am a teapot'
	})
	.listen(3000)

状态函数

¥status function

用于返回响应状态码的专用 status 函数。

¥A dedicated status function for returning status code with response.

typescript
import { Elysia } from 'elysia'

new Elysia()
    .get('/', ({ status }) => status(418, "Kirifuji Nagisa"))
    .listen(3000)
localhost

GET

建议在主处理程序中使用 status,因为它具有更好的推断能力:

¥It's recommended to use status inside the main handler as it has better inference:

  • 允许 TypeScript 检查返回值是否正确匹配响应模式

  • 基于状态码自动补齐类型缩小

  • 使用端到端类型安全 (Eden) 进行错误处理的类型缩小

set.status

如果未提供,则设置默认状态码。

¥Set a default status code if not provided.

建议在只需要返回特定状态码的插件中使用它,同时允许用户返回自定义值。例如,HTTP 201/206 或 403/405 等。

¥It's recommended to use this in a plugin that only needs to return a specific status code while allowing the user to return a custom value. For example, HTTP 201/206 or 403/405, etc.

typescript
import { 
Elysia
} from 'elysia'
new
Elysia
()
.
onBeforeHandle
(({
set
}) => {
set
.
status
= 418
return 'Kirifuji Nagisa' }) .
get
('/', () => 'hi')
.
listen
(3000)

status 函数不同,set.status 无法推断返回值的类型,因此无法检查返回值的类型是否与响应模式正确匹配。

¥Unlike status function, set.status cannot infer the return value type, therefore it can't check if the return value is correctly type to response schema.

提示

HTTP 状态指示响应类型。如果路由处理程序成功执行且没有错误,Elysia 将返回状态码 200。

¥HTTP Status indicates the type of response. If the route handler is executed successfully without error, Elysia will return the status code 200.

你还可以使用状态代码的通用名称(而不是数字)来设置状态代码。

¥You can also set a status code using the common name of the status code instead of using a number.

typescript
// @errors 2322
import { 
Elysia
} from 'elysia'
new
Elysia
()
.
get
('/', ({
set
}) => {
set
.
status
return 'Kirifuji Nagisa' }) .
listen
(3000)

set.headers

允许我们附加或删除以对象形式表示的响应标头。

¥Allowing us to append or delete response headers represented as an Object.

typescript
import { 
Elysia
} from 'elysia'
new
Elysia
()
.
get
('/', ({
set
}) => {
set
.
headers
['x-powered-by'] = 'Elysia'
return 'a mimir' }) .
listen
(3000)

警告

标头名称应小写,以强制 HTTP 标头和自动补齐的大小写一致性,例如,使用 set-cookie 而不是 Set-Cookie

¥The names of headers should be lowercase to force case-sensitivity consistency for HTTP headers and auto-completion, eg. use set-cookie rather than Set-Cookie.

redirect

将请求重定向到其他资源。

¥Redirect a request to another resource.

typescript
import { 
Elysia
} from 'elysia'
new
Elysia
()
.
get
('/', ({
redirect
}) => {
return
redirect
('https://youtu.be/whpVWVWBW4U?&t=8')
}) .
get
('/custom-status', ({
redirect
}) => {
// You can also set custom status to redirect return
redirect
('https://youtu.be/whpVWVWBW4U?&t=8', 302)
}) .
listen
(3000)

使用重定向时,返回值不是必需的,将被忽略。由于响应将来自其他资源。

¥When using redirect, returned value is not required and will be ignored. As response will be from another resource.

服务器

¥Server

可以通过 Context.server 访问服务器实例,并与服务器进行交互。

¥Server instance is accessible via Context.server to interact with the server.

由于服务器可能运行在不同的环境中(测试),因此可以为空。

¥Server could be nullable as it could be running in a different environment (test).

如果服务器正在使用 Bun 运行(分配),则 server 将可用(非空)。

¥If server is running (allocating) using Bun, server will be available (not null).

typescript
import { Elysia } from 'elysia'

new Elysia()
	.get('/port', ({ server }) => {
		return server?.port
	})
	.listen(3000)

请求 IP

¥Request IP

我们可以使用 server.requestIP 方法获取请求 IP

¥We can get request IP by using server.requestIP method

typescript
import { Elysia } from 'elysia'

new Elysia()
	.get('/ip', ({ server, request }) => {
		return server?.requestIP(request)
	})
	.listen(3000)

响应

¥Response

Elysia 建立在 Web 标准请求/响应之上。

¥Elysia is built on top of Web Standard Request/Response.

为了符合 Web 标准,Elysia 会将路由处理程序返回的值映射到 响应 中。

¥To comply with the Web Standard, a value returned from route handler will be mapped into a Response by Elysia.

让你专注于业务逻辑而不是样板代码。

¥Letting you focus on business logic rather than boilerplate code.

typescript
import { Elysia } from 'elysia'

new Elysia()
    // Equivalent to "new Response('hi')"
    .get('/', () => 'hi')
    .listen(3000)

如果你更喜欢显式的 Response 类,Elysia 也会自动处理。

¥If you prefer an explicit Response class, Elysia also handles that automatically.

typescript
import { Elysia } from 'elysia'

new Elysia()
    .get('/', () => new Response('hi'))
    .listen(3000)

提示

使用原始值或 Response 的性能几乎相同(+- 0.1%),因此无论性能如何,都可以选择你喜欢的一种。

¥Using a primitive value or Response has near identical performance (+- 0.1%), so pick the one you prefer, regardless of performance.

表单数据

¥Formdata

我们可以直接从处理程序返回 form 实用程序来返回 FormData

¥We may return a FormData by using returning form utility directly from the handler.

typescript
import { Elysia, form, file } from 'elysia'

new Elysia()
	.get('/', () => form({
		name: 'Tea Party',
		images: [file('nagi.web'), file('mika.webp')]
	}))
	.listen(3000)

即使需要返回文件或多部分表单数据,此模式也非常有用。

¥This pattern is useful if even need to return a file or multipart form data.

返回单个文件

¥Return a single file

或者,你可以直接返回 file 而不返回 form,从而返回单个文件。

¥Or alternatively, you can return a single file by returning file directly without form.

typescript
import { Elysia, file } from 'elysia'

new Elysia()
	.get('/', file('nagi.web'))
	.listen(3000)

句柄

¥Handle

由于 Elysia 建立在 Web 标准请求之上,我们可以使用 Elysia.handle 以编程方式对其进行测试。

¥As Elysia is built on top of Web Standard Request, we can programmatically test it using Elysia.handle.

typescript
import { Elysia } from 'elysia'

const app = new Elysia()
    .get('/', () => 'hello')
    .post('/hi', () => 'hi')
    .listen(3000)

app.handle(new Request('http://localhost/')).then(console.log)

Elysia.handle 是一个用于处理发送到服务器的实际请求的函数。

¥Elysia.handle is a function to process an actual request sent to the server.

提示

与单元测试的模拟不同,你可以期望它的行为类似于发送到服务器的实际请求。

¥Unlike unit test's mock, you can expect it to behave like an actual request sent to the server.

但它对于模拟或创建单元测试也很有用。

¥But also useful for simulating or creating unit tests.

Stream

要使用带有 yield 关键字的生成器函数返回开箱即用的响应流。

¥To return a response streaming out of the box by using a generator function with yield keyword.

typescript
import { Elysia } from 'elysia'

const app = new Elysia()
	.get('/ok', function* () {
		yield 1
		yield 2
		yield 3
	})

在这个例子中,我们可以使用 yield 关键字流式传输响应。

¥This this example, we may stream a response by using yield keyword.

服务器发送事件 (SSE)

¥Server Sent Events (SSE)

Elysia 通过提供 sse 实用函数来支持 服务器发送事件

¥Elysia supports Server Sent Events by providing a sse utility function.

typescript
import { 
Elysia
,
sse
} from 'elysia'
new
Elysia
()
.
get
('/sse', function* () {
yield
sse
('hello world')
yield
sse
({
event
: 'message',
data
: {
message
: 'This is a message',
timestamp
: new
Date
().
toISOString
()
}, }) })

当值被封装在 sse 中时,Elysia 会自动将响应标头设置为 text/event-stream,并将数据格式化为 SSE 事件。

¥When a value is wrapped in sse, Elysia will automatically set the response headers to text/event-stream and format the data as an SSE event.

设置标头

¥Set headers

Elysia 将延迟返回响应头,直到生成第一个块。

¥Elysia will defers returning response headers until the first chunk is yielded.

这使我们能够在响应流式传输之前设置标头。

¥This allows us to set headers before the response is streamed.

typescript
import { 
Elysia
} from 'elysia'
const
app
= new
Elysia
()
.
get
('/ok', function* ({
set
}) {
// This will set headers
set
.
headers
['x-name'] = 'Elysia'
yield 1 yield 2 // This will do nothing
set
.
headers
['x-id'] = '1'
yield 3 })

生成第一个块后,Elysia 将在同一响应中发送标头和第一个块。

¥Once the first chunk is yielded, Elysia will send the headers and the first chunk in the same response.

在生成第一个块后设置标头将不起作用。

¥Setting headers after the first chunk is yielded will do nothing.

条件流

¥Conditional Stream

如果响应没有返回 yield,Elysia 会自动将流转换为正常响应。

¥If the response is returned without yield, Elysia will automatically convert stream to normal response instead.

typescript
import { Elysia } from 'elysia'

const app = new Elysia()
	.get('/ok', function* () {
		if (Math.random() > 0.5) return 'ok'

		yield 1
		yield 2
		yield 3
	})

这使我们能够有条件地流式传输响应,或在必要时返回正常响应。

¥This allows us to conditionally stream a response or return a normal response if necessary.

中止

¥Abort

在流式传输响应时,请求通常在响应完全流式传输之前就被取消,这是很常见的情况。

¥While streaming a response, it's common that request may be cancelled before the response is fully streamed.

当请求取消时,Elysia 将自动停止生成器函数。

¥Elysia will automatically stop the generator function when the request is cancelled.

Eden

Eden 会将流响应解释为 AsyncGenerator,从而允许我们使用 for await 循环来消费流。

¥Eden will interpret a stream response as AsyncGenerator allowing us to use for await loop to consume the stream.

typescript
import { 
Elysia
} from 'elysia'
import {
treaty
} from '@elysiajs/eden'
const
app
= new
Elysia
()
.
get
('/ok', function* () {
yield 1 yield 2 yield 3 }) const {
data
,
error
} = await
treaty
(
app
).
ok
.
get
()
if (
error
) throw
error
for await (const
chunk
of
data
)
console
.
log
(
chunk
)

扩展上下文

¥Extending context

由于 Elysia 仅提供基本信息,我们可以根据具体需求自定义 Context,例如:

¥As Elysia only provides essential information, we can customize Context for our specific need for instance:

  • 提取用户 ID 作为变量

  • 注入通用模式存储库

  • 添加数据库连接

我们可以使用以下 API 扩展 Elysia 的上下文以自定义上下文:

¥We may extend Elysia's context by using the following APIs to customize the Context:

何时扩展上下文

¥When to extend context

你应该只在以下情况下扩展上下文:

¥You should only extend context when:

  • 属性是全局可变状态,使用 state 可在多个路由之间共享。

  • 属性使用 decorate 与请求或响应关联。

  • 属性使用 derive/resolve 从现有属性派生。

否则,我们建议单独定义一个值或函数,而不是扩展上下文。

¥Otherwise, we recommend defining a value or function separately than extending the context.

提示

建议将与请求和响应相关的属性或常用函数分配给 Context,以便分离关注点。

¥It's recommended to assign properties related to request and response, or frequently used functions to Context for separation of concerns.

状态

¥State

状态是 Elysia 应用之间共享的全局可变对象或状态。

¥State is a global mutable object or state shared across the Elysia app.

调用 state 后,value 将在调用时添加到 store 属性中,并可在处理程序中使用。

¥Once state is called, value will be added to store property once at call time, and can be used in handler.

typescript
import { 
Elysia
} from 'elysia'
new
Elysia
()
.
state
('version', 1)
.
get
('/a', ({
store
: {
version
} }) =>
version
)
.
get
('/b', ({
store
}) =>
store
)
.
get
('/c', () => 'still ok')
.
listen
(3000)
localhost

GET

何时使用

¥When to use

  • 当你需要在多个路由之间共享原始可变值时

  • 如果你想使用非原始类型或 wrapper 值或类来改变内部状态,请改用 decorate

关键要点

¥Key takeaway

  • store 是整个 Elysia 应用的单一真实来源全局可变对象的表示。

  • state 是一个用于分配初始值进行存储的函数,该值稍后可以进行修改。

  • 确保在处理程序中使用它之前先赋值。

typescript
import { 
Elysia
} from 'elysia'
new
Elysia
()
// ❌ TypeError: counter doesn't exist in store .
get
('/error', ({
store
}) =>
store
.counter)
Property 'counter' does not exist on type '{}'.
.
state
('counter', 0)
// ✅ Because we assigned a counter before, we can now access it .
get
('/', ({
store
}) =>
store
.
counter
)
localhost

GET

提示

请注意,我们不能在赋值之前使用状态值。

¥Beware that we cannot use a state value before assign.

Elysia 自动将状态值注册到存储中,无需显式指定类型或额外的 TypeScript 泛型。

¥Elysia registers state values into the store automatically without explicit type or additional TypeScript generic needed.

装饰

¥Decorate

decorate 在调用时直接为 Context 分配一个附加属性。

¥decorate assigns an additional property to Context directly at call time.

typescript
import { 
Elysia
} from 'elysia'
class
Logger
{
log
(
value
: string) {
console
.
log
(
value
)
} } new
Elysia
()
.
decorate
('logger', new
Logger
())
// ✅ defined from the previous line .
get
('/', ({
logger
}) => {
logger
.
log
('hi')
return 'hi' })

何时使用

¥When to use

  • Context 的常量或只读值对象

  • 可能包含内部可变状态的非原始值或类

  • 为所有处理程序添加额外的函数、单例或不可变属性。

关键要点

¥Key takeaway

  • 与 state 不同,修饰的值不应该被修改,尽管这是可能的。

  • 确保在处理程序中使用它之前先赋值。

派生

¥Derive

从 Context 中的现有属性中检索值并分配新属性。

¥Retrieve values from existing properties in Context and assign new properties.

在转换生命周期中发生请求时,Derive 会进行赋值,从而允许我们 "derive" (从现有属性创建新属性)

¥Derive assigns when request happens at transform lifecycle allowing us to "derive" (create new properties from existing properties).

typescript
import { 
Elysia
} from 'elysia'
new
Elysia
()
.
derive
(({
headers
}) => {
const
auth
=
headers
['authorization']
return {
bearer
:
auth
?.
startsWith
('Bearer ') ?
auth
.
slice
(7) : null
} }) .
get
('/', ({
bearer
}) =>
bearer
)
localhost

GET

因为 derive 在新请求启动后才会赋值,所以 derive 可以访问请求属性,例如 headers、query、body,而 store 和 decorate 则不能。

¥Because derive is assigned once a new request starts, derive can access request properties like headers, query, body where store, and decorate can't.

何时使用

¥When to use

  • 根据 Context 中现有属性创建一个新属性,无需验证或类型检查

  • 当你需要访问请求属性(例如 headers、query、body)而不进行验证时

关键要点

¥Key takeaway

  • 与 state 和 decorate 不同,derive 不是在调用时赋值,而是在新请求启动时赋值。

  • derive 在转换时调用,或者在验证发生之前,Elysia 无法安全地确认请求属性的类型,导致结果为未知。如果你想从类型化的请求属性中分配新值,你可能需要改用 resolve

解析

¥Resolve

derive 相同,resolve 允许我们为 context 分配新属性。

¥Same as derive, resolve allow us to assign a new property to context.

Resolve 在 beforeHandle 生命周期或验证之后被调用,这使我们能够安全地获取请求属性。

¥Resolve is called at beforeHandle lifecycle or after validation, allowing us to derive request properties safely.

typescript
import { 
Elysia
,
t
} from 'elysia'
new
Elysia
()
.
guard
({
headers
:
t
.
Object
({
bearer
:
t
.
String
({
pattern
: '^Bearer .+$'
}) }) }) .
resolve
(({
headers
}) => {
return {
bearer
:
headers
.
bearer
.
slice
(7)
} }) .
get
('/', ({
bearer
}) =>
bearer
)

何时使用

¥When to use

  • 根据 Context 中现有属性创建一个新属性,并保证类型完整性(已检查类型)

  • 当你需要访问请求属性(例如 headers、query、body)并进行验证时

关键要点

¥Key takeaway

  • resolve 在 beforeHandle 调用,即验证完成后调用。Elysia 可以安全地确认请求属性的类型,从而生成正确的类型。

来自 resolve/derive 的错误

¥Error from resolve/derive

由于 resolve 和 derive 基于 transform 和 beforeHandle 生命周期,因此我们可以从 resolve 和 derive 中返回错误。如果 derive 返回错误,Elysia 将提前退出并将错误作为响应返回。

¥As resolve and derive is based on transform and beforeHandle lifecycle, we can return an error from resolve and derive. If error is returned from derive, Elysia will return early exit and return the error as response.

typescript
import { 
Elysia
} from 'elysia'
new
Elysia
()
.
derive
(({
headers
,
status
}) => {
const
auth
=
headers
['authorization']
if(!
auth
) return
status
(400)
return {
bearer
:
auth
?.
startsWith
('Bearer ') ?
auth
.
slice
(7) : null
} }) .
get
('/', ({
bearer
}) =>
bearer
)

模式

¥Pattern

state、decorate 提供了类似的 API 模式,用于将属性赋值给 Context,如下所示:

¥state, decorate offers a similar APIs pattern for assigning property to Context as the following:

  • key-value

  • object

  • remap

其中 derive 只能与 remap 一起使用,因为它依赖于现有值。

¥Where derive can be only used with remap because it depends on existing value.

key-value

我们可以使用 state 和 decorate 来通过键值模式赋值。

¥We can use state, and decorate to assign a value using a key-value pattern.

typescript
import { Elysia } from 'elysia'

class Logger {
    log(value: string) {
        console.log(value)
    }
}

new Elysia()
    .state('counter', 0)
    .decorate('logger', new Logger())

此模式非常适合于设置单个属性的可读性。

¥This pattern is great for readability for setting a single property.

对象

¥Object

最好将多个属性的赋值包含在一个对象中,以便进行单次赋值。

¥Assigning multiple properties is better contained in an object for a single assignment.

typescript
import { Elysia } from 'elysia'

new Elysia()
    .decorate({
        logger: new Logger(),
        trace: new Trace(),
        telemetry: new Telemetry()
    })

对象提供了一个重复性更低的 API 来设置多个值。

¥The object offers a less repetitive API for setting multiple values.

重新映射

¥Remap

Remap 是一种函数重新赋值。

¥Remap is a function reassignment.

允许我们从现有值创建新值,例如重命名或删除属性。

¥Allowing us to create a new value from existing value like renaming or removing a property.

通过提供一个函数,并返回一个全新的对象来重新赋值。

¥By providing a function, and returning an entirely new object to reassign the value.

typescript
import { 
Elysia
} from 'elysia'
new
Elysia
()
.
state
('counter', 0)
.
state
('version', 1)
.
state
(({
version
, ...
store
}) => ({
...
store
,
elysiaVersion
: 1
})) // ✅ Create from state remap .
get
('/elysia-version', ({
store
}) =>
store
.
elysiaVersion
)
// ❌ Excluded from state remap .
get
('/version', ({
store
}) =>
store
.version)
Property 'version' does not exist on type '{ elysiaVersion: number; counter: number; }'.
localhost

GET

使用状态重映射从现有值创建新的初始值是个好主意。

¥It's a good idea to use state remap to create a new initial value from the existing value.

然而,需要注意的是,Elysia 不提供这种方法的响应性,因为 remap 只会分配一个初始值。

¥However, it's important to note that Elysia doesn't offer reactivity from this approach, as remap only assigns an initial value.

提示

使用 remap,Elysia 会将返回的对象视为新属性,并删除对象中缺少的任何属性。

¥Using remap, Elysia will treat a returned object as a new property, removing any property that is missing from the object.

词缀

¥Affix

为了提供更流畅的体验,某些插件可能包含大量属性值,逐一重新映射可能会很麻烦。

¥To provide a smoother experience, some plugins might have a lot of property value which can be overwhelming to remap one-by-one.

Affix 函数由前缀和后缀组成,允许我们重新映射实例的所有属性。

¥The Affix function which consists of prefix and suffix, allowing us to remap all property of an instance.

ts
import { 
Elysia
} from 'elysia'
const
setup
= new
Elysia
({
name
: 'setup' })
.
decorate
({
argon
: 'a',
boron
: 'b',
carbon
: 'c'
}) const
app
= new
Elysia
()
.
use
(
setup
.
prefix
('decorator', 'setup')
) .
get
('/', ({
setupCarbon
, ...
rest
}) =>
setupCarbon
)
localhost

GET

允许我们轻松地批量重新映射插件的属性,避免插件的名称冲突。

¥Allowing us to bulk remap a property of the plugin effortlessly, preventing the name collision of the plugin.

默认情况下,affix 将自动处理运行时和类型级代码,并将属性重新映射到驼峰命名规范。

¥By default, affix will handle both runtime, type-level code automatically, remapping the property to camelCase as naming convention.

在某些情况下,我们还可以重新映射插件的 all 属性:

¥In some condition, we can also remap all property of the plugin:

ts
import { 
Elysia
} from 'elysia'
const
setup
= new
Elysia
({
name
: 'setup' })
.
decorate
({
argon
: 'a',
boron
: 'b',
carbon
: 'c'
}) const
app
= new
Elysia
()
.
use
(
setup
.
prefix
('all', 'setup'))
.
get
('/', ({
setupCarbon
, ...
rest
}) =>
setupCarbon
)

参考和值

¥Reference and value

要更改状态,建议使用引用进行更改,而不是使用实际值。

¥To mutate the state, it's recommended to use reference to mutate rather than using an actual value.

从 JavaScript 访问属性时,如果我们将对象属性中的原始值定义为新值,则引用将丢失,该值将被视为新的独立值。

¥When accessing the property from JavaScript, if we define a primitive value from an object property as a new value, the reference is lost, the value is treated as new separate value instead.

例如:

¥For example:

typescript
const store = {
    counter: 0
}

store.counter++
console.log(store.counter) // ✅ 1

我们可以使用 store.counter 来访问和修改属性。

¥We can use store.counter to access and mutate the property.

但是,如果我们将计数器定义为新值

¥However, if we define a counter as a new value

typescript
const store = {
    counter: 0
}

let counter = store.counter

counter++
console.log(store.counter) // ❌ 0
console.log(counter) // ✅ 1

一旦将原始值重新定义为新变量,引用 "link" 将会丢失,从而导致意外行为。

¥Once a primitive value is redefined as a new variable, the reference "link" will be missing, causing unexpected behavior.

这可以应用于 store,因为它是一个全局可变对象。

¥This can apply to store, as it's a global mutable object instead.

typescript
import { Elysia } from 'elysia'

new Elysia()
    .state('counter', 0)
    // ✅ Using reference, value is shared
    .get('/', ({ store }) => store.counter++)
    // ❌ Creating a new variable on primitive value, the link is lost
    .get('/error', ({ store: { counter } }) => counter)
localhost

GET