Skip to main content

页面选项

默认情况下,SvelteKit 会先在服务器上渲染(或 prerender)任何组件,然后将其作为 HTML 发送到客户端。然后,它将在浏览器中再次渲染组件,使其在名为 hydration 的过程中具有交互性。因此,你需要确保组件可以在两个地方运行。SvelteKit 随后将初始化一个 router,接管后续导航。

¥By default, SvelteKit will render (or prerender) any component first on the server and send it to the client as HTML. It will then render the component again in the browser to make it interactive in a process called hydration. For this reason, you need to ensure that components can run in both places. SvelteKit will then initialize a router that takes over subsequent navigations.

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

¥You can control each of these on a page-by-page basis by exporting options from +page.js or +page.server.js, or for groups of pages using a shared +layout.js or +layout.server.js. To define an option for the whole app, export it from the root layout. Child layouts and pages override values set in parent layouts, so — for example — you can enable prerendering for your entire app then disable it for pages that need to be dynamically rendered.

你可以在应用的不同区域中混合搭配这些选项。例如,你可以预渲染营销页面以获得最大速度,服务器渲染动态页面以实现 SEO 和可访问性,并通过仅在客户端上渲染管理部分将其转变为 SPA。这使得 SvelteKit 非常通用。

¥You can mix and match these options in different areas of your app. For example, you could prerender your marketing page for maximum speed, server-render your dynamic pages for SEO and accessibility and turn your admin section into an SPA by rendering it on the client only. This makes SvelteKit very versatile.

prerender

你的应用的至少一些路由可能可以表示为在构建时生成的简单 HTML 文件。这些路由可以是 prerendered

¥It’s likely that at least some routes of your app can be represented as a simple HTML file generated at build time. These routes can be prerendered.

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

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

¥Alternatively, you can set export const prerender = true in your root +layout.js or +layout.server.js and prerender everything except pages that are explicitly marked as not prerenderable:

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

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

¥Routes with prerender = true will be excluded from manifests used for dynamic SSR, making your server (or serverless/edge functions) smaller. In some cases you might want to prerender a route but also include it in the manifest (for example, with a route like /blog/[slug] where you want to prerender your most recent/popular content but server-render the long tail) — for these cases, there’s a third option, ‘auto’:

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

如果你的整个应用都适合预渲染,则可以使用 adapter-static,它将输出适合与任何静态 Web 服务器一起使用的文件。

¥[!NOTE] If your entire app is suitable for prerendering, you can use adapter-static, which will output files suitable for use with any static webserver.

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

¥The prerenderer will start at the root of your app and generate files for any prerenderable pages or +server.js routes it finds. Each page is scanned for <a> elements that point to other pages that are candidates for prerendering — because of this, you generally don’t need to specify which pages should be accessed. If you do need to specify which pages should be accessed by the prerenderer, you can do so with config.kit.prerender.entries, or by exporting an entries function from your dynamic route.

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

¥While prerendering, the value of building imported from $app/environment will be true.

预渲染服务器路由(Prerendering server routes)

¥Prerendering server routes

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

¥Unlike the other page options, prerender also applies to +server.js files. These files are not affected by layouts, but will inherit default values from the pages that fetch data from them, if any. For example if a +page.js contains this load function...

+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,则它将被视为可预渲染。

¥...then src/routes/my-server-route.json/+server.js will be treated as prerenderable if it doesn’t contain its own export const prerender = false.

何时不进行预渲染(When not to prerender)

¥When not to prerender

基本规则如下:对于可预渲染的页面,任何两个直接点击该页面的用户都必须从服务器获取相同的内容。

¥The basic rule is this: for a page to be prerenderable, any two users hitting it directly must get the same content from the server.

并非所有页面都适合预渲染。组件标签内的任何非代码片段声明的内容都会隐式成为 代码片段 () 的一部分:你当然可以在预渲染页面中获取 onMount 中的个性化数据,但这可能会导致较差的用户体验,因为它将涉及空白的初始内容或加载指示器。

¥[!NOTE] Not all pages are suitable for prerendering. Any content that is prerendered will be seen by all users. You can of course fetch personalized data in onMount in a prerendered page, but this may result in a poorer user experience since it will involve blank initial content or loading indicators.

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

¥Note that you can still prerender pages that load data based on the page’s parameters, such as a src/routes/blog/[slug]/+page.svelte route.

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

¥Accessing url.searchParams during prerendering is forbidden. If you need to use it, ensure you are only doing so in the browser (for example in onMount).

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

¥Pages with actions cannot be prerendered, because a server must be able to handle the action POST requests.

路由冲突(Route conflicts)

