发布于 2026-01-06 5 阅读
0

SvelteKit 1.0 - 构建一个展示你的开发文章的个人博客 🦄 ✨ 我的网站

SvelteKit 1.0 - 构建一个展示你的开发文章的个人博客🦄

✨ 我的网站

本文旨在对最新版本的 SvelteKit 进行快速概览。我们将构建一个开发者作品集和博客网站,该网站会从您的 RSS 源以及 GitHub API 获取数据。

内容


SvelteKit 简介

Svelte 已迅速成为最受欢迎的 Web 框架[SO 调查],随着 SvelteKit 1.0 的发布,预计随着更多项目采用 Svelte + SvelteKit,对 Svelte + SvelteKit 开发人员的需求将会增加。

SvelteKit 之于 Svelte,就像 Next.js 之于 React——它处理路由、布局、服务器端渲染、部署,使开发高质量的 Web 应用程序更快、更容易、更有趣。

但为什么要选择 SvekteKit 呢?……您很快就会明白!它能让您轻松搭建并运行一个功能齐全的动态 Web 应用程序,并具备所有必要的质量指标,而这些指标在传统框架中通常需要花费数天甚至数周的时间才能实现。想想看,它拥有卓越的性能、简易的部署、清晰的代码结构以及流畅的开发体验。


我们将要建造什么

我们大多数人都有博客,无论是在 Dev.to 上,还是在其他平台上。今天,我们将为您构建并部署一个个人博客,它将您在其他平台上发布的所有文章聚合到一个网站上。

由于我不知道您使用的是哪些博客平台,所以不想依赖单独的 API。不过幸运的是,有一个简单的解决方案——RSS!几乎所有现代(以及一些老牌)的博客服务商都支持 RSS,我们可以用一个 URL 轻松获取您的所有文章。(例如,在 DEV 上:)https://dev.to/feed/[your-username]

这里有一个在线演示:devolio.netlify.app/blog

以下是完整来源:@Lissy93/Devolio

GitHub 标志 Lissy93 /我的网站

✨ 我的个人主页。这是一个开发者作品集网站,将您的所有项目、博客文章和统计数据集中在一个地方。

✨ 我的网站

一个面向开发者的可复用的聚合式作品集和博客网站
aliciasykes.com

引言

这是我的个人网站。它是可配置的,所以您可以随意使用,或者使用其中的任何部分 :)

这是
一个自托管的开发者主页,用于展示您的项目、文章、代码统计等内容。
数据来自外部来源(GitHub、RSS、社交平台等),因此无需内容管理系统 (CMS)。网站
采用 SvelteKit 和 TypeScript 构建,优先考虑 SEO、性能、可访问性和兼容性。

内容

DEV.to上提供了一个关于如何构建类似功能的教程。

该代码库的镜像地址为codeberg.org/alicia/​​devolio

作品集页面- 显示来自 GitHub 的项目

作品集页面会展示您创建的项目。数据来自您的 GitHub 个人资料,您还可以在配置中添加可选的额外字段。

每个项目可以包含:名称、描述、缩略图……

要自行部署,只需 fork 它,用您的 RSS 源 URL 更新配置,然后使用一键部署选项之一即可。


让我们开始吧!

步骤 0 - 先决条件

您需要安装Node.js(LTS 或最新版本)。此外,建议您安装Git、代码编辑器(例如VS Code)以及终端。或者,您也可以使用云服务,例如Codespaces


步骤 1 - 项目设置

我们可以通过运行以下命令轻松创建项目:

npm create svelte@latest dev-blog
Enter fullscreen mode Exit fullscreen mode

出现提示时,选择 SvelteKit,然后决定是否需要 TypeScript、ESLint、Prettier、Playwright、Vitest。

接下来,我们需要进入我们的项目(使用cd dev-blog),并安装依赖项(使用npm install)。

要启动启用实时重载功能的应用程序,请运行:

npm run dev
Enter fullscreen mode Exit fullscreen mode

