Skip to main content

常见问题

其他资源

请同时查看 Svelte FAQvite-plugin-svelte FAQ,了解这些库相关问题的答案。

我可以用 SvelteKit 制作什么?

SvelteKit 可以用于创建大多数类型的应用程序。开箱即用,SvelteKit 支持许多功能,包括:

SvelteKit 还可以通过适配器部署到各种托管架构。在使用 SSR (或在不预渲染的情况下添加服务端逻辑)的情况下,这些功能将被适配到目标后端。一些例子包括:

  • 使用 Node.js 后端的自托管动态 Web 应用。
  • 带有后端加载器和 API 部署为远程函数的 serverless web 应用。查看零配置部署了解常用部署选项。
  • 静态预渲染站点,如托管在 CDN 或静态主机上的博客或多页面站点。静态生成的站点不带后端。
  • 单页应用(SPA),具有客户端路由和渲染 API 驱动的动态内容。SPA 不带后端且不进行服务端渲染。当将 SvelteKit 与用 PHP、.Net、Java、C、Golang、Rust 等编写的应用打包在一起时,通常会选择这个选项。
  • 以上几种方式的混合;一些路由可以是静态的,一些路由可以使用后端函数获取动态信息。这可以通过页面选项配置,其中包括选择退出 SSR 的选项。

为了支持 SSR,需要一个 JS 后端 — 如 Node.js 或基于 Deno 的服务端、serverless function 或 edge function。

还可以编写自定义适配器或利用社区适配器将 SvelteKit 部署到更多平台,如专用服务端环境、浏览器扩展或原生应用。查看集成了解更多示例和集成。

如何在应用程序中包含 package.json 的详细信息?

由于 SvelteKit 期望 svelte.config.js 是一个 ES 模块,因此不能直接引用 JSON 文件。如果你想在应用程序中包含应用程序版本号或其他来自 package.json 的信息,可以像这样加载 JSON:

svelte.config
import { 
function readFileSync(path: PathOrFileDescriptor, options?: {
    encoding?: null | undefined;
    flag?: string | undefined;
} | null): Buffer (+2 overloads)

Returns the contents of the path.

For detailed information, see the documentation of the asynchronous version of this API: {@link readFile } .

If the encoding option is specified then this function returns a string. Otherwise it returns a buffer.

Similar to {@link readFile } , when the path is a directory, the behavior of fs.readFileSync() is platform-specific.

import { readFileSync } from 'node:fs';

// macOS, Linux, and Windows
readFileSync('<directory>');
// => [Error: EISDIR: illegal operation on a directory, read <directory>]

//  FreeBSD
readFileSync('<directory>'); // => <data>
@sincev0.1.8
@parampath filename or file descriptor
readFileSync
} from 'node:fs';
import { function fileURLToPath(url: string | URL, options?: FileUrlToPathOptions): string

This function ensures the correct decodings of percent-encoded characters as well as ensuring a cross-platform valid absolute path string.

import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);

new URL('file:///C:/path/').pathname;      // Incorrect: /C:/path/
fileURLToPath('file:///C:/path/');// Correct:   C:\path\ (Windows)

new URL('file://nas/foo.txt').pathname;    // Incorrect: /foo.txt
fileURLToPath('file://nas/foo.txt');       // Correct:   \\nas\foo.txt (Windows)

new URL('file:///你好.txt').pathname;      // Incorrect: /%E4%BD%A0%E5%A5%BD.txt
fileURLToPath('file:///你好.txt');// Correct:   /你好.txt (POSIX)

new URL('file:///hello world').pathname;   // Incorrect: /hello%20world
fileURLToPath('file:///hello world');      // Correct:   /hello world (POSIX)
@sincev10.12.0
@paramurl The file URL string or URL object to convert to a path.
@returnThe fully-resolved platform-specific Node.js file path.
fileURLToPath
} from 'node:url';
const const path: stringpath = function fileURLToPath(url: string | URL, options?: FileUrlToPathOptions): string

This function ensures the correct decodings of percent-encoded characters as well as ensuring a cross-platform valid absolute path string.

import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);

new URL('file:///C:/path/').pathname;      // Incorrect: /C:/path/
fileURLToPath('file:///C:/path/');// Correct:   C:\path\ (Windows)

new URL('file://nas/foo.txt').pathname;    // Incorrect: /foo.txt
fileURLToPath('file://nas/foo.txt');       // Correct:   \\nas\foo.txt (Windows)

