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

前言

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

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

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

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

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

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

Node 服务端

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

使用方法

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

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 来生成(如果您的应用没有任何依赖项,可以跳过此步骤)。然后您可以使用以下命令启动您的应用:

bash 复制代码
node build

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

压缩响应

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

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

环境变量

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

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

bash 复制代码
npm install dotenv

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

bash 复制代码
node +++-r dotenv/config+++ build

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

bash 复制代码
node +++--env-file=.env+++ build

PORT, HOSTSOCKET_PATH

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

ini 复制代码
HOST=127.0.0.1 PORT=4000 node build

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

ini 复制代码
SOCKET_PATH=/tmp/socket node build

ORIGIN, PROTOCOL_HEADER, HOST_HEADERPORT_HEADER

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

ini 复制代码
ORIGIN=https://my.site node build

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

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

ini 复制代码
PROTOCOL_HEADER=x-forwarded-proto HOST_HEADER=x-forwarded-host node build

!NOTE\] [`x-forwarded-proto`](https://link.juejin.cn?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FHTTP%2FHeaders%2FX-Forwarded-Proto "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto") 和 [`x-forwarded-host`](https://link.juejin.cn?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FHTTP%2FHeaders%2FX-Forwarded-Host "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-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 读取地址:

ini 复制代码
ADDRESS_HEADER=True-Client-IP node build

!NOTE\] 标头很容易被伪造。与 `PROTOCOL_HEADER` 和 `HOST_HEADER` 一样,只有在您[了解相关风险](https://link.juejin.cn?target=https%3A%2F%2Fadam-p.ca%2Fblog%2F2022%2F03%2Fx-forwarded-for%2F "https://adam-p.ca/blog/2022/03/x-forwarded-for/")的情况下才应设置这些变量。

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

css 复制代码
<client address>, <proxy 1 address>, <proxy 2 address>

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

css 复制代码
<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

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

js 复制代码
// @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

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

js 复制代码
envPrefix: 'MY_CUSTOM_';
sh 复制代码
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\] 如果您想自定义这一行为,您可以使用[自定义服务端](https://link.juejin.cn?target=https%3A%2F%2Fsvelte.yayujs.com%2Fdocs%2Fkit%2Fadapter-node%23Custom-server "https://svelte.yayujs.com/docs/kit/adapter-node#Custom-server")。

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

js 复制代码
// @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 套接字激活中使用 [`envPrefix`](https://link.juejin.cn?target=https%3A%2F%2Fsvelte.yayujs.com%2Fdocs%2Fkit%2Fadapter-node%23Options-envPrefix "https://svelte.yayujs.com/docs/kit/adapter-node#Options-envPrefix")。`LISTEN_PID` 和 `LISTEN_FDS` 始终无需前缀即可读取。

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

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

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

ini 复制代码
/// 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。
ini 复制代码
/// 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)的处理程序,并且设置你自己的服务器:

js 复制代码
// @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 个系列文章, 全系列文章目录:github.com/mqyqingfeng...

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

相关推荐
浪遏1 小时前
我的远程实习(二) | git 持续更新版
前端
智商不在服务器2 小时前
XSS 绕过分析:一次循环与两次循环的区别
前端·xss
_Matthew2 小时前
JavaScript |(四)正则表达式 | 尚硅谷JavaScript基础&实战
开发语言·javascript·正则表达式
MonkeyKing_sunyuhua2 小时前
npm WARN EBADENGINE required: { node: ‘>=14‘ }
前端·npm·node.js
Hi-Jimmy2 小时前
【VolView】纯前端实现CT三维重建-CBCT
前端·架构·volview·cbct
janthinasnail3 小时前
编写一个简单的chrome截图扩展
前端·chrome
拉不动的猪3 小时前
刷刷题40(vue中计算属性不能异步,如何实现异步)
前端·javascript·vue.js
冴羽yayujs3 小时前
SvelteKit 最新中文文档教程(6)—— 状态管理
前端·javascript·vue.js·前端框架·react·svelte·sveltekit
烛阴3 小时前
前端进阶必学:JavaScript Class 的正确打开方式,让你代码更清晰!
前端·javascript
乐闻x3 小时前
如何创建HTML自定义元素:使用 Web Component 的最佳实践
前端·html·web component