然后打开localhost:5173


步骤 2 - 完成设置

为了避免../../../导入语句中常见的丑陋之处,我们将在svelte.config.js文件中添加一个别名。

这可以通过alias在 下添加对象来实现config.sveltekit。这里有一个例子,我将映射./src/$src

import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/kit/vite';

/** @type {import('@sveltejs/kit').Config} */
const config = {
  preprocess: vitePreprocess(),
  kit: {
    adapter: adapter(),
    alias: {
      '$src/*': 'src/*',
    },
  },
};

export default config;
Enter fullscreen mode Exit fullscreen mode

我们可以稍后再回到 svelte.config 文件,因为我们将适配器放在这里,以便部署到各种平台,如 Netlify。

如果您想使用自己的 Prettier、ESLint 或 TypeScript 配置,可以分别更新 `prettier.config`、`eslint.config`.prettierrc.eslintrc.cjsprett` typescript.config` 文件tsconfig.json。运行npm run format`prettier.config` 应用 Prettier 规则,运行 ` npm run checkprettier.config` 进行验证。


步骤 3 - 组件

在继续之前,我们需要了解组件的基础知识。Svelte
(以及 SvelteKit)之所以如此易于使用,原因之一就是几乎所有东西都是组件。而且组件的结构非常简单。以下是一个示例:

<script>
// All JavaScript logic and imports go here
// Append lang="ts" to use TypeScript
</script>

<!-- All markup goes here -->
<p>Example Component</p>

<style>
// All styles go here, and are scoped to the current component
// Append lang="scss" to use SCSS (or another pre-processor)
p {
    color: hotpink;
}
</style>
Enter fullscreen mode Exit fullscreen mode

这是一个实际的例子,我们正在创建一个可重用的标题组件,带有可选的级别(h1、h2 等)、颜色、大小和字体。

<script lang="ts">

// Parameters
export let level: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' = 'h1'; // The semantic heading level
export let color: string | undefined = undefined; // An optional override color (defaults to accent)
export let size: string | undefined = undefined; // An optional override size (default depends on level)
export let font: string | undefined = undefined; // An optional override font (defaults to FiraCode)

// Computed values, for reactivity
$: computedColor = color ? `--headingColor: ${color};` : '';
$: computedSize = size ? `--headingSize: ${size};` : '';
$: computedFont = font ? `--headingFont: ${font};` : '';
$: computedStyles = `${computedColor} ${computedSize} ${computedFont}`;

</script>

<svelte:element this={level} style={computedStyles}>
  <slot></slot>
</svelte:element>


<style lang="scss">
  h1, h2, h3, h4, h5, h6 {
    font-weight: 700;
    transition: all .25s ease-in-out;
    font-family: var(--headingFont);
    color: var(--headingColor);
  }

  h1, h2, h3 { margin: 1rem 0; }
  h4, h5, h6 { margin: 0.5rem 0; }

  h1 { font-size: var(--headingSize, 2.8rem); }
  h2 { font-size: var(--headingSize, 2rem); }
  h3 { font-size: var(--headingSize, 1.75rem); }
  h4 { font-size: var(--headingSize, 1.5rem); }
  h5 { font-size: var(--headingSize, 1.25rem); }
  h6 { font-size: var(--headingSize, 1rem); }
</style>
Enter fullscreen mode Exit fullscreen mode

有几点需要注意:

  • 我们正在定义属性export let propName
  • 我们可以通过赋予属性默认值,使它们成为可选属性。
  • 我们可以在组件内部访问这些变量,只需将它们用花括号括起来即可。{}
  • 如果我们需要属性具有响应性,我们使用$: variabeName以下语法
  • 我们可以指定使用哪种类型的语义元素,<svelte:element this="div">
  • 将样式从 JS 传递到 CSS 的一种方法是定义 CSS 变量,并将它们传递给 style 属性。
    • (这其实没有听起来那么糟糕,因为所有样式都只作用于当前组件!)

