默认情况下,SvelteKit 会首先在服务器上渲染(或[预渲染](glossary#Prerendering))任何组件,并将其作为 HTML 发送到客户端。然后,它会在浏览器中再次渲染该组件,使其在称为[*激活*](glossary#Hydration)的过程中变得交互式。因此,您需要确保组件可以在两个地方运行。然后,SvelteKit 将初始化一个[*路由器*](routing),以接管后续的导航。 您可以通过从[`+page.js`](routing#page-page.js)或[`+page.server.js`](routing#page-page.server.js)导出选项来逐页控制这些功能,或者使用共享的[`+layout.js`](routing#layout-layout.js)或[`+layout.server.js`](routing#layout-layout.server.js)来控制一组页面。要为整个应用程序定义一个选项,请从根布局导出它。子布局和页面会覆盖父布局中设置的值,例如,您可以为整个应用程序启用预渲染,然后禁用需要动态渲染的页面。 您可以在应用程序的不同区域混合匹配这些选项。例如,您可以为营销页面预渲染以获得最大速度,为动态页面服务器渲染以提高 SEO 和可访问性,并将管理员部分转换为 SPA,仅通过客户端渲染。这使得 SvelteKit 非常灵活。 ## 预渲染 很可能您的应用中的一些路由可以在构建时表示为一个简单的 HTML 文件。这些路由可以是[*预渲染*](glossary#Prerendering)的。 ```js /// file: +page.js/+page.server.js/+server.js export const prerender = true; ``` 或者,您可以在根目录的 `export const prerender = true` 中设置,或者在 `+layout.js` 或 `+layout.server.js` 中设置,并将除了明确标记为 *不可* 渲染的页面之外的所有内容进行预渲染: ```js /// file: +page.js/+page.server.js/+server.js export const prerender = false; ``` 路由中带有 `prerender = true` 的将被排除在用于动态 SSR 的清单之外,使您的服务器(或无服务器/边缘函数)更小。在某些情况下,您可能希望预渲染一个路由但同时也将其包含在清单中(例如,使用 `/blog/[slug]` 这样的路由,您希望预渲染最新的/最受欢迎的内容但服务器端渲染长尾)—— 对于这些情况,还有一个第三种选项,'auto': ```js /// file: +page.js/+page.server.js/+server.js export const prerender = 'auto'; ``` > \[注意\] 如果您的整个应用程序适合预渲染,您可以使用 [`adapter-static`](adapter-static),这将输出适用于任何静态 Web 服务器的文件。 预渲染器将从您的应用根目录开始,为找到的任何可预渲染页面或`+server.js`路由生成文件。每个页面都会扫描指向其他页面(这些页面是预渲染的候选者)的``元素——正因为如此,您通常不需要指定哪些页面应该被访问。如果您*确实*需要指定预渲染器应该访问哪些页面,您可以通过`config.kit.prerender.entries`或从您的动态路由中导出`entries`函数来实现。 在预渲染时,从[`$app/environment`]($app-environment)导入的`building`的值将是`true`。 ### 预渲染服务器路由 与其他页面选项不同,`prerender` 也适用于 `+server.js` 文件。这些文件不受布局的影响,但如果存在,将继承从它们获取数据的页面的默认值。例如,如果 `+page.js` 包含此 `load` 函数... ```js /// file: +page.js export const prerender = true; /** @type {import('./$types').PageLoad} */ export async function load({ fetch }) { const res = await fetch('/my-server-route.json'); return await res.json(); } ``` ...然后 `src/routes/my-server-route.json/+server.js` 将作为可预渲染处理,如果它不包含自己的 `export const prerender = false` 。 ### 何时不进行预渲染 基本规则是这样的:为了使页面可预渲染,任何两个直接访问该页面的用户必须从服务器获得相同的内容。 > \[注意\] 并非所有页面都适合预渲染。任何预渲染的内容都将被所有用户看到。当然,您可以在预渲染页面的`onMount`中获取个性化数据,但这可能会导致用户体验较差,因为这将涉及空白初始内容或加载指示器。 请注意,您仍然可以预渲染基于页面参数加载数据的页面,例如 `src/routes/blog/[slug]/+page.svelte` 路由。 访问 [`url.searchParams`](load#Using-URL-data-url) 在预渲染期间是禁止的。如果您需要使用它,请确保您只在使用浏览器时这样做(例如在 `onMount`)。 页面中包含 [操作](form-actions) 的页面无法预渲染,因为服务器必须能够处理 `POST` 请求的操作。 ### 路由冲突 因为预渲染会写入文件系统,所以不可能有两个端点会导致目录和文件具有相同的名称。例如,`src/routes/foo/+server.js` 和 `src/routes/foo/bar/+server.js` 将尝试创建 `foo` 和 `foo/bar`,这是不可能的。 因此,出于其他原因,建议您始终包含文件扩展名 — `src/routes/foo.json/+server.js` 和 `src/routes/foo/bar.json/+server.js` 将导致 `foo.json` 和 `foo/bar.json` 文件和谐共存。 对于*页面*,我们通过编写`foo/index.html`来规避这个问题,而不是直接使用`foo`。 ### 故障排除 如果您遇到类似“以下路由被标记为可预渲染,但未进行预渲染”的错误,那是因为相关路由(或页面是父布局的情况)中存在 `export const prerender = true`,但页面未被预渲染爬虫访问,因此未进行预渲染。 由于这些路由无法动态服务器端渲染,当人们尝试访问相关路由时将导致错误。有几种方法可以修复它: * 确保 SvelteKit 可以通过以下链接找到路由:从[`config.kit.prerender.entries`](configuration#prerender)或[`entries`](#entries)页面选项。如果动态路由(即带有`[参数]`的页面)在其他入口点未找到,请将链接添加到此选项中;否则,由于 SvelteKit 不知道参数应该有什么值,它们将不会被预渲染。未标记为可预渲染的页面将被忽略,并且它们到其他页面的链接将不会被爬取,即使其中一些可能是可预渲染的。 * 确保 SvelteKit 可以通过从您已启用服务器端渲染的另一个预渲染页面中发现链接来找到该路由。 * 修改 `export const prerender = true` 为 `export const prerender = 'auto'` 。带有 `'auto'` 的路由可以动态服务器渲染 ## 条目 SvelteKit 将自动发现页面进行预渲染,从 *入口点* 开始并遍历它们。默认情况下,所有非动态路由都被视为入口点——例如,如果您有这些路由... ```bash / # non-dynamic /blog # non-dynamic /blog/[slug] # dynamic, because of `[slug]` ``` ...SvelteKit 将预渲染 `/` 和 `/blog`,并在过程中发现类似 `` 的链接,这些链接为它提供了新的预渲染页面。 大多数情况下,这已经足够了。在某些情况下,像`/blog/hello-world`这样的页面链接可能不存在(或者在预渲染页面上可能不存在),在这种情况下,我们需要告诉 SvelteKit 它们的存在。 这可以通过[`config.kit.prerender.entries`](configuration#prerender)来完成,或者通过从一个属于动态路由的`entries`函数、`+page.js`、`+page.server.js`或`+server.js`中导出,来实现。 ```js /// file: src/routes/blog/[slug]/+page.server.js /** @type {import('./$types').EntryGenerator} */ export function entries() { return [ { slug: 'hello-world' }, { slug: 'another-blog-post' } ]; } export const prerender = true; ``` `entries` 可以是一个 `async` 函数,允许您(例如)从 CMS 或数据库中检索帖子列表,在上面的示例中。 ## ssr 通常,SvelteKit 首先在服务器上渲染您的页面,然后将 HTML 发送到客户端进行[激活](glossary#Hydration)。如果您将`ssr`设置为`false`,它将渲染一个空的“外壳”页面。如果您的页面无法在服务器上渲染(例如,您使用了仅浏览器全局变量如`document`),这很有用,但在大多数情况下不建议这样做([参见附录](glossary#SSR))。 ```js /// file: +page.js export const ssr = 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 [将](glossary#Hydration)你的服务器端渲染的 HTML 转换为交互式的客户端渲染(CSR)页面。有些页面根本不需要 JavaScript——许多博客文章和“关于”页面都属于这一类。在这些情况下,你可以禁用 CSR: ```js /// file: +page.js export const csr = false; // If both `csr` and `ssr` are `false`, nothing will be rendered! ``` 禁用 CSR 不会向客户端发送任何 JavaScript。这意味着: * 网页应仅使用 HTML 和 CSS 工作。 * 所有 Svelte 组件中的 `