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

推荐订阅源

Y
Y Combinator Blog
D
Docker
有赞技术团队
有赞技术团队
D
DataBreaches.Net
The GitHub Blog
The GitHub Blog
爱范儿
爱范儿
H
Help Net Security
美团技术团队
MyScale Blog
MyScale Blog
B
Blog RSS Feed
C
Check Point Blog
Microsoft Security Blog
Microsoft Security Blog
阮一峰的网络日志
阮一峰的网络日志
A
About on SuperTechFans
小众软件
小众软件
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
GbyAI
GbyAI
G
Google Developers Blog
月光博客
月光博客
Google DeepMind News
Google DeepMind News
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Blog — PlanetScale
Blog — PlanetScale
MongoDB | Blog
MongoDB | Blog
F
Fortinet All Blogs

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 Migrate my website to SvelteKit2 漫谈编辑器 漫谈编辑器 Pure ESM package Pure ESM package 又折腾博客了 又折腾博客了 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 的前世今生
Rewrite my website with Svelte
https://www.albertaz.com/about · 2022-06-03 · via ALBERTAZ

The page you’re looking at is powered by Svelte. The previous version of the site was based on Gatsby, and even earlier on Hexo. When I came across Svelte, I was amazed by its features. So i decided to rewrite my site and take the opportunity to dive into Svelte and its ecosystem.

index-desktop

It took me five weekends to finish the rewriting process. Now, I’d like to share what I did.

Framework and Development

SvelteKit is a framework for building web applications. SvelteKit for Svelte is similar to Next.js for React, Nuxt.js for Vue. it offer build tools, filesystem-based routing and rendering practices for us to setup Svelte app quickly.

For development, SvelteKit integrates Vite, which makes both development and production builds extremely fast.

Additionally, I added config to make vite resolve my posts files from project root folder:

JavaScript
// svelte.config.js
    vite: {
      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: ['./']
        }
      },
    }
``

## Routing

With SvelteKit's filesystem-based routing, anything inside `src/routes` directory will be compiled to a page and accessible via its path.