步骤四:创建路线

接下来,我们将创建一个博客页面,所有文章都将显示在这里。(虽然也可以在首页的 `<head>` 标签内创建,src/routes/+page.svelte但这正好是一个讲解路由的好机会。)

SvelteKit 会根据目录结构自动创建路由routes。你只需要一个以路由名称命名的目录,其中包含一个 Svelte 文件+page.svelte。那么,让我们创建这个路由:touch src/routes/blog/+page.svelte——该文件的内容就是一个普通的 Svelte 组件,就像我们上面看到的那样。

<script lang="ts">
  let title = 'Blog Page';
</script>

<svelte:head>
  <title>{title}</title> 
</svelte:head>

<h2>{title}</h2>

<style lang="scss">
h2 {
  color: hotpink;
}
</style>
Enter fullscreen mode Exit fullscreen mode

我们还需要一个能够渲染单个帖子的路由,但我们希望该 URL 路径是动态的,或许可以根据帖子标题来变化。为此,我们可以创建一个名为 `<directory> [slug]` 的目录,用户访问该目录时将进入该目录。example.com/blog/example-post


第五步——特殊路线

现在正好可以提一下,我们可以让路由继承某些组件,这些组件将显示在所有页面上,例如导航栏和页脚。为此,我们可以创建一个名为 `<Layout_name>` 的布局文件,+layout.svelte由于我们希望它出现在所有页面上,因此我们将它放在 `<Layout_name>` 目录中src/routes

请在此处填写类似以下内容:

<script lang="ts">
  import NavBar from '$src/components/NavBar.svelte';
  import Footer from '$src/components/Footer.svelte';
  import { fade } from 'svelte/transition';
  import { page } from '$app/stores';
</script>

<svelte:head>
  <title>{$page.url.pathname.replaceAll('-', ' ')}</title> 
</svelte:head>

<NavBar />

<main in:fade>
  <slot />
</main>

<Footer />

<style lang="scss">
  @import "$src/styles/color-palette.scss";
  @import "$src/styles/media-queries.scss";
  @import "$src/styles/typography.scss";
  @import "$src/styles/dimensions.scss";

  :global(html) {
    scroll-behavior: smooth;
  }
  :global(::selection) {
    background-color: var(--accent);
    color: var(--background);
  }
</style>
Enter fullscreen mode Exit fullscreen mode

这里有两点需要注意:

  • 网站主要内容将显示在<slot />指定位置。
  • 我们正在添加页面过渡动画,方法是导入svelte/transition并设置in:fade页面中将要更改的部分。
  • 我们可以使用page对象(从导入$app/stores)获取当前页面的信息(例如路径)——在其前面加上一个逗号$可以保持值更新。
  • 如果我们需要在 <div> 内设置任何标签,<head>我们可以使用<svelte:head><div> 来进行设置。
  • 我们还可以弹出任何全局样式,例如重置或导入 CSS 变量。
  • 可以使用:global(body)(或任何你想要定位的选择器)来应用全局样式——但请谨慎使用!

SvelteKit 中还有一条特殊的路由,即 `<route>`,如果任何路由的函数+error.svelte中抛出错误,则会用这条路由代替当前路由进行渲染。load()

同样,我们创建这个文件src/routes/+error.svelte并填充类似这样的内容。(同样,我们可以从$page对象中获取有关当前路由的信息,包括错误代码。)

<script>
    import { page } from '$app/stores';

    const emojis = {
        // TODO add the rest!
        404: '🧱',
        420: '🫠',
        500: '💥'
    };
</script>

<h1>{$page.status} {$page.error.message}</h1>
<span style="font-size: 10em">
    {emojis[$page.status] ?? emojis[500]}
</span>
Enter fullscreen mode Exit fullscreen mode

值得注意的是,您可以通过将布局页面和错误页面嵌套在正确的路由目录中,为特定路由创建专属页面。如果您需要多个具有共同特征的布局页面,可以将这些元素提取到各自的组件中,以提高其可重用性。

