Svelte 与 SvelteKit 的初尝试
· 技术为什么是 Svelte 与 SvelteKit?
我很喜欢 Vue,同时对 React 也有一定的知识储备,按理来说二选一就可以了。但是,当我第一次了解到 Svelte 这个前端框架的时候,它还是成功钓起了我的好奇心。
Svelte 与 React 和 Vue 不同的是,它在构建过程中进行了优化,最终构建产物要比两者更小、更快。Evan You 曾经做过一个 Svelte 和 Vue 的对比测试:在组件较少的情况下,因为 Svelte 不具有 Runtime(运行时),所以其构建产物的体积要比 Vue 的小很多;而如果组件数量来到 19 个、或是 SSR 模式下来到 13 个组件,Svelte 和 Vue 可谓不相上下。
当然,今天要写的 Sliver Complex 登陆页并不会有这么多的组件。于是,轻量的项目就用轻量的框架写,我决定用 Svelte 来重写,顺便试一下它的元框架 SvelteKit。
实践之路
bun create svelte@latest
由于 pnpm 装一次依赖、在依赖安装期间我的硬盘负载率特别高,于是我使用了 bun 作为 JavaScript 打包器 / Node.js 包管理器。所以,我直接用 bun 初始化了一个 SvelteKit 项目,而事实证明 bun 和 SvelteKit 的兼容性还是可以的。
配置 UnoCSS
安装
SvelteKit 默认使用 Vite 作为打包器,并且它贴心的给你保留了 vite.config.ts
配置文件。因此,我们可以直接使用 UnoCSS 的 Vite 集成,那就先来安装一下吧:
bun add -D unocss
接着来动一下 vite.config.ts
:
import { sveltekit } from "@sveltejs/kit/vite"
import { defineConfig } from "vite"
import UnoCSS from "unocss/vite"
export default defineConfig({
plugins: [UnoCSS(), sveltekit()]
})
UnoCSS 的文档 贴心地告诉你 要将 UnoCSS 插件放在
@sveltejs/vite-plugin-svelte
前面,SvelteKit 同理。
如果你想要让 UnoCSS 的规则集也用上 SvelteKit 的 class:foo
或 class:foo={bar}
用法的话,那就多加一个 extractor 吧:
import { sveltekit } from "@sveltejs/kit/vite"
import UnoCSS from "unocss/vite"
import extractorSvelte from "@unocss/extractor-svelte"
/** @type {import('vite').UserConfig} */
const config = {
plugins: [
UnoCSS({
extractors: [extractorSvelte()]
}),
sveltekit()
]
}
引入 uno.css
UnoCSS 并没有告诉我们要在 Svelte 或 SvelteKit 的哪个地方引入 uno.css
(或者说人家根本没这个义务)。而 Svelte 每一个组件的 <style></style>
样式块中的样式都不会溢出这个组件,导入到特定一个组件是不可能的。
不过,我们可以创建一个 Layout(布局)、并在那里引入 uno.css
,这样可以绕开上面提到的特性。让我们在 src/routes
下创建一个 +layout.svelte
组件:
<script lang="ts">
import "uno.css"
</script>
<slot />
做完之后就可以开始在 SvelteKit 里尽情使用 UnoCSS 啦。
深浅色模式:svelte-theme-select
在用 Nuxt 重构我的博客时,我使用了 @nuxt/color-mode
来接管网站的深浅色模式。但现在是在 SvelteKit,自己写一套深浅色切换的代码是绝对不可能的,而且很大可能导致浏览器加载完毕才切换主题、导致用户看到一瞬间的闪烁。不过,SvelteKit 的插件也不少,这里我们就用 svelte-theme-select 来接管深浅色模式即可。
bun add svelte-theme-select
要引入 svelte-theme-select 的地方同样是 Layout 组件。由于我只需要其跟随用户偏好(一般是系统设置)切换深浅色主题,所以我只需要简单插入几行代码即可。
<script lang="ts">
import "uno.css";
import { createThemeSwitcher, Theme } from 'svelte-theme-select'; // 导入需要的内容
createThemeSwitcher(); // 执行函数
</script>
<slot />
<Theme /> // 插入相关的主题切换 JavaScript 代码
同时,因为我使用了 UnoCSS 的默认预设,其包含了 TailwindCSS 的大部分规则集,所以可以按照 TailwindCSS 编写深色主题时的方法来即可:
<div class="bg-white dark:bg-black">Background</div>
数据获取与遍历:显示 GitHub 组织下所有人可见的仓库
在设计这个登陆页的时候,我突发奇想:要是能显示 GitHub 组织下所有人可见的仓库,那该多好!
事实是可以做到,通过 GitHub API 获取、在 SvelteKit 上 map 一下需要的信息即可。这里先贴上代码:
<script lang="ts">
import { onMount } from 'svelte';
interface Repo {
name: string;
html_url: string;
description: string;
}
let repos: Repo[] = [];
let isLoading = true;
onMount(async () => {
const response = await fetch('https://api.github.com/users/s-complex/repos');
const data: Repo[] = await response.json();
repos = data.map((repo) => ({
name: repo.name,
html_url: repo.html_url,
description: repo.description
}));
isLoading = false;
});
</script>
<hgroup>
<h1>Project</h1>
<p>List of projects that are visible to everyone in this lab.</p>
</hgroup>
{#if isLoading}
<p>Loading...</p>
{:else}
<div class=":uno: grid grid-cols-1 sm:grid-cols-2 gap-4">
{#each repos as repo (repo.name)}
<a href={repo.html_url}>
<div class=":uno: px-4 py-2 shadow-md hover:shadow-lg rounded">
<h2>{repo.name}</h2>
<p>{repo.description}</p>
</div>
</a>
{/each}
</div>
{/if}
为了让访客能够知道数据获取的状态,我设定了一个 isLoading
值、默认是 true
,它将在数据获取之后改为 false
。在判断和数据遍历这方面和 Vue 将 if 判断和 for 遍历直接写在 HTML 代码或 <template></template>
模板块上不同,Svelte 是以 {#if}
和 {#each}
包裹住需要的代码来运行。
Svelte Head
虽然 src
下有一个 app.html
,但你打开之后会发现 <head></head>
这里不太寻常:
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
这是因为 Svelte 支持用 <svelte:head></svelte:head>
在各个组件定义头信息。我是直接简单粗暴的定义了一下:
<svelte:head>
<title>Sliver Complex</title>
<meta name="description" content="The landing site of Sliver Complex.">
<link rel="icon" href="https://library.gxres.net/images/icons/favicon.webp">
</svelte:head>
这个登陆页暂时不需要动态标题,所以就不在各个页组件定义了。
预渲染(SSG)
因为各个托管平台有着不同的部署方式,所以 SvelteKit 一一做了对应的「适配器(Adapter)」。因为我只喜欢预渲染(SSG),而 SvelteKit 自带的 Auto Adapter 我不知道能不能 SSG,干脆就自己装一个了,反正也不麻烦。
bun add -D @sveltejs/adapter-static
接下来,我们要在 svelte.config.js
将原本的 Adapter 换成 SSG 的:
import adapter from "@sveltejs/adapter-static" // 换掉这一行就好了
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: vitePreprocess(),
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter()
}
}
export default config
最后,在 src/routes
下添加一个 +layout.ts
,并在里面写一行:
export const prerender = true
如果你需要 Fallback(比如 SPA),你可以改为
false
。
小结
最终的成品确实在我的期待之中。重点是:我又多学了一门前端框架(x
如果你需要一个轻量的框架、给一个轻量的项目使用,快来用 Svelte 吧非常好用.jpg