还在用Hexo搭建技术博客?快试下vitepress吧~

2,718 阅读6分钟

我正在参加「掘金·启航计划」

前言

Hexo 是什么?官网的描述是:A fast, simple & powerful blog framework。之前折腾过用Hexo搭建个人技术博客,带有丰富的主题,可以引入RSS、Github 评论系统等。

身为一名前端er,了解到 Evan You 推出了 vitepress -- 基于 Vite 和 Vue 的静态站点生成器,其前身是 vuepress,自带暗黑模式。目前 vitepress 仍处于 alpha 阶段,最新版本是1.0.0-alpha.47,不过可以提前尝鲜一下。

Hexo vs Vitepress

二者的比较如下:

HexoVitepress
页面基本内容(nav/sidebar/footer)++
自定义页面(首页等)++
友情链接++
多种主题++
暗黑模式-+
站内搜索++
RSS+-
评论系统+-

目录结构

docs
  FE
    test.md
  LeetCode
    001.md
  index.md
  .vitepress
    config.ts
.gitignore
package.json
node_modules
yarn.lock

package.json

首先是新建package.json文件,内容为:

{
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vitepress dev docs",
    "build": "vitepress build docs",
    "serve": "vitepress serve docs"
  },
  "devDependencies": {
    "@types/node": "^18.14.0",
    "vitepress": "^1.0.0-alpha.47"
  }
}

这里需要安装开发依赖 vitepress 和 @types/node 。

其中,@types/node 提供了 Typescript 类型声明。

vitepress 的最新版本是1.0.0-alpha.47,目前是 alpha 内部测试版。一般来说,一个npm包从诞生到最终发布会经历以下几个阶段,参考:Software_release_life_cycle。尽管 vitepress 还处于 alpha 阶段,但是借着 vite 和 vue3 的东风,我们可以看到 vitepress 的功能在不断地完善中,这里拿来尝鲜一下还是不错的。

  • Pre-alpha: 功能不完整的版本
  • Alpha: 内部版本
  • Beta: 公测版本
  • Release candidate: 候选版本
  • Release to manufacturing (RTM): 生产商发放
  • General availability (GA): 一般可用
  • Release(Stable): 正式版

docs

docs 目录是主目录,其中的 index.md是首页,.vitepress目录是配置文件所在的目录。我们还可以在此新建更多的 .md 文件或目录。

vitepress 支持 .ts, .cjs, .mjs, .cts, .mts 格式的配置文件。

这里借鉴了官网的配置文件,内容如下:

// config.ts

import { createRequire } from 'module'
import { defineConfig } from 'vitepress'

const require = createRequire(import.meta.url)
const pkg = require('vitepress/package.json')

export default defineConfig({
  lang: 'en-US',
  title: 'VitePress',
  description: 'Vite & Vue powered static site generator.',

  lastUpdated: true,
  cleanUrls: true,

  head: [['meta', { name: 'theme-color', content: '#3c8772' }]],

  markdown: {
    headers: {
      level: [0, 0]
    }
  },

  themeConfig: {
    nav: nav(),

    sidebar: {
      '/FE/': sidebarGuide(),
      '/LeetCode/': sidebarConfig()
    },

    editLink: {
      pattern: 'https://github.com/vuejs/vitepress/edit/main/docs/:path',
      text: 'Edit this page on GitHub'
    },

    socialLinks: [
      { icon: 'github', link: 'https://github.com/vuejs/vitepress' },
      { icon: 'slack', link: 'https://github.com/vuejs/vitepress' }
    ],

    footer: {
      message: 'Released under the MIT License.',
      copyright: 'Copyright © 2019-present Evan You'
    },

    algolia: {
      appId: '8J64VVRP8K',
      apiKey: 'a18e2f4cc5665f6602c5631fd868adfd',
      indexName: 'vitepress'
    },

    carbonAds: {
      code: 'CEBDT27Y',
      placement: 'vuejsorg'
    }
  }
})

function nav() {
  return [
    { text: 'Front-End', link: '/FE/test', activeMatch: '/FE/' },
    { text: 'LeetCode', link: '/LeetCode/001', activeMatch: '/LeetCode/' },
    {
      text: pkg.version,
      items: [
        {
          text: 'Changelog',
          link: 'https://github.com/vuejs/vitepress/blob/main/CHANGELOG.md'
        },
        {
          text: 'Contributing',
          link: 'https://github.com/vuejs/vitepress/blob/main/.github/contributing.md'
        }
      ]
    }
  ]
}