现在,我们的路由目录结构应该看起来像这样:

src/routes
├── +error.svelte
├── +layout.svelte
├── +page.svelte
├── about
│  └── +page.svelte
└── blog
   ├── +page.svelte
   ├── +page.ts
   └── [slug]
      ├── +page.svelte
      └── +page.ts
Enter fullscreen mode Exit fullscreen mode

步骤 6 - 获取数据

现在是时候进入正题了!我们将从用户的 RSS 源中获取博客文章列表。

现在正好可以提一下,在每个路由的路径目录中,我们还可以有一个+page.js/+page.ts文件(与 . 并列+page.svelte)。我们将在这里进行数据获取。

为了简单起见,我们将使用解析器fast-xml-parser将 XML 响应解析为 JSON。

以下脚本仅用于从给定的 XML RSS 源中获取和解析订阅源。

import { XMLParser } from 'fast-xml-parser';

const parseXml = (rawRssData) => {
  const parser = new XMLParser();
  return parser.parse(rawRssData);
};

/** @type {import('./$types').PageLoad} */
export const load = () => {
  const RSS_URL = `https://notes.aliciasykes.com/feed`;
  const posts = fetch(RSS_URL)
    .then((response) => response.text())
    .then((rawXml) => parseXml(rawXml).rss.channel.item);
  return { posts };
};
Enter fullscreen mode Exit fullscreen mode

步骤 7 - 渲染结果

渲染返回数据的结果非常简单。在blog/+page.svelte组件中(文件旁边+page.ts),只需包含以下代码export let data——这将是我们的 fetch 函数返回的结果。现在我们可以在标记中引用这些数据了。

<script lang="ts">
  /** @type {import('./$types').PageData} */
  export let data;
</script>

Blog