new URL('file:///你好.txt').pathname;      // Incorrect: /%E4%BD%A0%E5%A5%BD.txt
fileURLToPath('file:///你好.txt');// Correct:   /你好.txt (POSIX)

new URL('file:///hello world').pathname;   // Incorrect: /hello%20world
fileURLToPath('file:///hello world');      // Correct:   /hello world (POSIX)
@sincev10.12.0
@paramurl The file URL string or URL object to convert to a path.
@returnThe fully-resolved platform-specific Node.js file path.
fileURLToPath
(new
new URL(input: string | {
    toString: () => string;
}, base?: string | URL): URL

Browser-compatible URL class, implemented by following the WHATWG URL Standard. Examples of parsed URLs may be found in the Standard itself. The URL class is also available on the global object.

In accordance with browser conventions, all properties of URL objects are implemented as getters and setters on the class prototype, rather than as data properties on the object itself. Thus, unlike legacy urlObjects, using the delete keyword on any properties of URL objects (e.g. delete myURL.protocol, delete myURL.pathname, etc) has no effect but will still return true.

@sincev7.0.0, v6.13.0
URL
('package.json', import.meta.ImportMeta.url: string

The absolute file: URL of the module.

url
));
const const pkg: anypkg = var JSON: JSON

An intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.

JSON
.JSON.parse(text: string, reviver?: (this: any, key: string, value: any) => any): any

Converts a JavaScript Object Notation (JSON) string into an object.

@paramtext A valid JSON string.
@paramreviver A function that transforms the results. This function is called for each member of the object. If a member contains nested objects, the nested objects are transformed before the parent object is.
parse
(
function readFileSync(path: PathOrFileDescriptor, options: {
    encoding: BufferEncoding;
    flag?: string | undefined;
} | BufferEncoding): string (+2 overloads)

Synchronously reads the entire contents of a file.

@parampath A path to a file. If a URL is provided, it must use the file: protocol. If a file descriptor is provided, the underlying file will not be closed automatically.
@paramoptions Either the encoding for the result, or an object that contains the encoding and an optional flag. If a flag is not provided, it defaults to 'r'.
readFileSync
(const path: stringpath, 'utf8'));

如何修复尝试引入包时遇到的错误?

大多数与引入库相关的问题都是由于打包不正确造成的。你可以通过在 publint 网站输入库来检查库的打包是否与 Node.js 兼容。

在检查库是否正确打包时,需要注意以下几点:

  • exports 优先于其他入口点字段,如 mainmodule。添加 exports 字段可能不向后兼容,因为它会阻止深度导入。
  • ESM 文件应以 .mjs 结尾,除非设置了 "type": "module",在这种情况下,CommonJS 文件应以 .cjs 结尾。
  • 如果没有定义 exports,则应该定义 main。它应该是 CommonJS 或 ESM 文件,并遵循上一条规则。如果定义了 module 字段,它应该指向 ESM 文件。
  • Svelte 组件应该以未编译的 .svelte 文件分发,包中的任何 JS 都应以 ESM 形式编写。自定义脚本和样式语言,如 TypeScript 和 SCSS,应该分别预处理为普通的 JS 和 CSS。我们建议使用 svelte-package 来打包 Svelte 库,它会为你完成这些工作。

当库提供 ESM 版本时,它们在浏览器中与 Vite 配合使用效果最好,特别是当它们是 Svelte 组件库的依赖项时。你可以建议库作者提供 ESM 版本。然而,CommonJS(CJS)依赖项也应该可以工作,因为默认情况下,vite-plugin-svelte 会要求 Vite 使用 esbuild 预打包它们并转换为 ESM。

如果你仍然遇到问题,我们建议搜索 Vite issue 追踪器和相关库的问题 issue。有时可以通过调整 optimizeDepsssr 配置值来解决问题,但我们建议这只作为临时解决方案,最终还是应该修复相关库。

如何在 SvelteKit 中使用视图过渡 API?

虽然 SvelteKit 没有与视图过渡的特定集成,但你可以在 onNavigate 中调用 document.startViewTransition 来在每次客户端导航时触发视图过渡。