¥Route conflicts

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

¥Because prerendering writes to the filesystem, it isn’t possible to have two endpoints that would cause a directory and a file to have the same name. For example, src/routes/foo/+server.js and src/routes/foo/bar/+server.js would try to create foo and foo/bar, which is impossible.

出于这个原因,建议你始终包含文件扩展名 - src/routes/foo.json/+server.jssrc/routes/foo/bar.json/+server.js 将导致 foo.jsonfoo/bar.json 文件和谐地并排存在。

¥For that reason among others, it’s recommended that you always include a file extension — src/routes/foo.json/+server.js and src/routes/foo/bar.json/+server.js would result in foo.json and foo/bar.json files living harmoniously side-by-side.

对于页面,我们通过编写 foo/index.html 而不是 foo 来解决这个问题。

¥For pages, we skirt around this problem by writing foo/index.html instead of foo.

故障排除(Troubleshooting)

¥Troubleshooting

如果你遇到像 ‘以下路由被标记为可预渲染,但未预渲染’ 这样的错误,那是因为有问题的路由(如果是页面,则是父布局)有 export const prerender = true,但预渲染爬虫没有到达该页面,因此没有预渲染。

¥If you encounter an error like ‘The following routes were marked as prerenderable, but were not prerendered’ it’s because the route in question (or a parent layout, if it’s a page) has export const prerender = true but the page wasn’t reached by the prerendering crawler and thus wasn’t prerendered.

由于这些路由无法动态服务器渲染,因此当人们尝试访问相关路由时会导致错误。有几种方法可以解决这个问题:

¥Since these routes cannot be dynamically server-rendered, this will cause errors when people try to access the route in question. There are a few ways to fix it:

  • 确保 SvelteKit 可以通过跟踪来自 config.kit.prerender.entriesentries 页面选项的链接来找到该路由。如果通过抓取其他入口点找不到动态路由(即带有 [parameters] 的页面)的链接,则将链接添加到此选项,否则它们不会被预渲染,因为 SvelteKit 不知道参数应该具有什么值。未标记为可预渲染的页面将被忽略,并且不会抓取它们与其他页面的链接,即使其中一些页面是可预渲染的。

    ¥Ensure that SvelteKit can find the route by following links from config.kit.prerender.entries or the entries page option. Add links to dynamic routes (i.e. pages with [parameters] ) to this option if they are not found through crawling the other entry points, else they are not prerendered because SvelteKit doesn’t know what value the parameters should have. Pages not marked as prerenderable will be ignored and their links to other pages will not be crawled, even if some of them would be prerenderable.

  • 确保 SvelteKit 可以通过从启用了服务器端渲染的其他预渲染页面之一发现指向该路由的链接来找到该路由。

    ¥Ensure that SvelteKit can find the route by discovering a link to it from one of your other prerendered pages that have server-side rendering enabled.

  • export const prerender = true 更改为 export const prerender = 'auto'。带有 'auto' 的路由可以动态服务器渲染

    ¥Change export const prerender = true to export const prerender = 'auto'. Routes with 'auto' can be dynamically server rendered

entries

SvelteKit 将通过从入口点开始并抓取它们来自动发现要预渲染的页面。默认情况下,所有非动态路由都被视为入口点 - 例如,如果你有这些路由...

¥SvelteKit will discover pages to prerender automatically, by starting at entry points and crawling them. By default, all your non-dynamic routes are considered entry points — for example, if you have these routes...

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

...SvelteKit 将预渲染 //blog,并在此过程中发现像 <a href="/blog/hello-world"> 这样的链接,这些链接会为其提供要预渲染的新页面。

¥...SvelteKit will prerender / and /blog, and in the process discover links like <a href="/blog/hello-world"> which give it new pages to prerender.

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

¥Most of the time, that’s enough. In some situations, links to pages like /blog/hello-world might not exist (or might not exist on prerendered pages), in which case we need to tell SvelteKit about their existence.

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

¥This can be done with config.kit.prerender.entries, or by exporting an entries function from a +page.js, a +page.server.js or a +server.js belonging to a dynamic route:

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 或数据库中检索帖子列表,如上例所示。

¥entries can be an async function, allowing you to (for example) retrieve a list of posts from a CMS or database, in the example above.

ssr

非交互式元素包括 hydrated、、( 等)、、、、 和 。如果你将 ssr 设置为 false,它会渲染一个空白的 ‘shell’ 页面。如果你的页面无法在服务器上渲染(因为你使用仅限浏览器的全局变量,例如 document),这将非常有用,但在大多数情况下不建议这样做(参见附录)。

