Skip to content

SvelteKit 最新中文文档教程(8)—— 部署 Node 服务端 #359

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
mqyqingfeng opened this issue Mar 21, 2025 · 0 comments
Open

Comments

@mqyqingfeng
Copy link
Owner

前言

Svelte,一个语法简洁、入门容易,面向未来的前端框架。

从 Svelte 诞生之初,就备受开发者的喜爱,根据统计,从 2019 年到 2024 年,连续 6 年一直是开发者最感兴趣的前端框架 No.1

image.png

Svelte 以其独特的编译时优化机制著称,具有轻量级高性能易上手等特性,非常适合构建轻量级 Web 项目

为了帮助大家学习 Svelte,我同时搭建了 Svelte 最新的中文文档站点。

如果需要进阶学习,也可以入手我的小册《Svelte 开发指南》,语法篇、实战篇、原理篇三大篇章带你系统掌握 Svelte!

欢迎围观我的“网页版朋友圈”、加入“冴羽·成长陪伴社群”,踏上“前端大佬成长之路”

Node 服务端

要生成独立的 Node 服务端,请使用 adapter-node

使用方法

使用 npm i -D @sveltejs/adapter-node 安装,然后将适配器添加到您的 svelte.config.js

// @errors: 2307
/// file: svelte.config.js
import adapter from '@sveltejs/adapter-node';

export default {
	kit: {
		adapter: adapter()
	}
};

部署

首先,使用 npm run build 构建您的应用。这将在适配器选项中指定的输出目录(默认为 build)中创建生产服务端。

要运行应用程序,您需要输出目录、项目的 package.jsonnode_modules 中的生产依赖项。生产依赖项可以通过复制 package.jsonpackage-lock.json 然后运行 npm ci --omit dev 来生成(如果您的应用没有任何依赖项,可以跳过此步骤)。然后您可以使用以下命令启动您的应用:

node build

开发依赖项将使用 Rollup 打包到您的应用中。要控制某个包是打包还是外部化,请将其分别放在 package.jsondevDependenciesdependencies 中。

压缩响应

通常您会希望压缩来自服务端的响应。如果您已经在为 SSL 或负载均衡部署了反向代理服务端,那么在该层处理压缩通常会带来更好的性能,因为 Node.js 是单线程的。

但是,如果您正在构建自定义服务端并确实想在那里添加压缩中间件,请注意我们建议使用 @polka/compression,因为 SvelteKit 会流式传输响应,而更流行的 compression 包不支持流式传输,使用时可能会导致错误。

环境变量

devpreview 模式下,SvelteKit 将从您的 .env 文件(或 .env.local,或 .env.[mode]由 Vite 决定)中读取环境变量。

在生产环境中,不会自动加载 .env 文件。要做到这一点,请在您的项目中安装 dotenv...

npm install dotenv

...并在运行构建的应用之前调用它:

node +++-r dotenv/config+++ build

如果您使用的是 Node.js v20.6+,您可以使用 --env-file 标志代替:

node +++--env-file=.env+++ build

PORT, HOSTSOCKET_PATH

默认情况下,服务端将使用 0.0.0.0 并在端口 3000 上接受连接。可以使用 PORTHOST 环境变量对其进行自定义:

HOST=127.0.0.1 PORT=4000 node build

或者,还可以配置服务端在指定的 socket 路径上接受连接。如果您使用 SOCKET_PATH 环境变量来执行此操作,则会忽略 HOSTPORT 环境变量。

SOCKET_PATH=/tmp/socket node build

ORIGIN, PROTOCOL_HEADER, HOST_HEADERPORT_HEADER

HTTP 并不会为 SvelteKit 提供一种可靠的方法来获取当前请求的 URL。最简单的方式是设置 ORIGIN 环境变量来告诉 SvelteKit 应用在哪里被提供服务:

ORIGIN=https://my.site node build

# 或者,例如本地预览和测试
ORIGIN=http://localhost:3000 node build

这样,当请求 /stuff 路径名时,就能正确解析到 https://my.site/stuff。或者,您可以指定用于告诉 SvelteKit 关于请求协议和主机的标头,由此 SvelteKit 可以构建 origin URL:

PROTOCOL_HEADER=x-forwarded-proto HOST_HEADER=x-forwarded-host node build

