VitePress 建站记录
平时浏览网页时,发现很多网站都是基于 VitePress 搭建的,正好目前 VitePress 也即将发布 1.0 正式版,基本不会再有大的变动,于是决定用它搭个博客玩玩,相比于其他建站工具,主要看重以下特点:
- 格式友好: 文档使用 Markdown 语言编写,易迁移和版本控制
- 轻量部署: 生成静态网站,不依赖数据库等,易于托管和维护
下面记录着建站过程中的一些配置和优化过程
界面优化
VitePress 配置
VitePress 内的各个元素默认英文显示,首先先进行一些本地化配置:
// .vitepress/config.mts
export default defineConfig({
themeConfig: {
outline: {
label: '章节速览'
},
lastUpdated: {
text: '最后编辑'
},
docFooter: {
prev: '上一篇',
next: '下一篇'
},
darkModeSwitchLabel: '外观',
sidebarMenuLabel: '菜单',
returnToTopLabel: '返回顶部'
}
})
// .vitepress/config.mts
export default defineConfig({
themeConfig: {
// 搜索
search: {
provider: 'local',
options: {
locales: {
root: {
translations: {
button: {
buttonText: '在莫记中搜索'
},
modal: {
displayDetails: '显示详细列表',
resetButtonTitle: '清除查询条件',
noResultsText: '未找到相关结果',
footer: {
navigateText: '切换',
selectText: '选择',
closeText: '关闭'
}
}
}
}
}
}
}
}
})
更换默认字体
VitePress 默认提供的字体看着不太习惯,于是想换成平时 IDE 内设置的字体:中文 HarmonyOS Sans SC
,代码 JetBrains Mono
文档内也提供了更换默认主题下字体的方式:
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme-without-fonts'
import './custom.css'
export default DefaultTheme
/* .vitepress/theme/custom.css */
@font-face {
font-family: 'HarmonyOS Sans SC';
src: url('font/HarmonyOS_Sans_SC_Regular.woff2') format('woff2');
font-weight: 400;
}
@font-face {
font-family: 'HarmonyOS Sans SC';
src: url('font/HarmonyOS_Sans_SC_Medium.woff2') format('woff2');
font-weight: 500;
}
@font-face {
font-family: 'HarmonyOS Sans SC';
src: url('font/HarmonyOS_Sans_SC_Bold.woff2') format('woff2');
font-weight: 600;
}
@font-face {
font-family: 'JetBrains Mono';
src: url('font/JetBrainsMono_Regular.woff2') format('woff2');
font-weight: 400;
}
:root {
--vp-font-family-base: 'HarmonyOS Sans SC';
--vp-font-family-mono: 'JetBrains Mono', 'HarmonyOS Sans SC';
}
为什么字体的 CSS 配置会是上面这样呢?
- 如果仅仅更改
--vp-font-family-base
,自己的设备由于安装了这些字体,确实能正确替换,但是别人打开网站就会回滚到默认字体 - 使用
@font-face
引用字体文件,构建时将这些字体打包到资源路径下,才能在任何地方都显示这些字体 - 查看 CSS 后,发现引用到的
font-weight
只有 400、500、600、700 这几个值,于是提供了 HarmonyOS Sans SC 的三种字重
关于字体的题外话
HarmonyOS Sans SC 提供的字符数量太多,导致字体文件过大,仅一个字重的 .ttf
字体文件就占到了 8 MB,访问网站需要加载 24 MB 的字体文件,显然不太合适,在使用前需要先对字体文件进行压缩
下面是简略的压缩流程:
- 提取出基本拉丁字符、中文标点符号、通用规范汉字表常用字集 3500 字的 Unicode 编码(仅 96+29+3500=3625 个字符,却能完全满足需求)
- 安装 Python 的
fonttools
库,执行pyftsubset xxx.ttf --unicodes-file=unicode.txt
命令,用提供的 Unicode 列表压缩字体并输出到相同路径 - 将
.ttf
格式的字体转换为.woff2
格式,其实现了更高的压缩率和更好的性能,是目前最适合应用在网站上的字体格式
如果追求极致的话,还可以对字体进行分片操作,将一个字体文件切分成多个包含不同字符子集的文件,访问时按需加载
附:通用规范汉字表 - 常用字集 3500 字一览,给你一点小小的方块字震撼
更换代码高亮主题
VitePress 使用 Shikiji 实现语法高亮效果,可通过下面的配置更换其内置的其他主题:
// .vitepress/config.mts
export default defineConfig({
markdown: {
theme: {
light: 'material-theme',
dark: 'material-theme'
}
}
})
为了和 material-theme
的主题搭配,同步更改了代码块相关的其他样式,VitePress 定义了很多 CSS 变量,通过简单的修改这些内置的变量即可方便的进行自定义:
/* .vitepress/theme/custom.css */
:root {
--vp-code-block-bg: #212121; /* 代码块背景颜色 */
--vp-code-block-color: #eeffff; /* 代码块文字颜色 */
--vp-code-lang-color: #848484; /* 代码块语言颜色 */
--vp-code-copy-copied-text-content: '已复制'; /* 代码复制成功文本 */
--vp-code-line-number-color: #424242; /* 代码行号颜色 */
--vp-code-block-divider-color: #303030; /* 代码行号分割颜色 */
--vp-code-tab-text-color: #848484; /* 代码组标签文本默认颜色 */
--vp-code-tab-hover-text-color: #eeffff; /* 代码组标签文本悬停颜色 */
--vp-code-tab-active-text-color: #eeffff; /* 代码组标签文本激活颜色 */
--vp-code-tab-active-bar-color: #80cbc4; /* 代码组标签下标激活颜色 */
--vp-code-tab-divider: #303030; /* 代码组标签分割颜色 */
}
易用性优化
自动生成侧边栏
VitePress 中的侧边栏配置是直接写在 config.mts
内的,确保了内容的正确性,但不太方便,于是实现了自动生成侧边栏的功能,这样提交了新的 Markdown 文件后,侧边栏就会依照目录结构自动更新:
// .vitepress/utils/genSidebar.ts
/**
* 根据文件路径生成页面链接
*/
function generateLink(filePath: string, basePath: string): string {
return '/' + path.relative(basePath, filePath)
.replace(/\\/g, '/')
.replace(/\.md$/, '')
}
/**
* 递归遍历目录生成侧边栏项
*/
function generateSidebarItems(directory: string, basePath: string): SidebarInfo {
// 侧边栏项
const sidebarItems: DefaultTheme.SidebarItem[] = []
// 该目录是否可点击
let folderLink = fs.existsSync(path.join(directory, 'index.md'))
? generateLink(directory, basePath) + '/'
: null
const filesAndDirs = fs.readdirSync(directory, { withFileTypes: true })
filesAndDirs.forEach(dirent => {
const fullPath = path.join(directory, dirent.name)
if (dirent.isDirectory()) {
// 如果是目录则递归调用
const directoryItems = generateSidebarItems(fullPath, basePath)
if (directoryItems.sidebarItems.length > 0) {
sidebarItems.push({
text: dirent.name,
...(directoryItems.folderLink ? { link: directoryItems.folderLink } : {}),
items: directoryItems.sidebarItems
})
}
} else if (path.extname(dirent.name) === '.md' && dirent.name !== 'index.md') {
// 如果是 Markdown 文件则添加到数组
sidebarItems.push({
text: path.basename(dirent.name, '.md'),
link: generateLink(fullPath, basePath)
})
}
})
return { sidebarItems, folderLink }
}
/**
* 为指定目录生成侧边栏
*/
export function generateSidebar(directory: string): DefaultTheme.SidebarItem[] {
let basePath = path.dirname(directory)
return generateSidebarItems(directory, basePath).sidebarItems
}
实现图片点击预览
由于 VitePress 中的图片无法直接放大,导致一些细节较多的图片不方便查看,这里参照 Issues 内的方式实现了图片点击预览的效果:
安装 medium-zoom,一个轻量级的 JavaScript 库,专门用于实现网页上图片的缩放交互,提供简单而优雅的图片预览体验
pnpm add -D vue
pnpm add -D medium-zoom
扩展默认主题,在页面加载或路由变化的时候初始化 mediumZoom
,使其应用到 .main img
,也就是 Markdown 文档中的所有图片,然后设置背景颜色和间距
// .vitepress/theme/index.ts
export default {
extends: DefaultTheme,
setup() {
const route = useRoute()
const initZoom = () => mediumZoom('.main img', {
background: 'var(--vp-c-gray-soft)',
margin: 24
})
onMounted(() => initZoom())
watch(
() => route.path,
() => nextTick(() => initZoom())
)
}
}
最后在 CSS 文件中添加两处 z-index
属性,目的是在图片放大时,背景遮罩和图像自身能够完全覆盖导航栏等页面元素
/* .vitepress/theme/custom.css */
.medium-zoom-image {
z-index: 30;
}
.medium-zoom-overlay {
z-index: 30;
}
效果如下:
网站部署
既然做博客,就要专注内容,所以选择了 GitHub Pages,直接利用仓库托管该网站,无需手动管理
GitHub Pages
VitePress 文档中也贴心的附上了 GitHub Actions 工作流的配置,翻译了部分注释,修改 Node.js 版本和更换包管理器为 pnpm 后,最终配置如下:
# .github/workflows/deploy.yml
name: Deploy VitePress site to Pages
# 推送到 main 分支或手动触发时执行
on:
push:
branches: [ main ]
workflow_dispatch:
# 设置 GITHUB_TOKEN 的权限以允许部署到 GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# 只允许一个并发部署, 并跳过多余的部署任务
concurrency:
group: pages
cancel-in-progress: false
jobs:
# 构建任务
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 8.x
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 20
cache: pnpm
- name: Setup Pages
uses: actions/configure-pages@v3
- name: Install dependencies
run: pnpm install
- name: Build with VitePress
run: |
pnpm docs:build
touch .vitepress/dist/.nojekyll
- name: Upload artifact
uses: actions/upload-pages-artifact@v2
with:
path: .vitepress/dist
# 部署任务
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: build
runs-on: ubuntu-latest
name: Deploy
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2
与之相应的,将仓库 Pages 设置中的部署构建源修改为 GitHub Actions
自定义域名
现在已经可以通过 username.github.io
访问该网站了,但既然 GitHub Pages 免费提供自定义域名的功能,也顺便用上,在下方填入自己的域名:
在域名注册商处修改对应域名的 DNS 解析,添加 CNAME
记录,值为原 GitHub Pages 域名 username.github.io
:
配置完成后,等待几分钟再刷新,会发现填入自定义域名的下方提示 DNS check successful
,现在已经可以通过自定义域名访问,且 GitHub 提供了 SSL 证书