import { function onNavigate(callback: (navigation: import("@sveltejs/kit").OnNavigate) => MaybePromise<void | (() => void)>): void

A lifecycle function that runs the supplied callback immediately before we navigate to a new URL except during full-page navigations.

If you return a Promise, SvelteKit will wait for it to resolve before completing the navigation. This allows you to — for example — use document.startViewTransition. Avoid promises that are slow to resolve, since navigation will appear stalled to the user.

If a function (or a Promise that resolves to a function) is returned from the callback, it will be called once the DOM has updated.

onNavigate must be called during a component initialization. It remains active as long as the component is mounted.

onNavigate
} from '$app/navigation';
function onNavigate(callback: (navigation: import("@sveltejs/kit").OnNavigate) => MaybePromise<void | (() => void)>): void

A lifecycle function that runs the supplied callback immediately before we navigate to a new URL except during full-page navigations.

If you return a Promise, SvelteKit will wait for it to resolve before completing the navigation. This allows you to — for example — use document.startViewTransition. Avoid promises that are slow to resolve, since navigation will appear stalled to the user.

If a function (or a Promise that resolves to a function) is returned from the callback, it will be called once the DOM has updated.

onNavigate must be called during a component initialization. It remains active as long as the component is mounted.

onNavigate
((navigation: OnNavigatenavigation) => {
if (!var document: Documentdocument.startViewTransition) return; return new
var Promise: PromiseConstructor
new <void | (() => void)>(executor: (resolve: (value: void | (() => void) | PromiseLike<void | (() => void)>) => void, reject: (reason?: any) => void) => void) => Promise<void | (() => void)>

Creates a new Promise.

@paramexecutor A callback used to initialize the promise. This callback is passed two arguments: a resolve callback used to resolve the promise with a value or the result of another promise, and a reject callback used to reject the promise with a provided reason or error.
Promise
((resolve: (value: void | (() => void) | PromiseLike<void | (() => void)>) => voidresolve) => {
var document: Documentdocument.startViewTransition(async () => { resolve: (value: void | (() => void) | PromiseLike<void | (() => void)>) => voidresolve(); await navigation: OnNavigatenavigation.Navigation.complete: Promise<void>

A promise that resolves once the navigation is complete, and rejects if the navigation fails or is aborted. In the case of a willUnload navigation, the promise will never resolve

complete
;
}); }); });

更多信息,请参见 Svelte 博客上的“解锁视图过渡”

如何在 SvelteKit 中使用 X?

请确保你已阅读了集成相关的文档部分。如果你仍然遇到问题,以下列出了常见问题的解决方案。

如何设置数据库?

将查询数据库的代码放在服务端路由中 - 不要在 .svelte 文件中查询数据库。你可以创建一个 db.js 或类似的文件,该文件立即建立连接并使客户端在整个应用程序中作为单例可访问。你可以在 hooks.server.js 中执行任何一次性设置代码,并在需要的任何端点中导入数据库辅助函数。

如何使用依赖于 document 或 window 的仅客户端库?

如果你需要访问 documentwindow 变量,或者需要代码仅在客户端运行,你可以将其包装在 browser 检查中:

import { const browser: boolean

true if the app is running in the browser.

browser
} from '$app/environment';
if (const browser: boolean

true if the app is running in the browser.

browser
) {
// 仅客户端代码在这里 }

如果你想在组件首次渲染到 DOM 后运行代码,也可以在 onMount 中运行:

import { function onMount<T>(fn: () => NotFunction<T> | Promise<NotFunction<T>> | (() => any)): void

The onMount function schedules a callback to run as soon as the component has been mounted to the DOM. It must be called during the component’s initialisation (but doesn’t need to live inside the component; it can be called from an external module).

If a function is returned synchronously from onMount, it will be called when the component is unmounted.

onMount does not run inside server-side components.

onMount
} from 'svelte';
onMount<void>(fn: () => void | (() => any) | Promise<void>): void

The onMount function schedules a callback to run as soon as the component has been mounted to the DOM. It must be called during the component’s initialisation (but doesn’t need to live inside the component; it can be called from an external module).

If a function is returned synchronously from onMount, it will be called when the component is unmounted.

onMount does not run inside server-side components.

onMount
(async () => {
const { const method: anymethod } = await import('some-browser-only-library'); const method: anymethod('hello world'); });

如果你想使用的库是无副作用的,你也可以静态导入它,它将在服务端构建中被 tree shake 掉,其中 onMount 将自动被替换为一个空操作:

import { function onMount<T>(fn: () => NotFunction<T> | Promise<NotFunction<T>> | (() => any)): void

The onMount function schedules a callback to run as soon as the component has been mounted to the DOM. It must be called during the component’s initialisation (but doesn’t need to live inside the component; it can be called from an external module).

If a function is returned synchronously from onMount, it will be called when the component is unmounted.

onMount does not run inside server-side components.

onMount
} from 'svelte';
import { module "some-browser-only-library"method } from 'some-browser-only-library'; onMount<void>(fn: () => void | (() => any) | Promise<void>): void

The onMount function schedules a callback to run as soon as the component has been mounted to the DOM. It must be called during the component’s initialisation (but doesn’t need to live inside the component; it can be called from an external module).

If a function is returned synchronously from onMount, it will be called when the component is unmounted.

onMount does not run inside server-side components.

onMount
(() => {
module "some-browser-only-library"method('hello world'); });

最后,你也可以考虑使用 {#await} 块:

index
<script>
  import { browser } from '$app/environment';

  const ComponentConstructor = browser ?
	import('some-browser-only-library').then((module) => module.Component) :
	new Promise(() => {});
</script>

{#await ComponentConstructor}
  <p>加载中...</p>
{:then component}
  <svelte:component this={component} />
{:catch error}
  <p>出错了: {error.message}</p>
{/await}
<script lang="ts">
  import { browser } from '$app/environment';

  const ComponentConstructor = browser ?
	import('some-browser-only-library').then((module) => module.Component) :
	new Promise(() => {});
</script>

{#await ComponentConstructor}
  <p>加载中...</p>
{:then component}
  <svelte:component this={component} />
{:catch error}
  <p>出错了: {error.message}</p>
{/await}

如何使用不同的后端 API 服务端?

你可以使用 event.fetch 从外部 API 服务端请求数据,但要注意,你需要处理 CORS,这将导致一些复杂情况,比如通常需要预检请求,从而导致更高的延迟。对不同子域的请求也可能由于额外的 DNS 查找、TLS 设置等而增加延迟。如果你想使用这种方法,你可能会发现 handleFetch 很有帮助。

另一种方法是设置代理以绕过 CORS 问题。在生产环境中,你需要将像 /api 这样的路径重写到 API 服务端;对于本地开发,使用 Vite 的 server.proxy 选项。

在生产环境中如何设置重写将取决于你的部署平台。如果重写不是一个选项,你也可以添加一个 API 路由

src/routes/api/[...path]/+server
/** @type {import('./$types').RequestHandler} */
export function 
function GET({ params, url }: {
    params: any;
    url: any;
}): Promise<Response>
@type{import('./$types').RequestHandler}
GET
({ params: anyparams, url: anyurl }) {
return function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response> (+1 overload)fetch(`https://my-api-server.com/${params: anyparams.path + url: anyurl.search}`); }
import type { 
type RequestHandler = (event: Kit.RequestEvent<Record<string, any>, string | null>) => MaybePromise<Response>
type RequestHandler = (event: Kit.RequestEvent<Record<string, any>, string | null>) => MaybePromise<Response>
RequestHandler
} from './$types';
export const const GET: RequestHandlerGET:
type RequestHandler = (event: Kit.RequestEvent<Record<string, any>, string | null>) => MaybePromise<Response>
type RequestHandler = (event: Kit.RequestEvent<Record<string, any>, string | null>) => MaybePromise<Response>
RequestHandler
= ({ params: Record<string, any>

The parameters of the current route - e.g. for a route like /blog/[slug], a { slug: string } object

params
, url: URL

The requested URL.

url
}) => {
return function fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response> (+1 overload)fetch(`https://my-api-server.com/${params: Record<string, any>

The parameters of the current route - e.g. for a route like /blog/[slug], a { slug: string } object

params
.path + url: URL

The requested URL.

url
.URL.search: stringsearch}`);
};

(注意,根据你的需求,你可能还需要代理 POST / PATCH 等请求,并转发 request.headers。)

如何使用中间件?

adapter-node 构建了一个中间件,你可以在生产模式下与你自己的服务端一起使用。在开发中,你可以使用 Vite 插件向 Vite 添加中间件。例如:

import { module "@sveltejs/kit/vite"sveltekit } from '@sveltejs/kit/vite';

/** @type {import('vite').Plugin} */
const const myPlugin: Plugin<any>
@type{import('vite').Plugin}
myPlugin
= {
OutputPlugin.name: stringname: 'log-request-middleware', Plugin<any>.configureServer?: ObjectHook<ServerHook> | undefined

Configure the vite server. The hook receives the {@link ViteDevServer }

instance. This can also be used to store a reference to the server for use in other hooks.

The hooks will be called before internal middlewares are applied. A hook can return a post hook that will be called after internal middlewares are applied. Hook can be async functions and will be called in series.

configureServer
(server: ViteDevServerserver) {
server: ViteDevServerserver.ViteDevServer.middlewares: Connect.Server

A connect app instance.

  • Can be used to attach custom middlewares to the dev server.
  • Can also be used as the handler function of a custom http server or as a middleware in any connect-style Node.js frameworks

https://github.com/senchalabs/connect#use-middleware

middlewares
.Connect.Server.use(fn: Connect.NextHandleFunction): Connect.Server (+3 overloads)

Utilize the given middleware handle to the given route, defaulting to /. This “route” is the mount-point for the middleware, when given a value other than / the middleware is only effective when that segment is present in the request’s pathname.

For example if we were to mount a function at /admin, it would be invoked on /admin, and /admin/settings, however it would not be invoked for /, or /posts.

use
((req: Connect.IncomingMessagereq, res: ServerResponse<IncomingMessage>res, next: Connect.NextFunctionnext) => {
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
@seesource
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.

@sincev0.1.100
log
(`收到请求 ${req: Connect.IncomingMessagereq.IncomingMessage.url?: string | undefined

Only valid for request obtained from {@link Server } .

Request URL string. This contains only the URL that is present in the actual HTTP request. Take the following request:

GET /status?name=ryan HTTP/1.1
Accept: text/plain

To parse the URL into its parts:

new URL(`http://${process.env.HOST ?? 'localhost'}${request.url}`);

When request.url is '/status?name=ryan' and process.env.HOST is undefined:

$ node
> new URL(`http://${process.env.HOST ?? 'localhost'}${request.url}`);
URL {
  href: 'http://localhost/status?name=ryan',
  origin: 'http://localhost',
  protocol: 'http:',
  username: '',
  password: '',
  host: 'localhost',
  hostname: 'localhost',
  port: '',
  pathname: '/status',
  search: '?name=ryan',
  searchParams: URLSearchParams { 'name' => 'ryan' },
  hash: ''
}

Ensure that you set process.env.HOST to the server’s host name, or consider replacing this part entirely. If using req.headers.host, ensure proper validation is used, as clients may specify a custom Host header.

@sincev0.1.90
url
}`);
next: (err?: any) => voidnext(); }); } }; /** @type {import('vite').UserConfig} */ const const config: UserConfig
@type{import('vite').UserConfig}
config
= {
UserConfig.plugins?: PluginOption[] | undefined

Array of vite plugins to use.

plugins
: [const myPlugin: Plugin<any>
@type{import('vite').Plugin}
myPlugin
, module "@sveltejs/kit/vite"sveltekit()]
}; export default const config: UserConfig
@type{import('vite').UserConfig}
config
;

有关更多详细信息,包括如何控制顺序,请参见 Vite 的 configureServer 文档

它能与 Yarn 2 一起工作吗?

某种程度上可以。Plug’n'Play 功能(简称 ‘pnp’)是有问题的(它偏离了 Node 模块解析算法,并且尚不支持原生 JavaScript 模块,而 SvelteKit 以及越来越多的包都在使用这个特性)。你可以在 .yarnrc.yml 文件中使用 nodeLinker: 'node-modules' 来禁用 pnp,但使用 npm 或 pnpm 可能更容易,它们同样快速和高效,但没有兼容性问题。

如何使用 Yarn 3?

目前在最新的 Yarn(版本 3)中的 ESM 支持被认为是实验性的

下面的方法似乎可行,但你的结果可能会有所不同。

首先创建一个新应用:

yarn create svelte myapp
cd myapp

启用 Yarn Berry:

yarn set version berry
yarn install

Yarn 3 全局缓存

Yarn Berry 的一个更有趣的特性是能够拥有一个全局包缓存,而不是在磁盘上为每个项目都有多个副本。但是,将 enableGlobalCache 设置为 true 会导致构建失败,所以建议在 .yarnrc.yml 文件中添加以下内容:

nodeLinker: node-modules

这将导致包被下载到本地的 node_modules 目录中,但避免了上述问题,这是目前使用 Yarn 版本 3 的最佳选择。

在 GitHub 编辑此页面

上一页 下一页