Skip to main content

Page options

默认情况下,SvelteKit 会首先在服务器上渲染(或预渲染)任何组件,并将其作为 HTML 发送到客户端。然后,它会在浏览器中再次渲染该组件,使其在称为激活的过程中变得交互式。因此,您需要确保组件可以在两个地方运行。然后,SvelteKit 将初始化一个路由器,以接管后续的导航。

您可以通过从+page.js+page.server.js导出选项来逐页控制这些功能,或者使用共享的+layout.js+layout.server.js来控制一组页面。要为整个应用程序定义一个选项,请从根布局导出它。子布局和页面会覆盖父布局中设置的值,例如,您可以为整个应用程序启用预渲染,然后禁用需要动态渲染的页面。

您可以在应用程序的不同区域混合匹配这些选项。例如,您可以为营销页面预渲染以获得最大速度,为动态页面服务器渲染以提高 SEO 和可访问性,并将管理员部分转换为 SPA,仅通过客户端渲染。这使得 SvelteKit 非常灵活。

预渲染

很可能您的应用中的一些路由可以在构建时表示为一个简单的 HTML 文件。这些路由可以是预渲染的。

+page.js/+page.server.js/+server
export const const prerender: trueprerender = true;

或者,您可以在根目录的 export const prerender = true 中设置,或者在 +layout.js+layout.server.js 中设置,并将除了明确标记为 不可 渲染的页面之外的所有内容进行预渲染:

+page.js/+page.server.js/+server
export const const prerender: falseprerender = false;

路由中带有 prerender = true 的将被排除在用于动态 SSR 的清单之外,使您的服务器(或无服务器/边缘函数)更小。在某些情况下,您可能希望预渲染一个路由但同时也将其包含在清单中(例如,使用 /blog/[slug] 这样的路由,您希望预渲染最新的/最受欢迎的内容但服务器端渲染长尾)—— 对于这些情况,还有一个第三种选项,’auto’:

+page.js/+page.server.js/+server
export const const prerender: "auto"prerender = 'auto';

[注意] 如果您的整个应用程序适合预渲染,您可以使用 adapter-static,这将输出适用于任何静态 Web 服务器的文件。

预渲染器将从您的应用根目录开始,为找到的任何可预渲染页面或+server.js路由生成文件。每个页面都会扫描指向其他页面(这些页面是预渲染的候选者)的<a>元素——正因为如此,您通常不需要指定哪些页面应该被访问。如果您确实需要指定预渲染器应该访问哪些页面,您可以通过config.kit.prerender.entries或从您的动态路由中导出entries函数来实现。

在预渲染时,从$app/environment导入的building的值将是true

预渲染服务器路由

与其他页面选项不同,prerender 也适用于 +server.js 文件。这些文件不受布局的影响,但如果存在,将继承从它们获取数据的页面的默认值。例如,如果 +page.js 包含此 load 函数...

+page
export const const prerender: trueprerender = true;