On my website, I have added pages for Index (Home), About, Uses, Blog, and Draws. The Blog page is further divided into post, notes and slides: 
```bash
.
├── about@page.svelte
├── blog
│   ├── [slug]
│   │   └── index@post-page.svelte
│   └── index@page.svelte
├── draw
│   ├── [slug]
│   │   └── index@draw-page.svelte
│   └── index@page.svelte
├── index@page.svelte
├── note
│   ├── [slug]
│   │   └── index@post-page.svelte
│   └── index@page.svelte
├── slide
│   └── index@page.svelte
└── uses.svx

The index@page.svelte files in blog, draw, note and slide folders serve as index pages for all my posts, drawings, notes and slides.

These pages can have a load function that fetches data for my blog posts before the component is created, and it can even run on the server side with prerender opton:

JavaScript
<script context="module">
  export const load = async({ fetch }) => {

    const fethcPosts = await fetch('/api/posts?sorted=true&reading=true');
    const allSortedPosts = await fethcPosts.json();
    return {
      props: {
        allSortedPosts,
      },
    };
  }
</script>

For the post detail pages, it is necessary to use a template to display different pages for each post. Therefore, I utilized dynamic routing with the index@post-page.svelte file wrapped in [slug] folder. The load function in these page only needs to fetch post data based on the slug:

JavaScript
<script context="module">
  export const load = async({ params, fetch }) => {
    const { slug } = params;

    const fethcPost = await fetch(`/api/post/${slug}`);
    const post = await fethcPost.json();

    if (!post) {
      return {
        status: 404,
        error: 'Post not found'
      }
    };

    return {
      props: {
        post,
      },
    };
  }
</script>

Layout

SvelteKit supports filesystem-based routing, allowing us to setup layouts, including nested and named layouts.

I added the following 4 layout files:

Bash
├── __layout-draw-page@default.svelte
├── __layout-page@default.svelte
├── __layout-post-page@default.svelte
├── __layout.svelte

__layout.svelte serves as the root layout for my website, defining the main structure of the pages:

HTML, XML
<Statistic />
<ThemeContext>
  <Header {pathname} />
  <div id="app-container" class="container">
    <slot />
  </div>
  <Footer />
</ThemeContext>

The other three files are named layouts, inherited directly from the __layout.svelte.

__layout-draw-page@default.svelte and __layout-post-page@default.svelte are layouts for blog detail pages.

__layout-page@default.svelte layouts the other pages.

You can see i used names page, draw-page and post-page for different layouts, which correspond to different page files such as index@post-page.svelte and about@page.svelte.

Styling and Typography

I prefer styling with Less and PostCSS. I like the feeling of writing original CSS Code rather than Atomic or Utility-first CSS like Tailwind CSS. As svelte-preprocess can automatically transforms the code to provide support for PostCSS and Less, I configure the svelte.config.js:

JavaScript
import sveltePreprocess from "svelte-preprocess";
...
const config = {
  preprocess: [
    sveltePreprocess({
      postcss: true,
    }),
    ...
  ],
};
export default config;

Post page with Markdown

To render Markdown files, i used mdsvex, which converts Markdown to HTML, supports using Markdown files as component, and allows components to be used within Markdown files via .svx file.

You can see i added uses.svx , combining svelte components, html and markdown syntax:

HTML, XML
<Seo {...seoProps} />


<div class="uses-page content">

# Uses

## Hardware

For normal markdown files, I configured mdsvex into svelte-preprocess in svelte.config.js:

JavaScript
//svelte.config.js
import rehypeSlug from 'rehype-slug';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
...
preprocess: [
  ...
  mdsvex({
    extensions: ['.md', '.svelte', '.svx'],
    highlight: {
      highlighter
    },
    rehypePlugins: [
      [
        rehypeUrls, 
        processUrl
      ],
      rehypeSlug,
      rehypeAutolinkHeadings,
    ],
  }),
]

rehype-slug plugin can add id attributes to headings, rehype-autolink-headings plugin add links from headings back to themselves. They help me to implement hash anchor for headings.

I customized rehype-urls plugin to fill the empty alt attribute for img tag, the target and rel attributes for outside a tag, and specified all the images should be in webp format:

JavaScript
function processUrl(url, node) {
  if (node.tagName === 'a') {
    if (!node.properties.alt) {
      node.properties.alt = 'img';
    }
    if (!url.href.startsWith('/')) {
      node.properties.target = '_blank';
      node.properties.rel = 'noreferrer external';
    }
  }
  if (node.tagName === 'img') {
    return `${node.properties.src}!/format/webp`;
  }
}

I used an endpoint, resolving all my posts and serving a XML file, to implement RSS feed.

JavaScript
// rss.xml.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,
  };
}

SSG

SSG means Static Site Generation, it is ideal for pre-rendering every page on my website.

SvelteKit provides several adapters, and I chose the static adapter for this purpose:

JavaScript
// svelte.config.js
import adapter from '@sveltejs/adapter-static'

...
kit: {
  adapter: adapter(),
    prerender: {
      default: true,
    },
  ...
}

Sitemap

To make a better performance for SEO, it is important to has an auto generated sitemap.

After using the static-adapter above, i can only generate sitemap files after the whole site was built.

I added a sitemap script in package.json:

JSON
// package.json
  "scripts": {
    "sitemap": "node --experimental-json-modules ./gen-sitemap.js",
  }

And a gen-sitemap.js to read all build files and trimming files url to build sitemap:

JavaScript
import fs from 'fs';
import fg from 'fast-glob';
import { create } from 'xmlbuilder2';
import globalConfig from './global-config.js';

const getUrl = (url) => {
    const trimmed = url.slice(6).replace('index.html', '');
    return `${globalConfig.siteURL}/${trimmed}`;
};

async function createSitemap() {
    const sitemap = create({ version: '1.0' }).ele('urlset', {
        xmlns: 'http://www.sitemaps.org/schemas/sitemap/0.9'
    });

    const pages = await fg(['build/**/*.html']);

    pages.forEach((page) => {
      const url = sitemap.ele('url');
      url.ele('loc').txt(getUrl(page));
    });

    const xml = sitemap.end({ prettyPrint: true });

    fs.writeFileSync('build/sitemap.xml', xml);
}

createSitemap();

Thanks

That’s what I did with Svelte and SvelteKit for this new website. Hope it can give you some ideas.

Thank you for reading.