Node 服务器
要生成独立的 Node 服务器,请使用 adapter-node
。
¥To generate a standalone Node server, use adapter-node
.
用法(Usage)
¥Usage
使用 npm i -D @sveltejs/adapter-node
安装,然后将适配器添加到你的 svelte.config.js
:
¥Install with npm i -D @sveltejs/adapter-node
, then add the adapter to your svelte.config.js
:
import import adapter
adapter from '@sveltejs/adapter-node';
export default {
kit: {
adapter: any;
}
kit: {
adapter: any
adapter: import adapter
adapter()
}
};
部署(Deploying)
¥Deploying
首先,使用 npm run build
构建你的应用。这将在适配器选项中指定的输出目录中创建生产服务器,默认为 build
。
¥First, build your app with npm run build
. This will create the production server in the output directory specified in the adapter options, defaulting to build
.
你需要输出目录、项目的 package.json
和 node_modules
中的生产依赖来运行应用。可以通过复制 package.json
和 package-lock.json
然后运行 npm ci --omit dev
来生成生产依赖(如果你的应用没有任何依赖,则可以跳过此步骤)。然后,你可以使用以下命令启动你的应用:
¥You will need the output directory, the project’s package.json
, and the production dependencies in node_modules
to run the application. Production dependencies can be generated by copying the package.json
and package-lock.json
and then running npm ci --omit dev
(you can skip this step if your app doesn’t have any dependencies). You can then start your app with this command:
node build
开发依赖将使用 Rollup 打包到你的应用中。要控制给定包是打包还是外部化,请将其分别放在 package.json
中的 devDependencies
或 dependencies
中。
¥Development dependencies will be bundled into your app using Rollup. To control whether a given package is bundled or externalised, place it in devDependencies
or dependencies
respectively in your package.json
.
压缩响应(Compressing responses)
¥Compressing responses
你通常需要压缩来自服务器的响应。如果你已经在反向代理后面部署服务器以进行 SSL 或负载平衡,由于 Node.js 是单线程的,因此在该层处理压缩通常会带来更好的性能。
¥You will typically want to compress responses coming from the server. If you are already deploying your server behind a reverse proxy for SSL or load balancing, it typically results in better performance to also handle compression at that layer since Node.js is single-threaded.
但是,如果你正在构建 自定义服务器 并且确实想在那里添加压缩中间件,请注意,我们建议使用 @polka/compression
,因为 SvelteKit 流式传输响应,而更流行的 compression
包不支持流式传输,使用时可能会导致错误。
¥However, if you’re building a custom server and do want to add a compression middleware there, note that we would recommend using @polka/compression
since SvelteKit streams responses and the more popular compression
package does not support streaming and may cause errors when used.
环境变量(Environment variables)
¥Environment variables
在 dev
和 preview
中,SvelteKit 将从你的 .env
文件(或 .env.local
、.env.[mode]
、由 Vite 确定)读取环境变量。
¥In dev
and preview
, SvelteKit will read environment variables from your .env
file (or .env.local
, or .env.[mode]
, as determined by Vite.)
在生产中,不会自动加载 .env
文件。为此,请在你的项目中安装 dotenv
...
¥In production, .env
files are not automatically loaded. To do so, install dotenv
in your project...
npm install dotenv
...并在运行构建的应用之前调用它:
¥...and invoke it before running the built app:
node -r dotenv/config build
如果你使用 Node.js v20.6+,则可以改用 --env-file
标志:
¥If you use Node.js v20.6+, you can use the --env-file
flag instead:
node --env-file=.env build
PORT、HOST 和 SOCKET_PATH(PORT, HOST and SOCKET_PATH)
¥PORT
, HOST
and SOCKET_PATH
默认情况下,服务器将使用端口 3000 接受 0.0.0.0
上的连接。这些可以使用 PORT
和 HOST
环境变量进行定制:
¥By default, the server will accept connections on 0.0.0.0
using port 3000. These can be customised with the PORT
and HOST
environment variables:
HOST=127.0.0.1 PORT=4000 node build
或者,可以将服务器配置为接受指定套接字路径上的连接。当使用 SOCKET_PATH
环境变量完成此操作时,HOST
和 PORT
环境变量将被忽略。
¥Alternatively, the server can be configured to accept connections on a specified socket path. When this is done using the SOCKET_PATH
environment variable, the HOST
and PORT
environment variables will be disregarded.
SOCKET_PATH=/tmp/socket node build
ORIGIN、PROTOCOL_HEADER、HOST_HEADER 和 PORT_HEADER(ORIGIN, PROTOCOL_HEADER, HOST_HEADER, and PORT_HEADER)
¥ORIGIN
, PROTOCOL_HEADER
, HOST_HEADER
, and PORT_HEADER
HTTP 没有为 SvelteKit 提供可靠的方式来了解当前正在请求的 URL。告诉 SvelteKit 应用在哪里提供服务的最简单方法是设置 ORIGIN
环境变量:
¥HTTP doesn’t give SvelteKit a reliable way to know the URL that is currently being requested. The simplest way to tell SvelteKit where the app is being served is to set the ORIGIN
environment variable:
ORIGIN=https://my.site node build
# or e.g. for local previewing and testing
ORIGIN=http://localhost:3000 node build
通过此方法,对 /stuff
路径名的请求将正确解析为 https://my.site/stuff
。或者,你可以指定标头来告知 SvelteKit 有关请求协议和主机的信息,它可以从中构建原始 URL:
¥With this, a request for the /stuff
pathname will correctly resolve to https://my.site/stuff
. Alternatively, you can specify headers that tell SvelteKit about the request protocol and host, from which it can construct the origin URL:
PROTOCOL_HEADER=x-forwarded-proto HOST_HEADER=x-forwarded-host node build
如果你使用反向代理(例如负载平衡器和 CDN),
x-forwarded-proto
和x-forwarded-host
是事实上的标准标头,它们会转发原始协议和主机。你应该只在你的服务器位于受信任的反向代理后面时设置这些变量;否则,客户端可能会欺骗这些标头。¥[!NOTE]
x-forwarded-proto
andx-forwarded-host
are de facto standard headers that forward the original protocol and host if you’re using a reverse proxy (think load balancers and CDNs). You should only set these variables if your server is behind a trusted reverse proxy; otherwise, it’d be possible for clients to spoof these headers.如果你在非标准端口上托管代理并且你的反向代理支持
x-forwarded-port
,你也可以设置PORT_HEADER=x-forwarded-port
。¥If you’re hosting your proxy on a non-standard port and your reverse proxy supports
x-forwarded-port
, you can also setPORT_HEADER=x-forwarded-port
.
如果 adapter-node
无法正确确定你部署的 URL,则在使用 表单操作 时可能会遇到此错误:
¥If adapter-node
can’t correctly determine the URL of your deployment, you may experience this error when using form actions:
禁止跨站点 POST 表单提交
¥[!NOTE] Cross-site POST form submissions are forbidden
ADDRESS_HEADER 和 XFF_DEPTH(ADDRESS_HEADER and XFF_DEPTH)
¥ADDRESS_HEADER
and XFF_DEPTH
传递给钩子和端点的 RequestEvent 对象包含一个返回客户端 IP 地址的 event.getClientAddress()
函数。默认情况下,这是连接的 remoteAddress
。如果你的服务器位于一个或多个代理(例如负载平衡器)后面,则此值将包含最内层代理的 IP 地址而不是客户端的 IP 地址,因此我们需要指定一个 ADDRESS_HEADER
来读取地址:
¥The RequestEvent object passed to hooks and endpoints includes an event.getClientAddress()
function that returns the client’s IP address. By default this is the connecting remoteAddress
. If your server is behind one or more proxies (such as a load balancer), this value will contain the innermost proxy’s IP address rather than the client’s, so we need to specify an ADDRESS_HEADER
to read the address from:
ADDRESS_HEADER=True-Client-IP node build
标头很容易被欺骗。与
PROTOCOL_HEADER
和HOST_HEADER
一样,你应该在设置这些之前 知道你在做什么。¥[!NOTE] Headers can easily be spoofed. As with
PROTOCOL_HEADER
andHOST_HEADER
, you should know what you’re doing before setting these.
如果 ADDRESS_HEADER
是 X-Forwarded-For
,则标头值将包含以逗号分隔的 IP 地址列表。XFF_DEPTH
环境变量应指定服务器前面有多少个受信任的代理。例如如果有三个受信任的代理,代理 3 将转发原始连接和前两个代理的地址:
¥If the ADDRESS_HEADER
is X-Forwarded-For
, the header value will contain a comma-separated list of IP addresses. The XFF_DEPTH
environment variable should specify how many trusted proxies sit in front of your server. E.g. if there are three trusted proxies, proxy 3 will forward the addresses of the original connection and the first two proxies:
<client address>, <proxy 1 address>, <proxy 2 address>
某些指南会告诉你读取最左边的地址,但这会让你留下 易受欺骗:
¥Some guides will tell you to read the left-most address, but this leaves you vulnerable to spoofing:
<spoofed address>, <client address>, <proxy 1 address>, <proxy 2 address>
我们改为从右侧读取,考虑受信任代理的数量。在这种情况下,我们将使用 XFF_DEPTH=3
。
¥We instead read from the right, accounting for the number of trusted proxies. In this case, we would use XFF_DEPTH=3
.
如果你需要读取最左边的地址(并且不关心欺骗) - 例如,提供地理定位服务,其中 IP 地址的真实性比可信性更重要,你可以通过检查应用中的
x-forwarded-for
标头来实现。¥[!NOTE] If you need to read the left-most address instead (and don’t care about spoofing) — for example, to offer a geolocation service, where it’s more important for the IP address to be real than trusted, you can do so by inspecting the
x-forwarded-for
header within your app.
BODY_SIZE_LIMIT
在流式传输时接受的最大请求主体大小(以字节为单位)。主体大小也可以用千字节(K
)、兆字节(M
)或千兆字节(G
)的单位后缀来指定。例如,512K
或 1M
。默认为 512kb。如果你需要更高级的功能,你可以使用值 Infinity
(旧版本的适配器中为 0)禁用此选项,并在 handle
中实现自定义检查。
¥The maximum request body size to accept in bytes including while streaming. The body size can also be specified with a unit suffix for kilobytes (K
), megabytes (M
), or gigabytes (G
). For example, 512K
or 1M
. Defaults to 512kb. You can disable this option with a value of Infinity
(0 in older versions of the adapter) and implement a custom check in handle
if you need something more advanced.
SHUTDOWN_TIMEOUT
在收到 SIGTERM
或 SIGINT
信号后强制关闭任何剩余连接之前等待的秒数。默认为 30
。适配器在内部调用 closeAllConnections
。有关更多详细信息,请参阅 优雅关机。
¥The number of seconds to wait before forcefully closing any remaining connections after receiving a SIGTERM
or SIGINT
signal. Defaults to 30
. Internally the adapter calls closeAllConnections
. See Graceful shutdown for more details.
IDLE_TIMEOUT
使用 systemd 套接字激活时,IDLE_TIMEOUT
指定在未收到任何请求后应用自动进入睡眠状态的秒数。如果未设置,应用将持续运行。有关更多详细信息,请参阅 套接字激活。
¥When using systemd socket activation, IDLE_TIMEOUT
specifies the number of seconds after which the app is automatically put to sleep when receiving no requests. If not set, the app runs continuously. See Socket activation for more details.
选项(Options)
¥Options
适配器可以配置各种选项:
¥The adapter can be configured with various options:
import import adapter
adapter from '@sveltejs/adapter-node';
export default {
kit: {
adapter: any;
}
kit: {
adapter: any
adapter: import adapter
adapter({
// default options are shown
out: string
out: 'build',
precompress: boolean
precompress: true,
envPrefix: string
envPrefix: ''
})
}
};
out
构建服务器的目录。它默认为 build
— 即 node build
将在创建服务器后在本地启动服务器。
¥The directory to build the server to. It defaults to build
— i.e. node build
would start the server locally after it has been created.
precompress
启用使用 gzip 和 brotli 对资源和预渲染页面进行预压缩。它默认为 true
。
¥Enables precompressing using gzip and brotli for assets and prerendered pages. It defaults to true
.
envPrefix
如果你需要更改用于配置部署的环境变量的名称(例如,为了与你无法控制的环境变量冲突),你可以指定前缀:
¥If you need to change the name of the environment variables used to configure the deployment (for example, to deconflict with environment variables you don’t control), you can specify a prefix:
envPrefix: 'MY_CUSTOM_';
MY_CUSTOM_HOST=127.0.0.1 \
MY_CUSTOM_PORT=4000 \
MY_CUSTOM_ORIGIN=https://my.site \
node build
优雅关机(Graceful shutdown)
¥Graceful shutdown
默认情况下,当收到 SIGTERM
或 SIGINT
信号时,adapter-node
会正常关闭 HTTP 服务器。它将:
¥By default adapter-node
gracefully shuts down the HTTP server when a SIGTERM
or SIGINT
signal is received. It will:
重命名:
¥reject new requests (
server.close
)等待已发出但尚未收到响应的请求完成,并在连接空闲时关闭连接(
server.closeIdleConnections
)¥wait for requests that have already been made but not received a response yet to finish and close connections once they become idle (
server.closeIdleConnections
)最后,关闭
SHUTDOWN_TIMEOUT
秒后仍处于活动状态的所有剩余连接。(server.closeAllConnections
)¥and finally, close any remaining connections that are still active after
SHUTDOWN_TIMEOUT
seconds. (server.closeAllConnections
)
如果你想自定义此行为,可以使用 自定义服务器。
¥[!NOTE] If you want to customize this behaviour you can use a custom server.
你可以监听 HTTP 服务器关闭所有连接后发出的 sveltekit:shutdown
事件。与 Node 的 exit
事件不同,sveltekit:shutdown
事件支持异步操作,并且始终在所有连接关闭时发出,即使服务器有悬而未决的工作(例如打开的数据库连接)。
¥You can listen to the sveltekit:shutdown
event which is emitted after the HTTP server has closed all connections. Unlike Node’s exit
event, the sveltekit:shutdown
event supports asynchronous operations and is always emitted when all connections are closed even if the server has dangling work such as open database connections.
var process: NodeJS.Process
process.NodeJS.Process.on(event: string | symbol, listener: (...args: any[]) => void): NodeJS.Process (+12 overloads)
Adds the listener
function to the end of the listeners array for the event
named eventName
. No checks are made to see if the listener
has already
been added. Multiple calls passing the same combination of eventName
and
listener
will result in the listener
being added, and called, multiple times.
server.on('connection', (stream) => {
console.log('someone connected!');
});
Returns a reference to the EventEmitter
, so that calls can be chained.
By default, event listeners are invoked in the order they are added. The emitter.prependListener()
method can be used as an alternative to add the
event listener to the beginning of the listeners array.
import { EventEmitter } from 'node:events';
const myEE = new EventEmitter();
myEE.on('foo', () => console.log('a'));
myEE.prependListener('foo', () => console.log('b'));
myEE.emit('foo');
// Prints:
// b
// a
on('sveltekit:shutdown', async (reason: any
reason) => {
await jobs.stop();
await db.close();
});
参数 reason
具有以下值之一:
¥The parameter reason
has one of the following values:
SIGINT
- 关闭由SIGINT
信号触发¥
SIGINT
- shutdown was triggered by aSIGINT
signalSIGTERM
- 关闭由SIGTERM
信号触发¥
SIGTERM
- shutdown was triggered by aSIGTERM
signalIDLE
- 关闭由IDLE_TIMEOUT
触发¥
IDLE
- shutdown was triggered byIDLE_TIMEOUT
套接字激活(Socket activation)
¥Socket activation
当今大多数 Linux 操作系统都使用名为 systemd 的现代进程管理器来启动服务器并运行和管理服务。你可以配置服务器以分配套接字并根据需要启动和扩展应用。这被称为 套接字激活。在这种情况下,操作系统将向你的应用传递两个环境变量 - LISTEN_PID
和 LISTEN_FDS
。然后,适配器将监听文件描述符 3,它引用你必须创建的 systemd 套接字单元。
¥Most Linux operating systems today use a modern process manager called systemd to start the server and run and manage services. You can configure your server to allocate a socket and start and scale your app on demand. This is called socket activation. In this case, the OS will pass two environment variables to your app — LISTEN_PID
and LISTEN_FDS
. The adapter will then listen on file descriptor 3 which refers to a systemd socket unit that you will have to create.
你仍然可以将
envPrefix
与 systemd 套接字激活一起使用。LISTEN_PID
和LISTEN_FDS
始终不带前缀读取。¥[!NOTE] You can still use
envPrefix
with systemd socket activation.LISTEN_PID
andLISTEN_FDS
are always read without a prefix.
要利用套接字激活,请按照以下步骤操作。
¥To take advantage of socket activation follow these steps.
将你的应用作为 systemd 服务 运行。它可以直接在主机系统上运行,也可以在容器内运行(例如使用 Docker 或 systemd 可移植服务)。如果你另外将
IDLE_TIMEOUT
环境变量传递给你的应用,如果IDLE_TIMEOUT
秒内没有请求,它将正常关闭。当有新请求进入时,systemd 将自动重新启动你的应用。¥Run your app as a systemd service. It can either run directly on the host system or inside a container (using Docker or a systemd portable service for example). If you additionally pass an
IDLE_TIMEOUT
environment variable to your app it will gracefully shutdown if there are no requests forIDLE_TIMEOUT
seconds. systemd will automatically start your app again when new requests are coming in.
[Service]
Environment=NODE_ENV=production IDLE_TIMEOUT=60
ExecStart=/usr/bin/node /usr/bin/myapp/build
创建随附的 套接字单元。适配器只接受一个套接字。
¥Create an accompanying socket unit. The adapter only accepts a single socket.
[Socket]
ListenStream=3000
[Install]
WantedBy=sockets.target
通过运行
sudo systemctl daemon-reload
,确保 systemd 已识别两个单元。然后在启动时启用套接字并使用sudo systemctl enable --now myapp.socket
立即启动它。一旦向localhost:3000
发出第一个请求,应用将自动启动。¥Make sure systemd has recognised both units by running
sudo systemctl daemon-reload
. Then enable the socket on boot and start it immediately usingsudo systemctl enable --now myapp.socket
. The app will then automatically start once the first request is made tolocalhost:3000
.
自定义服务器(Custom server)
¥Custom server
适配器在你的构建目录中创建两个文件 - index.js
和 handler.js
。运行 index.js
— 例如 node build
,如果你使用默认构建目录 — 将在配置的端口上启动服务器。
¥The adapter creates two files in your build directory — index.js
and handler.js
. Running index.js
— e.g. node build
, if you use the default build directory — will start a server on the configured port.
或者,你可以导入 handler.js
文件,该文件导出适用于 Express、Connect 或 Polka(甚至只是内置的 http.createServer
)的处理程序并设置你自己的服务器:
¥Alternatively, you can import the handler.js
file, which exports a handler suitable for use with Express, Connect or Polka (or even just the built-in http.createServer
) and set up your own server:
import { import handler
handler } from './build/handler.js';
import import express
express from 'express';
const const app: any
app = import express
express();
// add a route that lives separately from the SvelteKit app
const app: any
app.get('/healthcheck', (req, res) => {
res: any
res.end('ok');
});
// let SvelteKit handle everything else, including serving prerendered pages and static assets
const app: any
app.use(import handler
handler);
const app: any
app.listen(3000, () => {
var console: Console
The console
module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
- A
Console
class with methods such as console.log()
, console.error()
and console.warn()
that can be used to write to any Node.js stream.
- A global
console
instance configured to write to process.stdout
and
process.stderr
. The global console
can be used without calling require('console')
.
Warning: The global console object’s methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O
for
more information.
Example using the global console
:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console
class:
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to stdout
with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()
).
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
See util.format()
for more information.
log('listening on port 3000');
});