function sidebarGuide() {
  return [
    {
      text: 'Introduction',
      collapsed: false,
      items: [
        { text: 'What is VitePress?', link: '/guide/what-is-vitepress' },
        { text: 'Getting Started', link: '/guide/getting-started' },
        { text: 'Configuration', link: '/guide/configuration' },
        { text: 'Routing', link: '/guide/routing' },
        { text: 'Deploying', link: '/guide/deploying' },
        { text: 'Internationalization', link: '/guide/i18n' }
      ]
    },
    {
      text: 'Writing',
      collapsed: false,
      items: [
        { text: 'Markdown', link: '/guide/markdown' },
        { text: 'Asset Handling', link: '/guide/asset-handling' },
        { text: 'Frontmatter', link: '/guide/frontmatter' },
        { text: 'Using Vue in Markdown', link: '/guide/using-vue' },
        { text: 'API Reference', link: '/guide/api' }
      ]
    },
    {
      text: 'Theme',
      collapsed: false,
      items: [
        { text: 'Introduction', link: '/guide/theme-introduction' },
        { text: 'Nav', link: '/guide/theme-nav' },
        { text: 'Sidebar', link: '/guide/theme-sidebar' },
        { text: 'Prev Next Link', link: '/guide/theme-prev-next-link' },
        { text: 'Edit Link', link: '/guide/theme-edit-link' },
        { text: 'Last Updated', link: '/guide/theme-last-updated' },
        { text: 'Layout', link: '/guide/theme-layout' },
        { text: 'Home Page', link: '/guide/theme-home-page' },
        { text: 'Team Page', link: '/guide/theme-team-page' },
        { text: 'Badge', link: '/guide/theme-badge' },
        { text: 'Footer', link: '/guide/theme-footer' },
        { text: 'Search', link: '/guide/theme-search' },
        { text: 'Carbon Ads', link: '/guide/theme-carbon-ads' }
      ]
    },
    {
      text: 'Migrations',
      collapsed: false,
      items: [
        {
          text: 'Migration from VuePress',
          link: '/guide/migration-from-vuepress'
        },
        {
          text: 'Migration from VitePress 0.x',
          link: '/guide/migration-from-vitepress-0'
        }
      ]
    }
  ]
}

function sidebarConfig() {
  return [
    {
      text: 'Config',
      items: [
        { text: 'Introduction', link: '/config/introduction' },
        { text: 'App Configs', link: '/config/app-configs' },
        { text: 'Theme Configs', link: '/config/theme-configs' },
        { text: 'Frontmatter Configs', link: '/config/frontmatter-configs' }
      ]
    }
  ]
}

运行

首先,我们需要安装项目依赖,执行yarn

接着,设置配置文件 .vitepress/config.ts

然后,编写 .md 文件;

最后,执行 yarn dev

由于 vitepress 是基于 vite 的,所以本地运行时,我们很快就将静态的网站跑起来了~

config.ts 详解

我们需要在该文件中设置默认导出:

import { defineConfig } from 'vitepress'
export default defineConfig({
  // ...
})

页面配置包括 lang, title, description等:

lang: 'en-US',
title: 'VitePress',
description: 'Vite & Vue powered static site generator.',

lastUpdated

  • Type: boolean
  • Default: false

使用 git commit 信息来获取时间戳。需要注意的是,我们需要将代码提交到 git 仓库后,才会显示时间戳!

设置为 true 后,我们可以看到页面底部显示更新时间:Last updated: 2/22/2023, 8:41:03 PM

cleanUrls

  • Type: boolean
  • Default: false

是否允许页面的 URL 不带.html 后缀。

head

数组,可以设置 meta 标签等信息。

markdown

设置 markdown 渲染选项。VitePress 使用 Markdown-it 作为 parser, 使用 Shiki 用于高亮代码。可以在此设置 markdown 渲染选项,比如主题 theme 等。

export default {
  markdown: {
    theme: 'material-theme-palenight',
    lineNumbers: true
  }
}

themeConfig

在此自定义主题的样式,比如设置页面的 logo、上方的导航栏 nav、左侧导航栏 sidebar 等。

