个人博客:槿苏的知识铺
一、前言
在技术飞速发展的今天,高效地编写、维护和呈现文档已成为开发者不可或缺的能力。无论是开源项目、团队协作还是个人知识沉淀,一套结构清晰、体验优雅的文档系统都能显著提升信息的传递效率。而vuepress-reco
作为vuepress
的现代化主题,它既继承了vuepress
的markdown
友好性和静态站点的高性能,又通过丰富的主题功能和开箱即用的配置,借助vuepress
的插件生态,为技术文档注入了更多可能,让开发者能够专注于内容创作,而非繁琐的配置。本文将基于vuepress-reco 2.x
版本,系统性地介绍如何从零搭建一套文档系统。
二、搭建流程
1、环境准备
node version >= 18 # 安装脚手架工具 npm install @vuepress-reco/theme-cli -g
2、使用脚手架创建项目
theme-cli init
执行之后会出现以下内容,依次输入
? Whether to create a new directory? Yes # 是否创建目录 输入 Y ? What's the name of new directory? blog-vuepress-reco-demo # 项目目录名称 ? What's the title of your project? blog-demo # 标题(如果准备创建2.x版本,此项无效,可不填写) ? What's the description of your project? blog-demo by vuepress-reco 2.x # 描述(如果准备创建2.x版本,此项无效,可不填写) ? What's the author's name? demo # 作者(如果准备创建2.x版本,此项无效,可不填写) ? What style do you want your home page to be?(The 2.x version is the beta version) # 选择2.x blog style for 1.x doc style for 1.x > 2.x
选择之后稍作等待项目就创建成功了,使用vscode
打开该项目,执行npm install
安装依赖,安装完成之后运行npm run dev
,打开控制台输出的访问链接即可看到页面效果。
三、文档配置
1、项目结构
blog-vuepress-reco-demo ├─ docs #该目录下存放编写的文档 │ └─ theme-reco │ ├─ api.md │ ├─ plugin.md │ ├─ theme.md │ └─ README.md ├─ blogs #该目录下存放编写的博客文章 │ ├─ category1 │ │ ├─ 2018 │ │ │ └─ 121501.md │ │ └─ 2019 │ │ └─ 092101.md │ ├─ category2 │ │ ├─ 2016 │ │ │ └─ 121501.md │ │ └─ 2017 │ │ └─ 092101.md │ └─ other │ └─ guide.md ├─ series # vuepress-reco 2.x新增,使用脚手架创建无此目录,可手动创建,与docs目录作用类似 ├─ .vuepress #存放项目配置文件与静态资源 │ ├─ config.ts #配置文件 │ └─ public #该目录下存放网页中所需的静态资源 │ ├─ bg.svg #首页背景大图 │ ├─ head.png #头像 │ └─ logo.png #网站logo ├─ package.json #依赖管理文件 └─ README.md #博客首页的内容
2、打包工具
该主题模版内置了vite
和webpack
,默认使用vite
,此处我们也选择vite
,所以可以执行以下命令将webpack
打包工具卸载
npm uninstall @vuepress/bundler-webpack # 同理,如果选择webpack,可执行以下命令卸载vite npm uninstall @vuepress/bundler-vite
3、主题配置
主题配置请根据vuepress-reco
官方文档进行配置,本文所述仅限参考,且不做过多赘述。
(1)首页
主题的默认首页是根目录下的README.md
文件生成,配置内容请自行参考官方文档。在某些场景下,文档的首页并不一定是文档根目录的README.md
文件,这时我们可以在Frontmatter
中设置home: true
来置顶首页,并通过theme.home
来指定首页路径。
# another-home-path.md --- title: 指定首页 home: true ---
// .vuepress/config.ts import { defineUserConfig } from "vuepress"; import { recoTheme } from "vuepress-theme-reco"; export default defineUserConfig({ theme: recoTheme({ home: "/another-home-path", }), });
(2)插件
更多插件请前往vuepress
插件市场探索
// .vuepress/config.ts import { defineUserConfig } from "vuepress"; import { recoTheme } from "vuepress-theme-reco"; import { mediumZoomPlugin } from "@vuepress/plugin-medium-zoom"; export default defineUserConfig({ plugins: [ // 图片放大插件 mediumZoomPlugin({ selector: "img", }), ], });
(3)其他配置
// .vuepress/config.ts import { defineUserConfig } from "vuepress"; import { recoTheme } from "vuepress-theme-reco"; export default defineUserConfig({ // 打包文件路径 dest: "./dist", // 运行端口号 port: 3011, // 语言 locales: { "/": { lang: "zh-CN", }, }, // html head头部配置 head: [ ["link", { rel: "icon", href: "/favicon.ico" }], [ "meta", { name: "viewport", content: "width=device-width,initial-scale=1,user-scalable=no", }, ], ["meta", { name: "keywords", content: "" }], // 搜索引擎关键字 ["meta", { name: "author", content: "" }], // 作者 ["meta", { name: "robots", content: "all" }], ], });
(4)自动化配置
vuepress-reco
为了节约用户的时间成本,特地添加了自动设置分类、自动设置系列的功能
-
自动设置分类
import { defineUserConfig } from "vuepress"; import { recoTheme } from "vuepress-theme-reco"; export default defineUserConfig({ theme: recoTheme({ // 自动设置分类 autoSetBlogCategories: true, // 自动将分类和标签添加至头部导航条 autoAddCategoryToNavbar: { location: 1, // 插入位置,默认 0 showIcon: true, // 展示图标,默认 false }, // 当 autoAddCategoryToNavbar 为 true 时,则全部取默认值 autoAddCategoryToNavbar: true, }), });
当开启自动分类时,需要注意的是,由于该功能自动为
blogs
文件夹下的博客设置分类,也就是将该文件所在文件夹的名称设置为该文件的frontmatter
的categories
的值。假如您的blogs
文件夹下并不是只有一级目录,而是多级目录,那么此功能将会把分类名称设置为多级目录的路径名称。例如:blogs └─ category1 └─ 2018 └─121501.md
那么,分类名称将设置为
category1/2018
。 -
自动设置系列
为了节约用户的时间成本,主题可以自动将
series
文件夹下的文档,按照文件夹嵌套关系生成系列的配置。import { defineUserConfig } from "vuepress"; import { recoTheme } from "vuepress-theme-reco"; export default defineUserConfig({ theme: recoTheme({ // 自动设置系列 autoSetSeries: true, }), });
(5)自定义增强配置
从自动设置系列得到的启发,自己定义函数,将docs
下的文档按照series
配置的规则生产相应的配置,这样就不需要自己手动配置docs
文件夹下的配置。
// util.ts import * as fs from "fs"; import * as path from "path"; /** * 获取目录下的子目录 * * @param dir 指定目录 * @returns 子目录名称数组 */ const nextDirectories = (dir: string): string[] => { const files = fs.readdirSync(dir, { withFileTypes: true }); const directories = files .filter((dirent) => dirent.isDirectory()) .map((dirent) => dirent.name); return directories; }; /** * 将多个字符串变量拼接 * * @param separator * @param args * @returns */ const stringJoin = (separator: string, ...args: string[]): string => { return args .map((arg) => (arg.startsWith(separator) ? arg.slice(1) : arg)) .filter((arg) => arg) .join(separator); }; export { stringJoin, nextDirectories };
/** * 将某个目录下的文件返回SeriesConfigArray的结构 * * @param rootPath 项目根目录 * @param dir 目录 * ├─ index.md * ├─ one * │ ├─a.md * │ ├─b.md * │ └─ three * │ └─ e.md * └─ two * ├─ c.md * └─ d.md * @returns ["index",{"text":"one","children":["one/a","one/b",{"text":"three","children":["one/three/e"]}]},{"text":"two","children":["two/c","two/d"]}] */ const createSeriesConfigArray = ( rootPath: string, dir: string, basePath: string = "" ): any[] => { try { const fullDir = path.join(rootPath, dir, basePath); const files = fs.readdirSync(fullDir, { withFileTypes: true }); const results: any[] = []; files.forEach((dirent) => { if (dirent.isFile() && dirent.name.endsWith(".md")) { // 处理.md文件 const name = stringJoin("/", basePath, path.parse(dirent.name).name); results.push(name); } else if (dirent.isDirectory()) { const childDir = stringJoin("/", basePath, dirent.name); // 递归处理子目录 const children = createSeriesConfigArray(rootPath, dir, childDir); if (children.length > 0) { results.push({ text: dirent.name, children: children, }); } } }); return results; } catch (err) { console.error("Error:", err); throw err; // 或者根据需要处理错误 } };
// series.ts import path from "path"; import { nextDirectories, stringJoin } from "./utils"; const rootPath = path.dirname(__dirname); // 获取docs下的子目录 const docsName = "docs"; const directories = nextDirectories(path.join(rootPath, docsName)); // 生成series const series: any = {}; directories.forEach((dirName) => { const path = "/" + stringJoin("/", docsName, dirName); series[path + "/"] = createSeriesConfigArray(rootPath, path); }); export default series;
.vuepress/config.ts import { defineUserConfig } from "vuepress"; import { recoTheme } from "vuepress-theme-reco"; import series from "./series"; export default defineUserConfig({ theme: recoTheme({ // docs下的文档 series: series, }), });
四、在线部署
1、搭建Github Pages
国内外知名代码托管平台,如:Github
、Gitlab
、都有提供Gitee(Pages服务已下线)
Pages
服务,也就是可以托管您的静态资源,以此来搭建一个静态网站,这里我们选用Github
进行搭建Pages
服务,仅限参考,一切以官方文档为准。
Github Pages
的站点类型有以下两种:
个人或组织站点(User or Organization sites)
:对于个人或组织站点,每个GitHub
用户或组织只能有一个站点,它通常使用[username].github.io
或[organizationname].github.io
的格式,这是GitHub Pages
的默认站点,通常用于个人网站、博客等。项目站点(Project sites)
:对于项目站点,每个GitHub
仓库可以有一个关联的GitHub Pages
站点,这意味着对于每个项目,您可以创建一个独立的GitHub Pages
站点,无需限制。
此处我们选择创建个人或组织站点
:
1、在Github
顶部菜单栏点击+
,然后New repository
新建仓库,输入项目的相关信息,然后Create repository
创建仓库,需要注意的是,如果您的用户或组织名称包含大写字母,您必须小写字母。如图:
2、设置Github Pages
,访问 [username].github.io
以查看新网站,如果配置了自己的域名,可以使用自己域名进行访问。GitHub Pages
将查找index.html
、index.md
或README.md
文件,作为站点的入口文件。 请注意,对站点的更改在推送到 GitHub
后,最长可能需要 10 分钟才会发布。
2、部署项目
在项目目录下,创建内容如下的deploy.sh
文件
!/usr/bin/env sh # 确保脚本抛出遇到的错误 set -e # 编译生成静态文件 npm run build # 进入编译生成的文件夹 cd ./dist # 如果是发布到自定义域名 # echo '自定义域名' > CNAME git init git add -A git commit -m 'deploy' # 需要修改为您自己的GitHub Pages仓库地址 git remote add origin git@github.com:[username]/[username].github.io.git git push -f origin master cd - rm -rf ./dist
在项目目录下打开命令行窗口,根据自己的电脑环境执行对应的命令。
Linux环境下 bash deploy.sh # Windows环境下 deploy.sh
执行完成,稍作等待后访问GitHub Pages
站点链接即可查看发布效果。