¥Normally, SvelteKit renders your page on the server first and sends that HTML to the client where it’s hydrated. If you set ssr to false, it renders an empty ‘shell’ page instead. This is useful if your page is unable to be rendered on the server (because you use browser-only globals like document for example), but in most situations it’s not recommended (see appendix).

+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。

¥If you add export const ssr = false to your root +layout.js, your entire app will only be rendered on the client — which essentially means you turn your app into an SPA.

即使将 ssr 设置为 false,也应该将依赖浏览器 API 的代码导入到 +page.svelte+layout.svelte 文件中。这是因为页面选项可以被覆盖,并且需要通过在服务器上导入 +page.js+layout.js 文件(如果你有运行时)或在构建时(在预渲染的情况下)进行评估。

¥[!NOTE] Even with ssr set to false, code that relies on browser APIs should be imported in your +page.svelte or +layout.svelte file instead. This is because page options can be overriden and need to be evaluated by importing your +page.js or +layout.js file on the server (if you have a runtime) or at build time (in case of prerendering).

csr

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

¥Ordinarily, SvelteKit hydrates your server-rendered HTML into an interactive client-side-rendered (CSR) page. Some pages don’t require JavaScript at all — many blog posts and ‘about’ pages fall into this category. In these cases you can disable CSR:

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

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

¥Disabling CSR does not ship any JavaScript to the client. This means:

  • 网页应仅使用 HTML 和 CSS。

    ¥The webpage should work with HTML and CSS only.

  • 所有 Svelte 组件内的 <script> 标签都将被删除。

    ¥<script> tags inside all Svelte components are removed.

  • <form> 元素不能是 逐步增强

    ¥<form> elements cannot be progressively enhanced.

  • 浏览器使用全页导航处理链接。

    ¥Links are handled by the browser with a full-page navigation.

  • 热模块替换 (HMR) 将被禁用。

    ¥Hot Module Replacement (HMR) will be disabled.

你可以在开发过程中启用 csr(例如,利用 HMR),如下所示:

¥You can enable csr during development (for example to take advantage of HMR) like so:

+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
;

trailingSlash

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

¥By default, SvelteKit will remove trailing slashes from URLs — if you visit /about/, it will respond with a redirect to /about. You can change this behaviour with the trailingSlash option, which can be one of 'never' (the default), 'always', or 'ignore'.

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

¥As with other page options, you can export this value from a +layout.js or a +layout.server.js and it will apply to all child pages. You can also export the configuration from +server.js files.

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

此选项也影响 prerendering。如果 trailingSlashalways,则像 /about 这样的路由将产生 about/index.html 文件,否则它将创建 about.html,镜像静态 Web 服务器约定。

¥This option also affects prerendering. If trailingSlash is always, a route like /about will result in an about/index.html file, otherwise it will create about.html, mirroring static webserver conventions.

不建议忽略尾部斜杠 - 两种情况下相对路径的语义不同(来自 /x./y/y,但来自 /x//x/y),并且 /x/x/ 被视为单独的 URL,这对 SEO 有害。

¥[!NOTE] Ignoring trailing slashes is not recommended — the semantics of relative paths differ between the two cases (./y from /x is /y, but from /x/ is /x/y), and /x and /x/ are treated as separate URLs which is harmful to SEO.

config

借助 adapters 的概念,SvelteKit 能够在各种平台上运行。其中每个可能都有特定的配置来进一步调整部署 - 例如在 Vercel 上,你可以选择在边缘部署应用的某些部分,在无服务器环境中部署其他部分。

¥With the concept of adapters, SvelteKit is able to run on a variety of platforms. Each of these might have specific configuration to further tweak the deployment — for example on Vercel you could choose to deploy some parts of your app on the edge and others on serverless environments.

config 是一个在顶层具有键值对的对象。除此之外,具体形状取决于你使用的适配器。每个适配器都应提供一个 Config 接口来导入以确保类型安全。有关更多信息,请参阅适配器的文档。

¥config is an object with key-value pairs at the top level. Beyond that, the concrete shape is dependent on the adapter you’re using. Every adapter should provide a Config interface to import for type safety. Consult the documentation of your adapter for more information.

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 中的所有值。例如,此布局配置...

¥config objects are merged at the top level (but not deeper levels). This means you don’t need to repeat all the values in a +page.js if you want to only override some of the values in the upper +layout.js. For example this layout configuration...

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 } }

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

¥...is overridden by this page configuration...

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 } }

¥...which results in the config value { runtime: 'edge', regions: ['us1', 'us2'], foo: { baz: true } } for that page.

进一步阅读(Further reading)

¥Further reading

上一页 下一页