themeConfig 详解

themeConfig中包含了很多支持自定义的页面内容,详见官方文档。其中,比较重要的配置项是 nav, sidebar, socialLinks, footer 等。

nav

NavItem 数组,用于自定义上方的导航栏。NavItem 包含的字段有:

type NavItem = NavItemWithLink | NavItemWithChildren

type NavItemWithLink = {
  text: string
  link: string
  activeMatch?: string
}

interface NavItemWithChildren {
  text?: string
  items: NavItemWithLink[]
  activeMatch?: string
}

sidebar

SidebarItem 数组,可以自定义左侧导航栏的样式,字段有 text, link, items, collapsed(默认是否折叠):

export type Sidebar = SidebarItem[] | SidebarMulti
export interface SidebarMulti {
  [path: string]: SidebarItem[]
}
export type SidebarItem = {
  /**
   * The text label of the item.
   */
  text?: string
  /**
   * The link of the item.
   */
  link?: string
  /**
   * The children of the item.
   */
  items?: SidebarItem[]
  /**
   * If not specified, group is not collapsible.
   *
   * If `true`, group is collapsible and collapsed by default
   *
   * If `false`, group is collapsible but expanded by default
   */
  collapsed?: boolean
}

socialLinks

定义外部链接,位于nav导航栏的右侧。这里我们配置了 github 和 slack 两个外部链接:

socialLinks: [
    { icon: 'github', link: 'https://github.com/vuejs/vitepress' },
    { icon: 'slack', link: 'https://github.com/vuejs/vitepress' }
],

查看文档,支持的配置内容是:

export type SocialLinkIcon =
    | 'discord'
    | 'facebook'
    | 'github'
    | 'instagram'
    | 'linkedin'
    | 'mastodon'
    | 'slack'
    | 'twitter'
    | 'youtube'
    | { svg: string }

footer

配置页面底部的说明信息、版权信息等。比如:

footer: {
    message: 'Released under the MIT License.',
    copyright: 'Copyright © 2019-present Evan You'
},

algolia

  • Type: AlgoliaSearch

我们只需配置 appId, apiKey, indexName 选项,就可以实现站内搜索功能。

Layout

借助 Layout ,我们可以基于 vitepress 内置的样式快速地设置 .md 文件的基本样式。Layout 有以下几种选择:home、doc、page 和 false。不指定时,默认是 doc 。

Home Layout

可以通过设置 hero 或 features 来自定义页面的内容。比如首页的内容如下:

index.md

---
layout: home

title: VitePress
titleTemplate: Vite & Vue Powered Static Site Generator

hero:
  name: VitePress
  text: Vite & Vue Powered Static Site Generator
  tagline: Simple, powerful, and performant. Meet the modern SSG framework you've always wanted.
  actions:
    - theme: brand
      text: Get Started
      link: /guide/getting-started
    - theme: alt
      text: View on GitHub
      link: https://github.com/vuejs/vitepress

features:
  - title: "Vite: The DX that can't be beat"
    details: Feel the speed of Vite. Instant server start and lightning fast HMR that stays fast regardless of the app size.
  - title: Designed to be simplicity first
    details: With Markdown-centered content, it's built to help you focus on writing and deployed with minimum configuration.
  - title: Power of Vue meets Markdown
    details: Enhance your content with all the features of Vue in Markdown, while being able to customize your site with Vue.
  - title: Fully static yet still dynamic
    details: Go wild with true SSG + SPA architecture. Static on page load, but engage users with 100% interactivity from there.
---

预览效果:

首页

Doc Layout

会将全部内容用 .vp-doc 类包裹起来。几乎所有通用的HTML元素如 p 或 h2 会有特殊的样式。

而且,以下功能只会在 Doc 布局下出现:

  • Edit Link
  • Prev Next Link
  • Outline
  • Carbon Ads

Page Layout

该布局视为空白页面,页面内容的样式不会受到 vitepress 主题的影响,这有利于自定义页面。

No Layout

设置了layout: false。此布局可以用于自定义加载页面(不包含任何sidebar, navbar 或 footer)。

后记

社区还有用 vitepress 搭建 vue组件库的尝试,其实也跟搭建个人博客的流程差不多。

需要注意的是,vitepress 仍然是alpha阶段,API不稳定、改动较大。不过,不影响 前端er 尝鲜一下。