{#each data.posts as post}
  <li>
    <a target="_blank" href={post.link} rel="noreferrer">
      {post.title}
    </a>
  </li>
{/each}
Enter fullscreen mode Exit fullscreen mode

你会注意到我们使用了{#each data.posts as post}for 循环,因为返回的数据是一个数组。

这是 Svelte模板语法的一部分。此外,还有其他属性,例如{#if expression}...{/if}用于条件语句或{#await expression}...{:then name}...{/await}Promise 的属性,以及许多其他有用的功能。


步骤 8 - 服务器端

目前为止,我们取得的成果非常出色,但可能还会遇到一些问题:

  • 加载时间——RSS 源文件很大,每次加载时都在客户端获取它们效率不高。
  • SEO——动态加载的内容无法被大多数搜索引擎机器人抓取。
  • CORS——某些RSS源不允许来自跨域主机的客户端请求。

幸运的是,这个问题很容易解决。重命名+page.ts后,+page.server.ts页面将在服务器端渲染,而不是在用户浏览器端渲染。这样应该可以解决这些问题,而且不需要任何代码更改。

请注意,对于服务器端代码,我们不能使用任何浏览器 API。由于我们的很多代码既可以在服务器端运行,也可以在客户端运行,因此我们需要在使用某些功能之前检查它们是否可用。我们可以通过导入 ` browser<module> ` 来实现这一点$app/environment,然后使用 ` <module>`。if (browser) { /* Can access browser API here */ }


步骤 9 - 创建帖子页面

最后,当用户点击某个帖子时,我们需要渲染它。这很简单,因为 RSS 响应已经是 HTML 格式,所以只需要使用指令@html,然后设置样式即可。

<main class="article-content">
  {@html content}
</main>
Enter fullscreen mode Exit fullscreen mode

第 10 步 - 部署!

现在,让我们来设置部署。这也是 SvelteKit 如此强大的另一个原因,因为部署到几乎任何提供商都非常简单!

  1. 安装适用于您所需提供商的适配器
    • 例如 Netlify:npm i --save-dev @sveltejs/adapter-netlify
  2. 将所述适配器导入到您的svelte.config.js文件 中
    • 例如import netlifyAdapter from '@sveltejs/adapter-netlify';
  3. 在配置对象中初始化适配器kit
    • 成套工具:{ adapter: netlifyAdapter() }
  4. 部署!现在只需前往您的 Netlify 控制面板,然后导入项目即可。

如果您希望在 VPS 上运行项目,我们可以使用@sveltejs/adapter-node。重复上述步骤,然后运行yarn build​​,并通过运行 来启动 node 服务器node build/index.js

我们可能需要使用多个适配器,以便我们的项目能够兼容多个不同的托管服务提供商。以下是我的配置文件示例,它正是用于实现这一点的:

import autoAdapter from '@sveltejs/adapter-auto';
import netlifyAdapter from '@sveltejs/adapter-netlify';
import vercelAdapter from '@sveltejs/adapter-vercel';
import nodeAdapter from '@sveltejs/adapter-node';

import { vitePreprocess } from '@sveltejs/kit/vite';

const multiAdapter = (adapters) => {
  return {
    async adapt(argument) {
      await Promise.all(adapters.map(item =>
        Promise.resolve(item).then(resolved => resolved.adapt(argument))
      ))
    }
  };
};

/** @type {import('@sveltejs/kit').Config} */
const config = {
  preprocess: vitePreprocess(),
  kit: {
    adapter: multiAdapter([autoAdapter(), netlifyAdapter(), vercelAdapter(), nodeAdapter()]),
    alias: {
      '$src/*': 'src/*',
    },
  },
};

export default config;
Enter fullscreen mode Exit fullscreen mode

(使用前别忘了先用 npm 安装所有适配器!)

最后,我们来谈谈 Docker。由于它是一种流行的部署方法,这里我展示一个Dockerfile我编写的多架构 Docker 示例,它包含构建阶段、部署阶段和一些健康检查。

它还已发布到 DockerHub(在lissy93/devolio下),因此您应该能够将其与docker run -p 3000:80 lissy93/devolio- 一起使用,或者将其用作docker-compose.yml您自己的容器的模板。


项目

为了简洁起见,我省略了一些细节,但所有代码都可以在 GitHub 上找到,所以应该能解答任何你还不明白的地方——如果还有疑问,欢迎在下方留言 :)

我还添加了一些额外的功能:

  • 为了方便使用,已将所有数据提取到配置文件中,并使其可自定义颜色和主题。
  • 我使用了一个商店来跟踪帖子(在BlogStore.ts中)。
  • 新增了加载和合并多个 RSS 源以及排序和筛选结果的功能
  • 添加了国际化功能(在Language.ts中)
  • 我创建了一个页面来展示你的项目,数据是通过 GitHub API 从你的 GitHub 获取的。
  • 还有一个联系页面,包含电子邮件表单、社交媒体链接和 GPG 密钥。
  • 添加了更多用于部署到各种云服务的适配器,并编写了 Dockerfile。

以下是一些截图(仅使用纯色主题)

博客页面(通过 RSS 获取)

博客页面

项目页面(来自 GitHub)

项目页面

社交媒体链接(统计数据来自 API)

社交媒体链接

我计划扩展这个项目,添加一些功能,并将其打造成一个易于配置、可自定义主题的开发者作品集网站,方便任何人使用。如果您想查看更新,请在 GitHub 上给这个项目点个星标 :)

如果您想为源代码做出贡献,代码在这里MIT)GitHub 上,如果您能够提交 PR,我会在致谢名单中提及您!


感谢大家一直看到这里!我知道这篇文章有点长,而且格式也跟我平时不太一样。如果您有任何反馈、问题、建议或评论,请在下方留言,我会回复的 :)

如果你喜欢这类内容
欢迎关注我获取更多资讯 :)
在 GitHub 上关注 Lissy93在推特上关注Lissy_Sykes
文章来源:https://dev.to/lissy93/sveltekit-10-build-an-blog-fetching-posts-from-your-dev-profile-29f