核心概念
页面选项
在 GitHub 上编辑此页面默认情况下,SvelteKit 将首先在服务器上呈现(或预呈现)任何组件,并将其作为 HTML 发送到客户端。然后,它将在浏览器中再次呈现该组件,以使其在称为水化的过程中具有交互性。因此,你需要确保组件可以在两个地方运行。然后,SvelteKit 将初始化一个路由器,该路由器将接管后续导航。
你可以通过从+page.js
或+page.server.js
导出选项,或使用共享的+layout.js
或+layout.server.js
为组页面来逐页控制这些选项。要为整个应用定义一个选项,请从根布局中导出它。子布局和页面会覆盖父布局中设置的值,因此,例如,你可以为整个应用启用预呈现,然后为需要动态呈现的页面禁用它。
你可以在应用的不同区域混合和匹配这些选项。例如,你可以预呈现你的营销页面以获得最快的速度,为 SEO 和可访问性服务器呈现你的动态页面,并通过仅在客户端呈现你的管理部分将其变成 SPA。这使得 SvelteKit 非常通用。
预呈现永久链接
你的应用至少有一些路由很可能可以表示为在构建时生成的简单 HTML 文件。这些路由可以预呈现。
ts
export constprerender = true;
ts
export constprerender = true;
或者,你可以在根目录的 +layout.js
或 +layout.server.js
中设置 export const prerender = true
,并预渲染除明确标记为不可预渲染的页面之外的所有内容
ts
export constprerender = false;
ts
export constprerender = false;
具有 prerender = true
的路由将从用于动态 SSR 的清单中排除,从而使你的服务器(或无服务器/边缘函数)更小。在某些情况下,你可能希望预渲染一个路由,但也要将其包含在清单中(例如,对于像 /blog/[slug]
这样的路由,你希望预渲染最新/最受欢迎的内容,但服务器渲染长尾)——对于这些情况,有一个第三个选项,“auto”
ts
export constprerender = 'auto';
ts
export constprerender = 'auto';
如果你的整个应用程序适合预渲染,你可以使用
adapter-static
,它将输出适合与任何静态 Web 服务器一起使用的文件。
预渲染器将从应用程序的根目录开始,并为它找到的任何可预渲染页面或 +server.js
路由生成文件。每个页面都会被扫描,以查找指向其他适合预渲染的页面的 <a>
元素——因此,你通常不需要指定应该访问哪些页面。如果你确实需要指定哪些页面应该被预渲染器访问,你可以使用 config.kit.prerender.entries
来指定,或者通过从你的动态路由导出一个 entries
函数来指定。
在预渲染时,从 $app/environment
导入的 building
的值将为 true
。
预渲染服务器路由永久链接
与其他页面选项不同,prerender
也适用于 +server.js
文件。这些文件不受布局的影响,但会继承从它们获取数据的页面(如果有)的默认值。例如,如果一个 +page.js
包含这个 load
函数...
ts
export constprerender = true;/** @type {import('./$types').PageLoad} */export async functionload ({fetch }) {constres = awaitfetch ('/my-server-route.json');return awaitres .json ();}
ts
import type {PageLoad } from './$types';export constprerender = true;export constload :PageLoad = async ({fetch }) => {constres = awaitfetch ('/my-server-route.json');return awaitres .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.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
,但页面实际上未预渲染,因为预渲染爬虫未到达它。
由于这些路由无法动态地服务器渲染,因此当人们尝试访问相关路由时,这将导致错误。有两种方法可以解决此问题
- 通过关注
config.kit.prerender.entries
或entries
页面选项中的链接,确保 SvelteKit 可以找到路由。如果未通过爬取其他入口点找到动态路由(即带有[parameters]
的页面),请将指向这些路由的链接添加到此选项,否则它们不会被预渲染,因为 SvelteKit 不知道参数应该具有什么值。未标记为可预渲染的页面将被忽略,并且指向其他页面的链接不会被爬取,即使其中一些页面是可预渲染的。 - 将
export const prerender = true
更改为export const prerender = 'auto'
。具有'auto'
的路由可以动态地在服务器上呈现
entries永久链接
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
来完成,或者通过从属于动态路由的 +page.js
、+page.server.js
或 +server.js
导出一个 entries
函数来完成
ts
/** @type {import('./$types').EntryGenerator} */export functionentries () {return [{slug : 'hello-world' },{slug : 'another-blog-post' }];}export constprerender = true;
ts
import type {EntryGenerator } from './$types';export constentries :EntryGenerator = () => {return [{slug : 'hello-world' }, {slug : 'another-blog-post' }];};export constprerender = true;
entries
可以是一个 async
函数,它允许你(例如)从 CMS 或数据库中检索文章列表,如上面的示例所示。
ssr永久链接
通常,SvelteKit 首先在服务器上呈现你的页面,并将该 HTML 发送到客户端,客户端会对其进行 水合。如果你将 ssr
设置为 false
,它将呈现一个空的“外壳”页面。如果你无法在服务器上呈现你的页面(例如,你使用了仅限浏览器的全局变量,如 document
),这将很有用,但在大多数情况下不建议这样做(请参阅附录)。
ts
export constssr = false;// If both `ssr` and `csr` are `false`, nothing will be rendered!
ts
export constssr = false;// If both `ssr` and `csr` are `false`, nothing will be rendered!
如果你将 export const ssr = false
添加到根 +layout.js
,你的整个应用程序将仅在客户端呈现——这实际上意味着你将你的应用程序变成了 SPA。
csr永久链接
通常,SvelteKit 会将你的服务器呈现的 HTML 水合 为一个交互式的客户端呈现 (CSR) 页面。有些页面根本不需要 JavaScript——许多博客文章和“关于”页面属于这一类。在这些情况下,你可以禁用 CSR
ts
export constcsr = false;// If both `csr` and `ssr` are `false`, nothing will be rendered!
ts
export constcsr = false;// If both `csr` and `ssr` are `false`, nothing will be rendered!
禁用 CSR 不会向客户端发送任何 JavaScript。这意味着
- 该网页应该仅使用 HTML 和 CSS 工作。
- 所有 Svelte 组件内的
<script>
标签都会被移除。 <form>
元素无法 渐进增强。- 链接由浏览器通过全页面导航进行处理。
trailingSlash永久链接
默认情况下,SvelteKit 将从 URL 中移除尾部斜杠——如果你访问 /about/
,它将响应重定向到 /about
。你可以使用 trailingSlash
选项更改此行为,它可以是 'never'
(默认值)、'always'
或 'ignore'
。
与其他页面选项一样,你可以从 +layout.js
或 +layout.server.js
导出此值,它将应用于所有子页面。你还可以从 +server.js
文件导出配置。
ts
export consttrailingSlash = 'always';
ts
export consttrailingSlash = 'always';
此选项还会影响 预渲染。如果 trailingSlash
为 always
,则类似于 /about
的路由将生成一个 about/index.html
文件,否则它将创建 about.html
,反映静态 Web 服务器约定。
不建议忽略尾部斜杠——两种情况下相对路径的语义不同(从
/x
中的./y
为/y
,但从/x/
中为/x/y
),并且/x
和/x/
被视为单独的 URL,这对 SEO 有害。
config永久链接
凭借 适配器 的概念,SvelteKit 能够在各种平台上运行。其中每一个可能都有特定的配置来进一步调整部署——例如,在 Vercel 上,你可以选择将应用程序的某些部分部署在边缘,而其他部分部署在无服务器环境中。
config
是一个在顶层具有键值对的对象。除此之外,具体形状取决于你使用的适配器。每个适配器都应该提供一个 Config
接口以导入类型安全。有关更多信息,请查阅适配器的文档。
ts
/** @type {import('some-adapter').Config} */export constconfig = {runtime : 'edge'};
ts
import type {Config } from 'some-adapter';export constconfig :Config = {runtime : 'edge',};
config
对象在顶层合并(但不在更深层次)。这意味着如果你只想覆盖上层 +layout.js
中的一些值,则无需在 +page.js
中重复所有值。例如,此布局配置...
ts
export constconfig = {runtime : 'edge',regions : 'all',foo : {bar : true}}
ts
export constconfig = {runtime : 'edge',regions : 'all',foo : {bar : true,},};
...被此页面配置覆盖...
ts
export constconfig = {regions : ['us1', 'us2'],foo : {baz : true}}
ts
export constconfig = {regions : ['us1', 'us2'],foo : {baz : true,},};
...这会导致该页面的配置值{ runtime: 'edge', regions: ['us1', 'us2'], foo: { baz: true } }
。