惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog RSS Feed
量子位
Y
Y Combinator Blog
大猫的无限游戏
大猫的无限游戏
B
Blog
U
Unit 42
C
Check Point Blog
I
InfoQ
aimingoo的专栏
aimingoo的专栏
雷峰网
雷峰网
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 【当耐特】
人人都是产品经理
人人都是产品经理
The Cloudflare Blog
H
Help Net Security
MongoDB | Blog
MongoDB | Blog
博客园 - Franky
H
Hackread – Cybersecurity News, Data Breaches, AI and More
J
Java Code Geeks
Microsoft Azure Blog
Microsoft Azure Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
云风的 BLOG
云风的 BLOG
宝玉的分享
宝玉的分享
爱范儿
爱范儿

ALBERTAZ

How I Built a Scannable 3D QR Code Generator | Every QR Code OpenStreetMap Data Pipeline for Canvas Maps | EveryCityMap 每日关灯:今天这题理论最少 8 步 OpenStreetMap + Canvas 城市地图实践:EveryCityMap 的五个工程取舍 OpenStreetMap + Canvas 城市地图实践:EveryCityMap 的五个工程取舍 博客 CDN 迁移到 Cloudflare R2 博客 CDN 迁移到 Cloudflare R2 Migrate my website to SvelteKit2 漫谈编辑器 漫谈编辑器 Pure ESM package Pure ESM package 又折腾博客了 又折腾博客了 Rewrite my website with Svelte Rewrite my website with Svelte 字体漫谈-网站字体最佳实践 字体漫谈-网站字体最佳实践 你不知道的 Web Components - 现状 你不知道的 Web Components - 现状 你不知道的 Web Components - 过去和未来 你不知道的 Web Components - 过去和未来 Node 应用本地 HTTPS Node 应用本地 HTTPS docker 部署 Gatsby 应用 docker 部署 Gatsby 应用 再提示『您的磁盘几乎已满』算我输 再提示『您的磁盘几乎已满』算我输 G6-Mobile 的前世今生 G6-Mobile 的前世今生
Migrate my website to SvelteKit2
https://www.albertaz.com/about · 2024-09-16 · via ALBERTAZ

为什么

之前我的博客使用的是 sveltejs 1.x 相关的周边《rewrite my blog with svelte》,但在一次更新部署时遇到了依赖兼容性问题:

Bash
@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 日将不再支持构建部署:

Bash
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,再结合具体遇到的问题补充修复。

1.依赖升级

  • svelte 升级到 4.x。
  • @sveltejs/kit 升级到 2.x,并添加 @sveltejs/vite-plugin-svelte 取代了内置 Vite 处理逻辑。
  • vite 升级到 5.x,当然后续我们需要适配 Vite 5 的变更。
  • 其他依赖如 typescriptsvelte-preprocess、svelte 部署相关的 adapter 也升级到较新版本。
JSON
"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 单独配置。
JavaScript
    adapter: vercel({
-     edge: false,
+     runtime: 'edge',

    prerender: {
-     default: true,
    },
-    vite:{}

单独移出的 vite.config.ts 很简单:

TypeScript
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

JSON
"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

JSON
"scripts": {
  "dev": "vite dev",
  "build": "vite build",
  "preview": "vite preview",
  "check": "svelte-check"
}

2.路由调整

由于 SvelteKit 基于文件系统的路由规则上发生了 breaking change(具体可看SvelteKit Routing)我的路由结构也需要调整:

  • 采用 嵌套路由,优化页面组件结构。
  • 使用新的 PageServerLoad 取代旧版 load 方法。
  • 配置 PageConfig 以支持 SSG(静态页面生成)。

组件文件命名变更:

旧版 (SvelteKit 1) 新版 (SvelteKit 2)
_layout.svelte +layout.svelte
_error.svelte +error.svelte
index@page.svelte +page.svelte

load 函数变更:

旧版 SvelteKit 1

HTML, XML
<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

HTML, XML
<script>
  export let data;
</script>

<script>
  const { posts } = data;
</script>

loadcontext="module" 变为 PageServerLoader +page.server.js 处理:

JavaScript
export async function load({ fetch }) {
  const res = await fetch('/api/posts');
  return { posts: await res.json() };
}

前面提到 sveltekit 支持对每个页面单独配置 prerender,理论上说对于博客这种静态没太多交互的站点,每个页面都应该配置成 prerender,构建和部署时就生成页面,也就是 SSG,而不是需要 server-renderSSR。但由于我不排除未来还会增加一些动态内容,在 SSR 的 manifest 中保留页面记录,所以我的配置是 export const prerender = 'auto';

JavaScript
/** @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

JavaScript
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 对象

3.Vercel 部署问题

在尝试 ssg 时,发现 Vercel 对 PageServerLoad 的支持有限,导致 searchParams 不能在 load 阶段访问,因为它在服务器端不可用。所以只好把涉及到 url 传参的接口全部改掉:

更新前的代码:

JavaScript
export async function GET({ url}) {
  const sorted = url.searchParams.get('sorted');
  const sortedDraws = await getAllDraws({ sorted });
  return new Response(JSON.stringify(sortedDraws));
}

更新后的代码:

JavaScript
export async function GET() {
  const sortedDraws = await getAllDraws({ sorted: true });
  return new Response(JSON.stringify(sortedDraws));
}

此外,我还增加了性能监控: src/routes/+layout.svelte

JavaScript
+ import { injectSpeedInsights } from '@vercel/speed-insights/sveltekit';

+ injectSpeedInsights();

总结

本次博客技术栈升级的核心变化:

  • 依赖升级到最新的 SvelteKit,避免旧版兼容性问题。
  • 采用新的 PageServerLoad 和 PageConfig 方式优化 SSG 支持,并保留未来 SSR 的可拓展⭐️。
  • 解决 searchParams 在服务器端不可用的问题。
  • 路由结构调整,提高代码可维护性。
  • 优化 Vercel 部署流程。