[!NOTE] x-forwarded-protox-forwarded-host 是事实上的标准请求头,用于在使用反向代理(如负载均衡器和 CDN)时转发原始协议和主机。只有在您的服务端位于受信任的反向代理之后时,才应设置这些变量;否则,客户端可能会伪造这些标头。
如果您在非标准端口托管代理,并且您的反向代理支持 x-forwarded-port,您也可以设置 PORT_HEADER=x-forwarded-port

如果 adapter-node 无法正确确定您的部署的 URL,在使用 表单 actions 时可能会出现以下错误:

[!NOTE] Cross-site POST form submissions are forbidden

ADDRESS_HEADERXFF_DEPTH

传递给 hooks 和端点的 RequestEvent 对象包含一个 event.getClientAddress() 函数,用于返回客户端的 IP 地址。默认情况下,这是发起连接的 remoteAddress。如果您的服务器位于一个或多个代理(如负载均衡器)之后,这个值将包含最内部代理的 IP 地址,而不是客户端的 IP 地址,因此我们需要指定一个 ADDRESS_HEADER 读取地址:

ADDRESS_HEADER=True-Client-IP node build

[!NOTE] 标头很容易被伪造。与 PROTOCOL_HEADERHOST_HEADER 一样,只有在您了解相关风险的情况下才应设置这些变量。

如果 ADDRESS_HEADERX-Forwarded-For,其值会包含用逗号分隔的 IP 地址列表。此时应通过 XFF_DEPTH 环境变量指定在您的服务器前有多少个受信任的代理。例如,如果有三个受信任的代理,代理 3 会转发客户端原始连接和前两个代理的地址:

<client address>, <proxy 1 address>, <proxy 2 address>

有些指南会告诉您读取最左边的地址,但这样会容易被伪造

<spoofed address>, <client address>, <proxy 1 address>, <proxy 2 address>

因此,我们从右侧读取,并依据受信任的代理数量进行处理。在这个示例里,我们会使用 XFF_DEPTH=3

[!NOTE] 如果您确实需要读取最左侧的地址(并且不在意被伪造)——例如提供地理位置服务,在此情况下,IP 地址“真实性”比“可信度”更重要,您可以在应用中自行检查 x-forwarded-for 标头来实现这一点。

BODY_SIZE_LIMIT

接受的最大请求体大小,以字节为单位(包括流式传输时)。

请求体大小也可以使用单位后缀指定,包括千字节(K)、兆字节(M)或千兆字节(G)。例如 512K1M。默认值为 512kb。

您可以设置值为 Infinity(在适配器的旧版本中为 0)来禁用此选项,如果需要更高级的功能,可以在 handle 中自行实现更高级的检查逻辑。

SHUTDOWN_TIMEOUT

接收到 SIGTERMSIGINT 信号后,在强制关闭任何剩余连接之前等待的秒数。默认值是 30。在内部,适配器会调用 closeAllConnections。更多细节请参见 优雅关闭

IDLE_TIMEOUT

在使用 systemd 套接字激活时,IDLE_TIMEOUT 用于指定当应用在没有请求的情况下经过多少秒会自动休眠。如果未设置,则应用会一直运行。详见 套接字激活 获取更多信息。

Options

该适配器可以通过多种选项进行配置:

// @errors: 2307
/// file: svelte.config.js
import adapter from '@sveltejs/adapter-node';

export default {
	kit: {
		adapter: adapter({
			// 以下为默认选项
			out: 'build',
			precompress: true,
			envPrefix: ''
		})
	}
};

out

构建服务端输出的目录,默认为 build —— 也就是说,如果您使用默认目录,执行 node build 将在本地启动服务端。

precompress

使用 gzip 和 brotli 对资源和预渲染页面进行预压缩。默认值为 true

envPrefix

如果您需要更改用于配置部署的环境变量名称(例如,与您无法控制的环境变量冲突),可以指定一个前缀:

envPrefix: 'MY_CUSTOM_';
MY_CUSTOM_HOST=127.0.0.1 \
MY_CUSTOM_PORT=4000 \
MY_CUSTOM_ORIGIN=https://my.site \
node build

优雅关闭

默认情况下,当接收到 SIGTERMSIGINT 信号时,adapter-node 会优雅地关闭 HTTP 服务器。它将:

  1. 拒绝新的请求(server.close
  2. 等待已经发出的请求但尚未收到响应的请求完成,并在连接变为空闲后关闭连接(server.closeIdleConnections
  3. 最后,在超过 SHUTDOWN_TIMEOUT 秒后强制关闭所有仍处于活动状态的连接(server.closeAllConnections)。

[!NOTE] 如果您想自定义这一行为,您可以使用自定义服务端

您还可以监听 sveltekit:shutdown 事件,该事件会在 HTTP 服务器关闭全部连接后触发。与 Node 的 exit 事件不同,sveltekit:shutdown 事件支持异步操作,并且无论服务器是否有未完成的任务(如未关闭的数据库连接),在所有连接都关闭后都会被触发:

// @errors: 2304
process.on('sveltekit:shutdown', async (reason) => {
	await jobs.stop();
	await db.close();
});

参数 reason 的可能取值包括:

  • SIGINT - 关机由 SIGINT 信号触发
  • SIGTERM - 关机由 SIGTERM 信号触发
  • IDLE - 关机由 IDLE_TIMEOUT 触发

套接字激活

当今大多数 Linux 操作系统都使用名为 systemd 的现代进程管理器来启动、运行和管理服务。您可以配置服务器来分配一个套接字,并在需要时按需启动应用。这被称为 套接字激活。在这种情况下,操作系统会向您的应用传递两个环境变量:LISTEN_PIDLISTEN_FDS。然后,适配器会在文件描述符 3 上进行监听,该描述符对应您创建的 systemd 套接字单元。

[!NOTE] 您仍然可以在 systemd 套接字激活中使用 envPrefixLISTEN_PIDLISTEN_FDS 始终无需前缀即可读取。

要利用套接字激活,请按以下步骤操作:

  1. 让您的应用作为一个 systemd 服务 运行。它既可以直接运行在主机系统上,也可以在容器内(例如使用 Docker 或 systemd 可移植服务)运行。

如果您额外向应用传递一个 IDLE_TIMEOUT 环境变量,它将在没有请求持续 IDLE_TIMEOUT 秒时,优雅地关闭。之后如果有新的请求到来,systemd 将自动重新启动您的应用。

/// file: /etc/systemd/system/myapp.service
[Service]
Environment=NODE_ENV=production IDLE_TIMEOUT=60
ExecStart=/usr/bin/node /usr/bin/myapp/build
  1. 创建一个配套的 socket 单元。适配器仅接受单个 socket。
/// file: /etc/systemd/system/myapp.socket
[Socket]
ListenStream=3000

[Install]
WantedBy=sockets.target
  1. 通过运行 sudo systemctl daemon-reload 确保 systemd 识别了这两个单元。然后使用 sudo systemctl enable --now myapp.socket 在启动时启用该 socket 并立即启动它。这样当第一个请求到达 localhost:3000 时,应用将自动启动。

自定义服务端

该适配器会在您的构建目录中创建两个文件——index.jshandler.js。运行 index.js(例如,如果您使用默认 build 目录,那么执行 node build)将会在指定端口上启动服务器。

或者,您可以导入 handler.js 文件,它导出一个兼容 ExpressConnectPolka (甚至是内置的 http.createServer)的处理程序,并且设置你自己的服务器:

// @errors: 2307 7006
/// file: my-server.js
import { handler } from './build/handler.js';
import express from 'express';

const app = express();

// 添加一个独立于 SvelteKit 应用的路由
app.get('/healthcheck', (req, res) => {
	res.end('ok');
});

// 让 SvelteKit 处理其他所有内容,包括提供预渲染页面和静态资源
app.use(handler);

app.listen(3000, () => {
	console.log('listening on port 3000');
});

Svelte 中文文档

点击查看中文文档 - SvelteKit Node 服务端

系统学习 Svelte,欢迎入手小册《Svelte 开发指南》。语法篇、实战篇、原理篇三大篇章带你系统掌握 Svelte!

此外我还写过 JavaScript 系列TypeScript 系列React 系列Next.js 系列冴羽答读者问等 14 个系列文章, 全系列文章目录:https://github.com/mqyqingfeng/Blog

欢迎围观我的“网页版朋友圈”、加入“冴羽·成长陪伴社群”,踏上“前端大佬成长之路”

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant