从 Sapper 迁移
SvelteKit 是 Sapper 的后继者,并共享其设计的许多元素。
¥SvelteKit is the successor to Sapper and shares many elements of its design.
如果你有一个现有的 Sapper 应用并计划将其迁移到 SvelteKit,则需要进行一些更改。你可能会发现在迁移时查看 一些示例 很有帮助。
¥If you have an existing Sapper app that you plan to migrate to SvelteKit, there are a number of changes you will need to make. You may find it helpful to view some examples while migrating.
package.json
type:”module”(type: “module”)
将 "type": "module"
添加到你的 package.json
。如果你使用的是 Sapper 0.29.3 或更新版本,则可以将此步骤与其余步骤分开执行,作为增量迁移的一部分。
¥Add "type": "module"
to your package.json
. You can do this step separately from the rest as part of an incremental migration if you are using Sapper 0.29.3
or newer.
dependencies
如果你正在使用其中之一,请删除 polka
或 express
,以及任何中间件,例如 sirv
或 compression
。
¥Remove polka
or express
, if you’re using one of those, and any middleware such as sirv
or compression
.
devDependencies
从你的 devDependencies
中删除 sapper
,并将其替换为 @sveltejs/kit
和你计划使用的任何 adapter(参见 下一节)。
¥Remove sapper
from your devDependencies
and replace it with @sveltejs/kit
and whichever adapter you plan to use (see next section).
scripts
任何引用 sapper
的脚本都应更新:
¥Any scripts that reference sapper
should be updated:
sapper build
应该使用 Node adapter 变为vite build
¥
sapper build
should becomevite build
using the Node adaptersapper export
应该使用静态 adapter 变为vite build
¥
sapper export
should becomevite build
using the static adaptersapper dev
应该变成vite dev
¥
sapper dev
should becomevite dev
node __sapper__/build
应该变成node build
¥
node __sapper__/build
should becomenode build
项目文件(Project files)
¥Project files
在 src/routes
中,你的应用的大部分内容可以保留在原处,但需要移动或更新几个项目文件。
¥The bulk of your app, in src/routes
, can be left where it is, but several project files will need to be moved or updated.
配置(Configuration)
¥Configuration
你的 webpack.config.js
或 rollup.config.js
应该替换为 svelte.config.js
,如 此处 所述。Svelte 预处理器选项应移至 config.preprocess
。
¥Your webpack.config.js
or rollup.config.js
should be replaced with a svelte.config.js
, as documented here. Svelte preprocessor options should be moved to config.preprocess
.
你需要添加 adapter。sapper build
大致相当于 adapter-node,而 sapper export
大致相当于 adapter-static,但你可能更喜欢使用为要部署到的平台设计的适配器。
¥You will need to add an adapter. sapper build
is roughly equivalent to adapter-node while sapper export
is roughly equivalent to adapter-static, though you might prefer to use an adapter designed for the platform you’re deploying to.
如果你使用的文件类型插件无法由 Vite 自动处理,则需要找到 Vite 等效插件并将其添加到 Vite 配置。
¥If you were using plugins for filetypes that are not automatically handled by Vite, you will need to find Vite equivalents and add them to the Vite config.
src/client.js
此文件在 SvelteKit 中没有等效项。任何自定义逻辑(超出 sapper.start(...)
)都应在 +layout.svelte
文件中的 onMount
回调内表达。
¥This file has no equivalent in SvelteKit. Any custom logic (beyond sapper.start(...)
) should be expressed in your +layout.svelte
file, inside an onMount
callback.
src/server.js
使用 adapter-node
时,等效处理程序为 自定义服务器。否则,此文件没有直接等效项,因为 SvelteKit 应用可以在无服务器环境中运行。
¥When using adapter-node
the equivalent is a custom server. Otherwise, this file has no direct equivalent, since SvelteKit apps can run in serverless environments.
src/service-worker.js
大多数从 @sapper/service-worker
导入的内容在 $service-worker
中都有等效项:
¥Most imports from @sapper/service-worker
have equivalents in $service-worker
:
files
未更改¥
files
is unchangedroutes
已被移除¥
routes
has been removedshell
现在是build
¥
shell
is nowbuild
timestamp
现在是version
¥
timestamp
is nowversion
src/template.html
src/template.html
文件应重命名为 src/app.html
。
¥The src/template.html
file should be renamed src/app.html
.
删除 %sapper.base%
、%sapper.scripts%
和 %sapper.styles%
。将 %sapper.head%
替换为 %sveltekit.head%
,将 %sapper.html%
替换为 %sveltekit.body%
。<div id="sapper">
不再是必需的。
¥Remove %sapper.base%
, %sapper.scripts%
and %sapper.styles%
. Replace %sapper.head%
with %sveltekit.head%
and %sapper.html%
with %sveltekit.body%
. The <div id="sapper">
is no longer necessary.
src/node_modules
Sapper 应用中的常见模式是将内部库放在 src/node_modules
内的目录中。这不适用于 Vite,因此我们改用 src/lib
。
¥A common pattern in Sapper apps is to put your internal library in a directory inside src/node_modules
. This doesn’t work with Vite, so we use src/lib
instead.
页面和布局(Pages and layouts)
¥Pages and layouts
重命名文件(Renamed files)
¥Renamed files
为了消除歧义,现在的路由仅由文件夹名称组成,文件夹名称指向 +page.svelte
对应于路由。有关概述,请参阅 路由文档。以下显示了旧/新比较:
¥Routes now are made up of the folder name exclusively to remove ambiguity, the folder names leading up to a +page.svelte
correspond to the route. See the routing docs for an overview. The following shows a old/new comparison:
旧 | 新 |
---|---|
routes/about/index.svelte | 行类型 |
routes/about.svelte | 行类型 |
你的自定义错误页面组件应从 _error.svelte
重命名为 +error.svelte
。任何 _layout.svelte
都作为 DOM 元素的属性公开(以及在可能的情况下作为属性可读/可写)。忽略任何其他文件。
¥Your custom error page component should be renamed from _error.svelte
to +error.svelte
. Any _layout.svelte
files should likewise be renamed +layout.svelte
. Any other files are ignored.
导入(Imports)
¥Imports
来自 @sapper/app
的 goto
、prefetch
和 prefetchRoutes
导入应分别替换为来自 $app/navigation
的 goto
、preloadData
和 preloadCode
导入。
¥The goto
, prefetch
and prefetchRoutes
imports from @sapper/app
should be replaced with goto
, preloadData
and preloadCode
imports respectively from $app/navigation
.
应替换从 @sapper/app
导入的 stores
— 请参阅下面的 存储 部分。
¥The stores
import from @sapper/app
should be replaced — see the Stores section below.
你之前从 src/node_modules
目录中导入的任何文件都需要用 $lib
导入替换。
¥Any files you previously imported from directories in src/node_modules
will need to be replaced with $lib
imports.
预加载(Preload)
¥Preload
与以前一样,页面和布局可以导出一个函数,允许在渲染之前加载数据。
¥As before, pages and layouts can export a function that allows data to be loaded before rendering takes place.
此函数已从 preload
重命名为 load
,它现在位于其 +page.svelte
(或 +layout.svelte
)旁边的 +page.js
(或 +layout.js
)中,并且其 API 已更改。不再有两个参数 — page
和 session
— 而是一个 event
参数。
¥This function has been renamed from preload
to load
, it now lives in a +page.js
(or +layout.js
) next to its +page.svelte
(or +layout.svelte
), and its API has changed. Instead of two arguments — page
and session
— there is a single event
argument.
不再有 this
对象,因此也没有 this.fetch
、this.error
或 this.redirect
。相反,你可以从输入法中获取 fetch
,现在会抛出 error
和 redirect
。
¥There is no more this
object, and consequently no this.fetch
, this.error
or this.redirect
. Instead, you can get fetch
from the input methods, and both error
and redirect
are now thrown.
存储(Stores)
¥Stores
在 Sapper 中,你将获得对提供的存储的引用,如下所示:
¥In Sapper, you would get references to provided stores like so:
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
。
¥The page
store still exists; preloading
has been replaced with a navigating
store that contains from
and to
properties. page
now has url
and params
properties, but no path
or query
.
你可以在 SvelteKit 中以不同的方式访问它们。stores
现在是 getStores
,但在大多数情况下这是不必要的,因为你可以直接从 $app/stores
导入 navigating
和 page
。如果你使用的是 Svelte 5 和 SvelteKit 2.12 或更高版本,请考虑改用 $app/state
。
¥You access them differently in SvelteKit. stores
is now getStores
, but in most cases it is unnecessary since you can import navigating
, and page
directly from $app/stores
. If you’re on Svelte 5 and SvelteKit 2.12 or higher, consider using $app/state
instead.
路由(Routing)
¥Routing
不再支持正则表达式路由。而是使用 高级路由匹配。
¥Regex routes are no longer supported. Instead, use advanced route matching.
片段(Segments)
¥Segments
以前,布局组件收到一个指示子段的 segment
prop。这已被删除;你应该使用更灵活的 $page.url.pathname
(或 page.url.pathname
)值来派生你感兴趣的段。
¥Previously, layout components received a segment
prop indicating the child segment. This has been removed; you should use the more flexible $page.url.pathname
(or page.url.pathname
) value to derive the segment you’re interested in.
URLs
在 Sapper 中,所有相对 URL 都是针对基本 URL(通常是 /
,除非使用了 basepath
选项)而不是针对当前页面进行解析的。
¥In Sapper, all relative URLs were resolved against the base URL — usually /
, unless the basepath
option was used — rather than against the current page.
这会导致问题,但在 SvelteKit 中不再如此。相反,相对 URL 是根据当前页面(或 load
函数中的 fetch
URL 的目标页面)解析的。在大多数情况下,使用根相对(即以 /
开头)URL 更容易,因为它们的含义不依赖于上下文。
¥This caused problems and is no longer the case in SvelteKit. Instead, relative URLs are resolved against the current page (or the destination page, for fetch
URLs in load
functions) instead. In most cases, it’s easier to use root-relative (i.e. starts with /
) URLs, since their meaning is not context-dependent.
<a>属性(<a> attributes)
¥<a> attributes
sapper:prefetch
现在是data-sveltekit-preload-data
¥
sapper:prefetch
is nowdata-sveltekit-preload-data
sapper:noscroll
现在是data-sveltekit-noscroll
¥
sapper:noscroll
is nowdata-sveltekit-noscroll
端点(Endpoints)
¥Endpoints
在 Sapper 中,服务器路由 接收 Node 的 http
模块(或 Polka 和 Express 等框架提供的增强版本)公开的 req
和 res
对象。
¥In Sapper, server routes received the req
and res
objects exposed by Node’s http
module (or the augmented versions provided by frameworks like Polka and Express).
SvelteKit 的设计与应用的运行位置无关 - 它可以运行在 Node 服务器上,但也可以运行在无服务器平台或 Cloudflare Worker 中。因此,你不再直接与 req
和 res
交互。你的端点需要更新以匹配新签名。
¥SvelteKit is designed to be agnostic as to where the app is running — it could be running on a Node server, but could equally be running on a serverless platform or in a Cloudflare Worker. For that reason, you no longer interact directly with req
and res
. Your endpoints will need to be updated to match the new signature.
为了支持这种与环境无关的行为,fetch
现在在全局上下文中可用,因此你无需导入 node-fetch
、cross-fetch
或类似的服务器端获取实现即可使用它。
¥To support this environment-agnostic behavior, fetch
is now available in the global context, so you don’t need to import node-fetch
, cross-fetch
, or similar server-side fetch implementations in order to use it.
集成(Integrations)
¥Integrations
有关集成的详细信息,请参阅 integrations。
¥See integrations for detailed information about integrations.
HTML 压缩器(HTML minifier)
¥HTML minifier
Sapper 默认包含 html-minifier
。SvelteKit 不包含此功能,但你可以将其添加为 prod 依赖,然后通过 hook 使用它:
¥Sapper includes html-minifier
by default. SvelteKit does not include this, but you can add it as a prod dependency and then use it through a hook:
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, // some hydration code needs comments, so leave them in
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 文件。
¥Note that prerendering
is false
when using vite preview
to test the production build of the site, so to verify the results of minifying, you’ll need to inspect the built HTML files directly.