从 Sapper 迁移
SvelteKit 是 Sapper 的继任者,它们共享许多设计元素。
如果您有一个现有的 Sapper 应用想要迁移到 SvelteKit,您需要进行一些更改。在迁移过程中,查看一些示例可能会有所帮助。
package.json
type: “module”
在您的 package.json
中添加 "type": "module"
。如果您使用的是 Sapper 0.29.3 或更新版本,您可以将此步骤作为渐进式迁移的一部分单独进行。
dependencies
如果您使用了 polka
或 express
,请移除它们,以及任何中间件,如 sirv
或 compression
。
devDependencies
从您的 devDependencies
中移除 sapper
,并替换为 @sveltejs/kit
和您计划使用的适配器(参见下一节)。
scripts
任何引用 sapper
的脚本都应该更新:
sapper build
应改为使用 Node 适配器的vite build
sapper export
应改为使用静态适配器的vite build
sapper dev
应改为vite dev
node __sapper__/build
应改为node build
项目文件
您的应用程序的主体部分,即 src/routes
中的内容可以保持原样,但需要移动或更新几个项目文件。
配置
您的 webpack.config.js
或 rollup.config.js
应该替换为 svelte.config.js
,如此处所述。Svelte 预处理器选项应移至 config.preprocess
。
您需要添加一个适配器。sapper build
大致相当于 adapter-node,而 sapper export
大致相当于 adapter-static,不过您可能更倾向于使用专门为您部署的平台设计的适配器。
如果您之前使用的插件用于处理 Vite 不会自动处理的文件类型,您需要找到 Vite 的等效插件并将它们添加到 Vite 配置中。
src/client.js
此文件在 SvelteKit 中没有对应的文件。任何自定义逻辑(除了 sapper.start(...)
)都应该在您的 +layout.svelte
文件中的 onMount
回调内表达。
src/server.js
当使用 adapter-node
时,对应的是自定义服务端。否则,此文件没有直接对应的文件,因为 SvelteKit 应用可以在无服务端环境中运行。
src/service-worker.js
大多数从 @sapper/service-worker
导入的内容在 $service-worker
中都有对应项:
files
保持不变routes
已被移除shell
现在是build
timestamp
现在是version
src/template.html
src/template.html
文件应重命名为 src/app.html
。
移除 %sapper.base%
、%sapper.scripts%
和 %sapper.styles%
。将 %sapper.head%
替换为 %sveltekit.head%
,将 %sapper.html%
替换为 %sveltekit.body%
。<div id="sapper">
不再需要。
src/node_modules
Sapper 应用中的一个常见模式是将内部库放在 src/node_modules
目录中。这在 Vite 中不起作用,所以我们改用 src/lib
。
页面和布局
重命名文件
现在路由仅由文件夹名称构成以消除歧义,通向 +page.svelte
的文件夹名称对应于路由。查看路由文档了解概述。以下是旧文件和新文件的对比:
旧 | 新 |
---|---|
routes/about/index.svelte | routes/about/+page.svelte |
routes/about.svelte | routes/about/+page.svelte |
您的自定义错误页面组件应从 _error.svelte
重命名为 +error.svelte
。任何 _layout.svelte
文件同样应重命名为 +layout.svelte
。其他文件将被忽略。
引入
从 @sapper/app
导入的 goto
、prefetch
和 prefetchRoutes
应分别替换为从 $app/navigation
导入的 goto
、preloadData
和 preloadCode
。
从 @sapper/app
的 stores
导入应被替换 — 请参阅下面的Stores (存储)部分。
之前从 src/node_modules
目录中导入的任何文件现在需要用 $lib
导入替换。
Preload
如之前一样,页面和布局可以导出一个函数,该函数允许在渲染发生之前加载数据。
此函数名称从 preload
重命名为 load
,现在它位于紧邻其 +page.svelte
(或 +layout.svelte
)的 +page.js
(或 +layout.js
)中,且其 API 已更改。不再是两个参数 — page
和 session
— 而是一个单一的 event
参数。
不再有 this
对象,因此也没有 this.fetch
、this.error
或 this.redirect
。取而代之的是,您可以从输入方法中获取 fetch
,error
和 redirect
现在通过抛出实现。
存储 (Stores)
在 Sapper 中,您可以这样获取提供的存储的引用:
import { module "@sapper/app"
stores } from '@sapper/app';
const { const preloading: any
preloading, const page: any
page, const session: any
session } = module "@sapper/app"
stores();
page
存储仍然存在;preloading
被替换为具有 from
和 to
属性的 navigating
存储。page
现在有 url
和 params
属性,但没有 path
或 query
。
在 SvelteKit 中访问它们的方式有所不同。stores
现在是 getStores
,但在大多数情况下,这是不必要的,因为您可以直接从 $app/stores
导入 navigating
和 page
。如果您使用的是 Svelte 5 和 SvelteKit 2.12 或更高版本,请考虑使用 $app/state
。
路由 (Routing)
不再支持正则路由。取而代之的是使用高级路由匹配。
段 (Segments)
以前,布局组件会接收一个 segment
属性,指示子段。这已被移除;您应该使用更灵活的 $page.url.pathname
(或 page.url.pathname
)值来派生您感兴趣的段。
URL
在 Sapper 中,所有相对 URL 都是相对于基本 URL 解析的 — 通常是 /
,除非使用了 basepath
选项 — 而不是相对于当前页面。
这导致了一些问题,现在在 SvelteKit 中不再这样了。相对 URL 是相对于当前页面解析的(对于 load
函数中的 fetch
URL,解析为目标页面)。在大多数情况下,使用根相对 URL(即以 /
开头)更简单,因为它们的意义不依赖于上下文。
<a> 属性
sapper:prefetch
现在是data-sveltekit-preload-data
sapper:noscroll
现在是data-sveltekit-noscroll
端点 (Endpoints)
在 Sapper 中,服务端路由 接收由 Node 的 http
模块暴露的 req
和 res
对象(或由框架如 Polka 和 Express 提供增强的版本)。
SvelteKit 被设计成可以与应用运行的环境无关 — 它可以运行在 Node 服务端,也可以运行在无服务端平台或 Cloudflare Worker 中。因此,您不能再直接与 req
和 res
交互。您的端点需要更新以符合新签名。
为了支持这种与环境无关的行为,fetch
现在在全局上下文中可用,因此不需要引入 node-fetch
、cross-fetch
或类似的服务端 fetch 实现来使用它。
集成 (Integrations)
参阅集成文档了解集成的详细信息。
HTML 压缩器
Sapper 默认包含 html-minifier
。SvelteKit 没有包含此功能,但您可以将其添加为生产依赖,然后通过钩子使用:
import { module "html-minifier"
minify } from 'html-minifier';
import { const building: boolean
SvelteKit analyses your app during the build
step by running it. During this process, building
is true
. This also applies during prerendering.
building } from '$app/environment';
const const minification_options: {
collapseBooleanAttributes: boolean;
collapseWhitespace: boolean;
conservativeCollapse: boolean;
decodeEntities: boolean;
html5: boolean;
ignoreCustomComments: RegExp[];
minifyCSS: boolean;
... 8 more ...;
sortClassName: boolean;
}
minification_options = {
collapseBooleanAttributes: boolean
collapseBooleanAttributes: true,
collapseWhitespace: boolean
collapseWhitespace: true,
conservativeCollapse: boolean
conservativeCollapse: true,
decodeEntities: boolean
decodeEntities: true,
html5: boolean
html5: true,
ignoreCustomComments: RegExp[]
ignoreCustomComments: [/^#/],
minifyCSS: boolean
minifyCSS: true,
minifyJS: boolean
minifyJS: false,
removeAttributeQuotes: boolean
removeAttributeQuotes: true,
removeComments: boolean
removeComments: false, // 一些 hydration 代码需要注释,所以保留它们
removeOptionalTags: boolean
removeOptionalTags: true,
removeRedundantAttributes: boolean
removeRedundantAttributes: true,
removeScriptTypeAttributes: boolean
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: boolean
removeStyleLinkTypeAttributes: true,
sortAttributes: boolean
sortAttributes: true,
sortClassName: boolean
sortClassName: true
};
/** @type {import('@sveltejs/kit').Handle} */
export async function function handle(input: {
event: RequestEvent;
resolve(event: RequestEvent, opts?: ResolveOptions): MaybePromise<Response>;
}): MaybePromise<...>
handle({ event: RequestEvent<Partial<Record<string, string>>, string | null>
event, resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>
resolve }) {
let let page: string
page = '';
return resolve: (event: RequestEvent, opts?: ResolveOptions) => MaybePromise<Response>
resolve(event: RequestEvent<Partial<Record<string, string>>, string | null>
event, {
ResolveOptions.transformPageChunk?(input: {
html: string;
done: boolean;
}): MaybePromise<string | undefined>
Applies custom transforms to HTML. If done
is true, it’s the final chunk. Chunks are not guaranteed to be well-formed HTML
(they could include an element’s opening tag but not its closing tag, for example)
but they will always be split at sensible boundaries such as %sveltekit.head%
or layout/page components.
transformPageChunk: ({ html: string
html, done: boolean
done }) => {
let page: string
page += html: string
html;
if (done: boolean
done) {
return const building: boolean
SvelteKit analyses your app during the build
step by running it. During this process, building
is true
. This also applies during prerendering.
building ? module "html-minifier"
minify(let page: string
page, const minification_options: {
collapseBooleanAttributes: boolean;
collapseWhitespace: boolean;
conservativeCollapse: boolean;
decodeEntities: boolean;
html5: boolean;
ignoreCustomComments: RegExp[];
minifyCSS: boolean;
... 8 more ...;
sortClassName: boolean;
}
minification_options) : let page: string
page;
}
}
});
}
请注意,当使用 vite preview
测试生产构建的站点时,prerendering
为 false
,因此要验证压缩效果,您需要直接检查生成的 HTML 文件。