/** @type {import('./$types').PageLoad} */
export async function 
function load({ fetch }: {
    fetch: any;
}): Promise<any>
@type{import('./$types').PageLoad}
load
({ fetch: anyfetch }) {
const const res: anyres = await fetch: anyfetch('/my-server-route.json'); return await const res: anyres.json(); }
import type { 
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad
} from './$types';
export const const prerender: trueprerender = true; export const const load: PageLoadload:
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
type PageLoad = (event: Kit.LoadEvent<Record<string, any>, Record<string, any> | null, Record<string, any>, string | null>) => MaybePromise<void | Record<string, any>>
PageLoad
= async ({
fetch: {
    (input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
    (input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>;
}

fetch is equivalent to the native fetch web API, with a few additional features:

  • It can be used to make credentialed requests on the server, as it inherits the cookie and authorization headers for the page request.
  • It can make relative requests on the server (ordinarily, fetch requires a URL with an origin when used in a server context).
  • Internal requests (e.g. for +server.js routes) go directly to the handler function when running on the server, without the overhead of an HTTP call.
  • During server-side rendering, the response will be captured and inlined into the rendered HTML by hooking into the text and json methods of the Response object. Note that headers will not be serialized, unless explicitly included via filterSerializedResponseHeaders
  • During hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request.

You can learn more about making credentialed requests with cookies here

fetch
}) => {
const const res: Responseres = await fetch: (input: string | URL | globalThis.Request, init?: RequestInit) => Promise<Response> (+1 overload)fetch('/my-server-route.json'); return await const res: Responseres.Body.json(): Promise<any>json(); };

...然后 src/routes/my-server-route.json/+server.js 将作为可预渲染处理,如果它不包含自己的 export const prerender = false

何时不进行预渲染

基本规则是这样的:为了使页面可预渲染,任何两个直接访问该页面的用户必须从服务器获得相同的内容。

[注意] 并非所有页面都适合预渲染。任何预渲染的内容都将被所有用户看到。当然,您可以在预渲染页面的onMount中获取个性化数据,但这可能会导致用户体验较差,因为这将涉及空白初始内容或加载指示器。

请注意,您仍然可以预渲染基于页面参数加载数据的页面,例如 src/routes/blog/[slug]/+page.svelte 路由。

访问 url.searchParams 在预渲染期间是禁止的。如果您需要使用它,请确保您只在使用浏览器时这样做(例如在 onMount)。

页面中包含 操作 的页面无法预渲染,因为服务器必须能够处理 POST 请求的操作。

路由冲突

因为预渲染会写入文件系统,所以不可能有两个端点会导致目录和文件具有相同的名称。例如,src/routes/foo/+server.jssrc/routes/foo/bar/+server.js 将尝试创建 foofoo/bar,这是不可能的。

因此,出于其他原因,建议您始终包含文件扩展名 — src/routes/foo.json/+server.jssrc/routes/foo/bar.json/+server.js 将导致 foo.jsonfoo/bar.json 文件和谐共存。

对于页面,我们通过编写foo/index.html来规避这个问题,而不是直接使用foo

故障排除

如果您遇到类似“以下路由被标记为可预渲染,但未进行预渲染”的错误,那是因为相关路由(或页面是父布局的情况)中存在 export const prerender = true,但页面未被预渲染爬虫访问,因此未进行预渲染。

由于这些路由无法动态服务器端渲染,当人们尝试访问相关路由时将导致错误。有几种方法可以修复它:

  • 确保 SvelteKit 可以通过以下链接找到路由:从config.kit.prerender.entriesentries页面选项。如果动态路由(即带有[参数]的页面)在其他入口点未找到,请将链接添加到此选项中;否则,由于 SvelteKit 不知道参数应该有什么值,它们将不会被预渲染。未标记为可预渲染的页面将被忽略,并且它们到其他页面的链接将不会被爬取,即使其中一些可能是可预渲染的。
  • 确保 SvelteKit 可以通过从您已启用服务器端渲染的另一个预渲染页面中发现链接来找到该路由。
  • 修改 export const prerender = trueexport const prerender = 'auto' 。带有 'auto' 的路由可以动态服务器渲染

条目

SvelteKit 将自动发现页面进行预渲染,从 入口点 开始并遍历它们。默认情况下,所有非动态路由都被视为入口点——例如,如果您有这些路由...

/    # non-dynamic
/blog# non-dynamic
/blog/[slug]  # dynamic, because of `[slug]`

...SvelteKit 将预渲染 //blog,并在过程中发现类似 <a href="/blog/hello-world"> 的链接,这些链接为它提供了新的预渲染页面。

大多数情况下,这已经足够了。在某些情况下,像/blog/hello-world这样的页面链接可能不存在(或者在预渲染页面上可能不存在),在这种情况下,我们需要告诉 SvelteKit 它们的存在。

这可以通过config.kit.prerender.entries来完成,或者通过从一个属于动态路由的entries函数、+page.js+page.server.js+server.js中导出,来实现。

src/routes/blog/[slug]/+page.server
/** @type {import('./$types').EntryGenerator} */
export function 
function entries(): {
    slug: string;
}[]
@type{import('./$types').EntryGenerator}
entries
() {
return [ { slug: stringslug: 'hello-world' }, { slug: stringslug: 'another-blog-post' } ]; } export const const prerender: trueprerender = true;
import type { 
type EntryGenerator = () => Promise<Array<Record<string, any>>> | Array<Record<string, any>>
type EntryGenerator = () => Promise<Array<Record<string, any>>> | Array<Record<string, any>>
EntryGenerator
} from './$types';
export const const entries: EntryGeneratorentries:
type EntryGenerator = () => Promise<Array<Record<string, any>>> | Array<Record<string, any>>
type EntryGenerator = () => Promise<Array<Record<string, any>>> | Array<Record<string, any>>
EntryGenerator
= () => {
return [ { slug: stringslug: 'hello-world' }, { slug: stringslug: 'another-blog-post' } ]; }; export const const prerender: trueprerender = true;

entries 可以是一个 async 函数,允许您(例如)从 CMS 或数据库中检索帖子列表,在上面的示例中。

ssr

通常,SvelteKit 首先在服务器上渲染您的页面,然后将 HTML 发送到客户端进行激活。如果您将ssr设置为false,它将渲染一个空的“外壳”页面。如果您的页面无法在服务器上渲染(例如,您使用了仅浏览器全局变量如document),这很有用,但在大多数情况下不建议这样做(参见附录)。

+page
export const const ssr: falsessr = false;
// If both `ssr` and `csr` are `false`, nothing will be rendered!

如果您将export const ssr = false添加到您的根+layout.js中,您的整个应用将仅在客户端渲染——这实际上意味着您将应用转换为单页应用(SPA)。

[注意] 如果您的所有页面选项都是布尔值或字符串字面量,SvelteKit 将静态评估它们。如果不是,它将在服务器上导入您的 +page.js+layout.js 文件(在构建时间和如果您的应用程序不是完全静态的运行时),以便评估选项。在后一种情况下,当模块加载时,不应运行浏览器专用代码。实际上,这意味着您应该在您的 +page.svelte+layout.svelte 文件中导入浏览器专用代码。

csr

通常,SvelteKit 你的服务器端渲染的 HTML 转换为交互式的客户端渲染(CSR)页面。有些页面根本不需要 JavaScript——许多博客文章和“关于”页面都属于这一类。在这些情况下,你可以禁用 CSR:

+page
export const const csr: falsecsr = false;
// If both `csr` and `ssr` are `false`, nothing will be rendered!

禁用 CSR 不会向客户端发送任何 JavaScript。这意味着:

  • 网页应仅使用 HTML 和 CSS 工作。
  • 所有 Svelte 组件中的 <script> 标签已被移除。
  • 《<form> 元素不能 渐进增强
  • 链接由浏览器以全页导航方式处理。
  • 热模块替换(HMR)将被禁用。

您可以在开发过程中启用csr(例如,为了利用 HMR)如下:

+page
import { const dev: boolean

Whether the dev server is running. This is not guaranteed to correspond to NODE_ENV or MODE.

dev
} from '$app/environment';
export const const csr: booleancsr = const dev: boolean

Whether the dev server is running. This is not guaranteed to correspond to NODE_ENV or MODE.

dev
;

尾随斜杠

默认情况下,SvelteKit 将从 URL 中删除尾部斜杠——如果您访问/about/,它将响应重定向到/about。您可以通过trailingSlash选项更改此行为,该选项可以是'never'(默认值)、'always''ignore'之一。

与其他页面选项一样,您可以从 +layout.js+layout.server.js 导出此值,并将其应用于所有子页面。您还可以从 +server.js 文件中导出配置。

src/routes/+layout
export const const trailingSlash: "always"trailingSlash = 'always';

此选项也会影响预渲染。如果trailingSlash设置为总是,则类似/about的路径将导致生成一个about/index.html文件,否则将创建一个about.html文件,与静态 Web 服务器约定相匹配。

[注意] 不推荐忽略尾部斜杠——相对路径在这两种情况下具有不同的语义(从 ./y/x/y,但从 /x//x/y),并且 /x/x/ 被视为不同的 URL,这对 SEO 有害。

配置

使用适配器的概念,SvelteKit 能够在各种平台上运行。这些平台中的每一个可能都有特定的配置来进一步调整部署——例如,在 Vercel 上,您可以选择将应用程序的一些部分部署在边缘,而将其他部分部署在无服务器环境中。

config 是一个顶层具有键值对的对象。除此之外,具体的形状取决于您所使用的适配器。每个适配器都应该提供一个用于类型安全的 Config 接口。请查阅您适配器的文档以获取更多信息。

src/routes/+page
/** @type {import('some-adapter').Config} */
export const const config: Config
@type{import('some-adapter').Config}
config
= {
Config.runtime: stringruntime: 'edge' };
import type { Config } from 'some-adapter';

export const const config: Configconfig: Config = {
	Config.runtime: stringruntime: 'edge'
};

config 对象在顶层合并(但不在更深层)。这意味着如果您只想覆盖上层 +layout.js 中的一些值,则不需要在 +page.js 中重复所有值。例如,此布局配置...

src/routes/+layout
export const 
const config: {
    runtime: string;
    regions: string;
    foo: {
        bar: boolean;
    };
}
config
= {
runtime: stringruntime: 'edge', regions: stringregions: 'all',
foo: {
    bar: boolean;
}
foo
: {
bar: booleanbar: true } }

...此页面配置所覆盖...

src/routes/+page
export const 
const config: {
    regions: string[];
    foo: {
        baz: boolean;
    };
}
config
= {
regions: string[]regions: ['us1', 'us2'],
foo: {
    baz: boolean;
}
foo
: {
baz: booleanbaz: true } }

...这导致该页面的配置值 { runtime: 'edge', regions: ['us1', 'us2'], foo: { baz: true } }

进一步阅读

Edit this page on GitHub llms.txt