之前我的博客使用的是 sveltejs 1.x 相关的周边,但在一次更新部署时遇到了依赖兼容性问题:
@sveltejs/vite-plugin-svelte directly depended upon svelte, throw error
SyntaxError: Named export 'createFilter' not found. The requested module 'vite' is a CommonJS module%2C which may not support all module.exports as named exports.
此外,目前在 Vercel 上用的 Node.js 版本仍然是 16.x,Vercel 在 2025 年 1 月 31 日将不再支持构建部署:
Error: Node.js version 16.x has reached End-of-Life. Deployments created on or after 2025-01-31 will fail to build. Please set Node.js Version to 20.x in your Project Settings to use Node.js 20
所以是时候做一波升级了。
核心还是参考 svelte 官方的升级指南 svelte/v4-migration-guide,再结合具体遇到的问题补充修复。
svelte
升级到 4.x。@sveltejs/kit
升级到 2.x,并添加 @sveltejs/vite-plugin-svelte
取代了内置 Vite 处理逻辑。vite
升级到 5.x,当然后续我们需要适配 Vite 5 的变更。typescript
、svelte-preprocess
、svelte 部署相关的 adapter 也升级到较新版本。"devDependencies": {
- "@sveltejs/adapter-auto": "1.0.0-next.50",
- "@sveltejs/adapter-static": "1.0.0-next.29",
- "@sveltejs/adapter-vercel": "1.0.0-next.58",
- "@sveltejs/kit": "1.0.0-next.350",
+ "@sveltejs/adapter-auto": "^3.2.4",
+ "@sveltejs/adapter-static": "^3.0.4",
+ "@sveltejs/adapter-vercel": "^4.0.5",
+ "@sveltejs/kit": "^2.5.27",
+ "@sveltejs/vite-plugin-svelte": "^3.1.2",
- "svelte": "^3.44.0",
- "svelte-preprocess": "^4.10.5",
- "vite": "^2.9.1",
+ "svelte": "^4.2.19",
+ "svelte-preprocess": "^5.0.3",
+ "vite": "^5.4.5",
+ "typescript": "^5.x"
}
相应的,需要对 svelte.config.js
更新一些配置,主要包括:
prerender
的配置 具体的 prerender 会在页面中配置。vercel-adaptor
开启 runtime: 'edge'
。vite
配置,通过 vite.config.ts
单独配置。 adapter: vercel({
- edge: false,
+ runtime: 'edge',
prerender: {
- default: true,
},
- vite:{}
单独移出的 vite.config.ts
很简单:
import path from 'path';
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()],
resolve: {
alias: {
$draw: path.resolve('./draw'),
$blog: path.resolve('./blog'),
$note: path.resolve('./note'),
$src: path.resolve('./src'),
}
},
// allows vite access to ./posts
server: {
fs: {
allow: ['./']
}
},
});
此外,为了适配新的 Svelte 版本,我调整了 package.json 里的 dev 脚本,使其兼容新的 vite 配置。
svelte-kit
命令替换为 vite
,适配新版本。svelte-kit sync
已经不再默认需要。旧版 SvelteKit 1
"scripts": {
"dev": "svelte-kit dev",
"build": "svelte-kit build",
"preview": "svelte-kit preview",
"check": "svelte-check --tsconfig tsconfig.json",
"prepare": "svelte-kit sync"
}
新版 SvelteKit 2
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-check"
}
由于 SvelteKit 基于文件系统的路由规则上发生了 breaking change(具体可看SvelteKit Routing)我的路由结构也需要调整:
组件文件命名变更:
旧版 (SvelteKit 1) | 新版 (SvelteKit 2) |
---|---|
_layout.svelte | +layout.svelte |
_error.svelte | +error.svelte |
index@page.svelte | +page.svelte |
load
函数变更:
旧版 SvelteKit 1
<script context="module">
export async function load({ fetch }) {
const res = await fetch('/api/posts');
return { props: { posts: await res.json() } };
}
</script>
<script>
export let posts;
</script>
新版 SvelteKit 2
<script>
export let data;
</script>
<script>
const { posts } = data;
</script>
load
由 context="module"
变为 PageServerLoader +page.server.js
处理:
export async function load({ fetch }) {
const res = await fetch('/api/posts');
return { posts: await res.json() };
}
前面提到 sveltekit 支持对每个页面单独配置 prerender
,理论上说对于博客这种静态没太多交互的站点,每个页面都应该配置成 prerender
,构建和部署时就生成页面,也就是 SSG
,而不是需要 server-render
的 SSR
。但由于我不排除未来还会增加一些动态内容,在 SSR
的 manifest 中保留页面记录,所以我的配置是 export const prerender = 'auto';
:
/** @type {import('./$types').PageServerLoad} */
export async function load({ fetch }) {
const fethcDraws = await fetch('/api/draws-lite');
const fethcPosts = await fetch('/api/posts-lite');
const liteDraws = await fethcDraws.json();
const litePosts = await fethcPosts.json();
return {
liteDraws,
litePosts,
};
}
export const prerender = 'auto';
rss 之类的纯静态生成页面的路由也需要改造,改造方式就是 增加 src/routes/rss.xml/+server.js
:
export const get = async () => {
const sortedPosts = await getAllPosts({ sorted: true });
const headers = {
'Cache-Control': 'max-age=0, s-max-age=3600',
'Content-Type': 'application/xml',
};
const body = xml(sortedPosts);
- return {
- headers,
- body,
- };
+ return new Response(body, {
+ headers,
+ });
+ export const prerender = true;
}
重点在于配置 prerender
让他构建时生成,同时让 server handler 返回 Response
对象
在尝试 ssg 时,发现 Vercel 对 PageServerLoad 的支持有限,导致 searchParams 不能在 load 阶段访问,因为它在服务器端不可用。所以只好把涉及到 url 传参的接口全部改掉:
更新前的代码:
export async function GET({ url}) {
const sorted = url.searchParams.get('sorted');
const sortedDraws = await getAllDraws({ sorted });
return new Response(JSON.stringify(sortedDraws));
}
更新后的代码:
export async function GET() {
const sortedDraws = await getAllDraws({ sorted: true });
return new Response(JSON.stringify(sortedDraws));
}
此外,我还增加了性能监控: src/routes/+layout.svelte
+ import { injectSpeedInsights } from '@vercel/speed-insights/sveltekit';
+ injectSpeedInsights();
本次博客技术栈升级的核心变化: