搭建和mybatis-plus官网一样主题的网站(cos+宝塔+vercel)
# 一、前言
网站主题最初是在开发数据api的时候看到的,当时在学习Mybatis-Plus,最初也不知道这是个主题,就觉得Mybatis-Plus官网UI真的不错,直到后面发现另外一个类似的网站,我意识到这是个模板,于是有了自己也搭建一个的想法,不过拖延症相当严重,最初是在宝塔上修改的,效率极低,一度放弃部署,直到最近换成本地开发才好了很多,也就是最近网站搭建初步完成,接下来就是往里面增加文章等等,这里带大家从零开始搭建Vdoing主题网站,算是对过去一个月的知识总结。下图就是mybatis-plus官网,是我们年轻人喜欢的风格(这里随便吐槽一下xx园吧,风格还是20年前的,不知道他们的UI设计师是不是还是兼职的)。
# 二、准备工作
# 1.github注册
what?你居然没有github账号,建议你放弃网站部署(开玩笑)。作为全球最大的代码托管平台,基本上每个稍微接触过开发的应该都用过,如果你还没注册的话,赶紧注册一个,没听过github太丢人了,注册也很简单(有手就行)。
点击进入github官网 (opens new window),这里需要注意一点,如果是国内访问的话,移动是可以的,其他运营商貌似有屏蔽,如果访问不了,自行百度解决。
# 2.申请属于自己的域名
这里推荐去国外的Godaddy (opens new window)平台注册一个域名
关于如何选一个比较好的域名可以看看我的这篇我的域名注册踩坑指南 (opens new window)
# 3.申请注册vercel平台
vercel平台是一个可以免费托管个人网站的自动化部署平台,只要你的代码托管平台(类似github,gitlab,gitee等)commit发生变化,它就会自动更新和重新部署,解放生产力。
点击vercel官网 (opens new window)sign up注册一个账号,当然也可以选择github直接登录,会更方便一点,可以直接导入自己的仓库,包括私有仓库。
# 4.下载node和yarn
# node
进入node的官网 (opens new window),选择LTS即长期稳定版本下载。会得到一个msi后缀的文件,即开箱即用的版本,不用自己手动配置环境变量啥的,会配置好的,注意安装位置改一下就行,放在除c盘外的盘即可,其他就一步步点击确认即可,最后cmd中输入node有welcome提示即为成功,不成功按照相应提示浏览器搜索即可。
# yarn
这个是个包管理工具,和npm差不多,听说更快一点,这里安装是由于主题更新采用的是这个命令,所以这里安装一下。
yarn官网 (opens new window),如果不想配置环境变量的话,还是可以采用msi后缀安装。
# 5.图床配置
# CDN + COS配置图床
有了图床,你就可以随时随地采用URL引用你的图片,非常方便,可以参考我写的这篇搭建博客CDN+COS搭建图床超详细步骤 (opens new window),有个缺点就是需要备案才能使用自定义域名和CDN服务,不过如果想要白嫖的话,搜索国外的图床也是可以的,国内也有一些提供图床配置的,使用它们的域名上传图片也可以。这里采用的是pic-go图床管理工具,强烈安利,真的太方便了。
# 路径引用
如果你没有自己的图床也没关系,主题博客还提供本地引用的功能,不过这个更改图片的时候比较麻烦,虽然有时候它可能更快一点点。
# 6.开发IDE下载
我是用习惯了idea,所以这里推荐idea进行开发,安装node.js插件,然后就可以本地开发了,没有的话安装一下即可。
安装node.js插件
至此,准备工作已经完成,接下来就进入今天的主题。
# 三、本地开发
# 1.github fork项目
Vdoing主题项目地址 (opens new window),这里建议fork整个仓库,然后自己做一些修改即可,作者大大真的太强了。
# 2.基础网站配置
# 主页配置
打开以下目录的index.md,你可能会问咋是md格式的,不应该是html格式的吗?别着急,这个是开发中的,打包之后是index.html
以下字段对应相关的图,可以自行体会一下,对应导航栏下面的几个字段,其中涉及使用URL引入的,可以替换成路径引用,具体字段帮助请自行查看fork过来的仓库介绍,也可以到Vdoing官方文档 (opens new window)查阅.
home: true
heroImage: https://img.de7v.com/img/new_logo_2.png
# heroImage:/img/web.png
# 这个路径在public下的img目录中
heroText: IT七剑客
tagline: 🚀互联网中的持剑者
actionText: 立刻进入 →
actionLink: /wresource/
bannerBg: none
2
3
4
5
6
7
8
9
通过路径引用的图片目录,把你想要的图片全放进去然后就可以引用了。
# config.ts配置
进入config.ts界面
首先我们先找到head位置进行配置,打开网站的小图标,同样也可以使用URL或者本地引用
找到location开头的这段代码,网站的标题和描述写一下,这个会在百度搜索的快照中展示。
locales: {
'/': {//代表baseurl,即网站所有的引用的根路径,默认是没有的,如果文件夹有需要可以修改
lang: 'zh-CN',
title: "IT七剑客",
description: '互联网中的持剑者,七剑客带你斩断一切bug',
}
},
2
3
4
5
6
7
下面这些代码主要是配置导航栏和其他全局功能,其中logo是最左侧显示的图片,repro是自己的github名,用于直接跳转github主页,其他的取默认即可。
sidebarDepth: 2, // 侧边栏显示深度,默认1,最大2(显示到h3标题)
logo: 'https://img.de7v.com/img/new_logo_2.png', // 导航栏logo
repo: 'xyh-fu', // 导航栏右侧生成Github链接
searchMaxSuggestions: 10, // 搜索结果显示最大数
lastUpdated: '上次更新', // 开启更新时间,并配置前缀文字 string | boolean (取值为git提交时间)
docsDir: 'docs', // 编辑的文件夹
// docsBranch: 'master', // 编辑的文件所在分支,默认master。 注意:如果你的分支是main则修改为main
editLinks: true, // 启用编辑
editLinkText: '编辑',
2
3
4
5
6
7
8
9
博主信息页面,在侧边栏,可以配置一些基础信息
blogger:{ // 博主信息,显示在首页侧边栏
avatar: 'https://img.de7v.com/img/new_logo_2.png',
name: 'IT七剑客',
slogan: '欢迎投稿加入IT七剑客'
}
2
3
4
5
最后再说明一下页脚的配置,这里用于配置全局页脚
footer:{ // 页脚信息
createYear: 2022, // 博客创建年份
//博客版权信息,支持a标签
copyrightInfo:'IT七剑客 | MIT License<li><img src="https://img.de7v.com/img/badge.png" style="zoom:30%;"><a href = "https://beian.miit.gov.cn" target="_blank" >闽ICP备2021006579号-4</a></li><li><img src="https://img.de7v.com/img/badge.png" style="zoom:30%;"> <a target="_blank" rel="noopener" href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=35012102500470">闽公网安备 35012102500470号</a></li>'
},
2
3
4
5
至此基本配置已经大功告成,接下来我们谈谈文章是怎么配置和主页路由的规则。
# 文章誊写和路由设置
首先配置目录页,这个是文章的导航界面,可以分别查看各个目录下的文章列表
---
pageComponent:
name: Catalogue
data:
path: 01.wresource 安卓
imgUrl: https://img.de7v.com/img/wresource.png
description: Android,Kotlin,Jetpack等技术
title: wresource 安卓
date: 2020-03-11 21:50:53
permalink: /wresource
sidebar: false
article: false
comment: false
editLink: false
author:
name: wresource
link: https://github.com/xyh-fu
---
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
在这里设置目录的路由以及一些基础的配置,放图自行体会,这里需要注意一点,path要和docs下面的相应文章目录路径名一致,这里仅仅只是配置一下界面,真正的文章是放在docs下面的目录中。
关于文章誊写和命名规则具体查看作者的这篇文章关于目录和文章名称约定 (opens new window)(这里偷懒一波)。
接下来再次回到之前得index.md中,可以配置主界面相关信息了
features: # 可选的
- title: wresource 安卓
details: Android,Kotlin,Jetpack等技术
link: /wresource/ # 可选
imgUrl: https://img.de7v.com/img/wresource.png # 可选
2
3
4
5
配置效果如下图所示
回到config.ts界面,我们来搭建一下导航栏,在themeConfig中的nav字段
nav: [
{
text: '首页',
link: '/'
},
{
text: 'wresource',
link: '/wresource/', //目录页链接,此处link是vdoing主题新增的配置项,有二级导航时,可以点击一级导航跳到目录页
},
{
text: '郭霖',
link: '/guolin/',
},
{
text: '孤寒者',
link: '/guhanzhe/',
},
{
text: 'IT邦德',
link: '/jeames007/',
},
{
text: '沉默王二',
link: '/wang_er/'
},
],
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
搭建好是这个效果,当然更多效果请参考fork过来中仓库的注释,个人比较喜欢简洁一点的导航栏
# 3.插件配置
这部分就属于网站的内部配置了,与外观没有多大关系,如果觉得可以了,就不用配置了
# 站点信息页面
主要用于统计网站相关信息的功能同时也增加文章字数统计以及阅读量统计等等,这个配置参考kbt大大的博客,这里提醒一下readfile文件在博客中失效,建议去kbt大大的github项目主页获取最新的代码。站点信息搭建 (opens new window)
这里给出当时踩坑的那个readfile文件代码,如果有不同,以作者的为主。
import fs from 'fs'; // 文件模块
import path from 'path'; // 路径模块
import matter from 'gray-matter'; // FrontMatter解析器 https://github.com/jonschlinkert/gray-matter
import chalk from 'chalk' // 命令行打印美化
const log = console.log
const docsRoot = path.join(__dirname, '..', '..', '..', 'docs'); // docs文件路径
/**
* 获取本站的文章数据
* 获取所有的 md 文档,可以排除指定目录下的文档
*/
function readFileList(excludeFiles: Array<string> = [''], dir: string = docsRoot, filesList: Array<Object> = []) {
const files = fs.readdirSync(dir);
files.forEach((item, index) => {
let filePath = path.join(dir, item);
const stat = fs.statSync(filePath);
if (!(excludeFiles instanceof Array)) {
log(chalk.yellow(`error: 传入的参数不是一个数组。`))
}
excludeFiles.forEach((excludeFile) => {
if (stat.isDirectory() && item !== '.vuepress' && item !== '@pages' && item !== excludeFile) {
readFileList(excludeFiles, path.join(dir, item), filesList); //递归读取文件
} else {
if (path.basename(dir) !== 'docs') { // 过滤 docs目录级下的文件
const fileNameArr = path.basename(filePath).split('.')
let name = null, type = null;
if (fileNameArr.length === 2) { // 没有序号的文件
name = fileNameArr[0]
type = fileNameArr[1]
} else if (fileNameArr.length === 3) { // 有序号的文件
name = fileNameArr[1]
type = fileNameArr[2]
} else { // 超过两个‘.’的
log(chalk.yellow(`warning: 该文件 "${filePath}" 没有按照约定命名,将忽略生成相应数据。`))
return
}
if (type === 'md') { // 过滤非 md 文件
filesList.push({
name,
filePath
});
}
}
}
});
});
return filesList;
}
/**
* 获取本站的文章总字数
* 可以排除某个目录下的 md 文档字数
*/
function readTotalFileWords(excludeFiles = ['']) {
const filesList = readFileList(excludeFiles);
let wordCount = 0;
filesList.forEach((item: any) => {
const content = getContent(item.filePath);
let len = counter(content);
wordCount += len[0] + len[1];
});
if (wordCount < 1000) {
return wordCount;
}
return Math.round(wordCount / 100) / 10 + 'k';
}
/**
* 获取每一个文章的字数
* 可以排除某个目录下的 md 文档字数
*/
function readEachFileWords(excludeFiles: Array<string> = [''], cn: number, en: number) {
const filesListWords = [];
const filesList = readFileList(excludeFiles);
filesList.forEach((item: any) => {
const content = getContent(item.filePath);
let len = counter(content);
// 计算预计的阅读时间
let readingTime = readTime(len, cn, en);
let wordsCount: any = 0;
wordsCount = len[0] + len[1];
if (wordsCount >= 1000) {
wordsCount = Math.round(wordsCount / 100) / 10 + 'k';
}
// fileMatterObj => {content:'剔除frontmatter后的文件内容字符串', data:{<frontmatter对象>}, ...}
const fileMatterObj = matter(content, {});
const matterData = fileMatterObj.data;
filesListWords.push({ ...item, wordsCount, readingTime, ...matterData });
});
return filesListWords;
}
/**
* 计算预计的阅读时间
*/
function readTime(len: Array<number>, cn: number = 300, en: number = 160) {
let readingTime = len[0] / cn + len[1] / en;
if (readingTime > 60 && readingTime < 60 * 24) { // 大于一个小时,小于一天
let hour = Math.trunc(readingTime / 60);
let minute = Math.trunc(readingTime - hour * 60);
if (minute === 0) {
return hour + 'h';
}
return hour + 'h' + minute + 'm';
} else if (readingTime > 60 * 24) { // 大于一天
let day = Math.trunc(readingTime / (60 * 24));
let hour = Math.trunc((readingTime - day * 24 * 60) / 60);
if (hour === 0) {
return day + 'd';
}
return day + 'd' + hour + 'h';
}
return readingTime < 1 ? '1' : Math.trunc(readingTime * 10) / 10 + 'm'; // 取一位小数
}
/**
* 读取文件内容
*/
function getContent(filePath: string) {
return fs.readFileSync(filePath, 'utf8');
}
/**
* 获取文件内容的字数
* cn:中文
* en:一整句英文(没有空格隔开的英文为 1 个)
*/
function counter(content: string) {
const cn = (content.match(/[\u4E00-\u9FA5]/g) || []).length;
const en = (content.replace(/[\u4E00-\u9FA5]/g, '').match(/[a-zA-Z0-9_\u0392-\u03c9\u0400-\u04FF]+|[\u4E00-\u9FFF\u3400-\u4dbf\uf900-\ufaff\u3040-\u309f\uac00-\ud7af\u0400-\u04FF]+|[\u00E4\u00C4\u00E5\u00C5\u00F6\u00D6]+|\w+/g) || []).length;
return [cn, en];
}
export {
readFileList,
readTotalFileWords,
readEachFileWords,
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
下面给出效果图
这里有一点比较坑,就是当时这个模块默认挂载到热门标签下面,然而我没有设置标签。最后给挂在了页面底部。请务必仔细阅读作者的博客。
# 广告配置
首先在config.ts的head中引入下列代码,这里是谷歌广告配置。
[
'script',
{
'data-ad-client': '自己的广告id',
async: 'async',
src: 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js',
},
], // 网站关联Google AdSense 与 html格式广告支持(你可以去掉)
2
3
4
5
6
7
8
然后在themeConfig中添加下列字段
在下列目录中完成html广告的配置
我的配置,在全局右下角添加广告。
windowRB:
` <script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle"
style="display:inline-block;
width:200px;
height:200px"
data-ad-client="ca-pub-广告id"
data-ad-slot="用户名"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>`,
2
3
4
5
6
7
8
9
10
11
# 百度统计
这部分比较简单,在head中加入以下字段即可(谁说比较简单的,这个我鼓捣了一周,插件用不了,不知道是啥原因,最后采用这个比较简单的方法配置),至于百度统计码,百度一下百度统计即可。它最重要的作用就是分析网站的来访数量以及搜索量等等,站长必备工具之一。
[
'script',
{
src: 'https://hm.baidu.com/hm.js?百度统计码',
},
],
2
3
4
5
6
# 评论配置
这个是最近配置成功的,踩了好多坑,也是参考kbt大大的博客,我用的也是twikoo评论,之前使用gittalk的,后面发现国内根本不能访问,所以换成了目前这个评论系统。kbt大大写的搭建评论的博客 (opens new window),说明几个坑。
部署时注意是部署云函数,不是该评论系统项目,被这个坑了好久好久。
点击自动部署会自动进入dev分支下的vercel配置,不是整个仓库
关于mongo db,感觉应该是香港节点会更快一点,不知道为啥会推荐us节点
关于环境id
这里应该是最近vercel的默认域名被禁的缘故,vercel.app之类的域名国内均不能访问,所以在部署环境id时需要添加自己的域名进行部署,类似xx.de7v.com。
关于管理员的评论管理
最开始是使用代理才可以进行配置的,之前都失败了,这一步如果没有默认的密码账号输入的话,建议开代理。
# 四、站点部署
# 1.COS+CDN部署
温馨提示,这部分部署需要国内备案域名,如果没有备案域名,请使用其他两种方式部署。
首先先运行vuepress项目,进入项目路径,终端输入以下命令进行构建项目
npm run build
生成静态文件成功之后可以在dist文件夹找到打包好的静态文件
# 先创建一个存储桶
# 上传文件
直接将文件复制一下拖拽到cos的以下界面,注意是dist里面的文件,不要传dist整个文件夹,反正最后是存储桶中有如下的内容,即dist里面的文件。
# 开启静态网站设置
此时还访问不了,如果想直接访问的话,权限管理改成公共读就可以访问了,使用访问节点的url即可,不过我们要介绍使用cdn进行访问,更快。
# cdn配置访问
利用cdn进行私有读写,cdn提供给外部访问,配置相关可以查看这篇博客里面的配置,几乎是一样的,不多赘述。CDN+COS搭建图床超详细步骤 (opens new window),最后记得开启https访问。
至此我们已经完成了cos+cdn部署静态网站。下面是部署成功的站点 (opens new window),访问速度还可以。
# 2.vercel部署
vercel部署可以说是最简单的部署,还带自动化部署的功能,强烈推荐,目前主站是部署在vercel (opens new window)上的。
进入主界面,选择添加新项目:Add New,选择Project
之后就是选择导入github中的项目,私有仓库也可以,gitpage中这个功能貌似收费,只能public。
配置运行参数,这部分是当初最坑的那个,当时不知道输出填啥,一直构建失败
先选择vue.js构建
接下来就是build和Output settings,按照以下设置即可
其中output directory是很坑,之前不知道这个是输出文件,一直部署dev模式的网站,速度感人。
点击deploy即部署的意思
接下来就是稍微有点长的构建时间,大概10分钟吧,主要是Vdoing作者的文章比较多,构建需要花一定时间。
部署完毕是这样的,不过目前有个小尴尬的事情,自动提供的这几个域名国内访问不了,所以只能自己添加域名了。
在domains中添加自己的域名
先添加,然后根据提示进行添加dns解析
按照提示添加完解析即可
添加完会自动帮你添加ssl证书
大功告成
至此部署已经完成,之后只需要远程提交自己的修改到github上面即可完成修改界面。
# 3.宝塔部署
不说了,这个是几个中最简单的,先打包,然后上传到网站上,之后直接访问即可。
# 打包
# 创建静态网站
记得填入自己的域名并添加dns解析
将里面的文件删去,最后一个.user.ini没删除问题不大,最主要是为了防止冲突
上传完解压一下就行
将解压后的文件复制到网站最开始的目录下,并删去原来的dist和dist.rar文件
最后申请一下免费的证书和开启强制https就结束了(声明,没有hhh.com域名,只是随便输一个)
浏览器键入自己的域名就可以访问了,我的宝塔部署的站点IT七剑客 (opens new window),挺快的,比之前dev模式好多了
# 五、常用命令
# 安装node依赖
自动安装缺少的依赖
npm install
# 项目调试
在测试功能时使用,该模式下,所有改动均可以快速看到效果,无需重新构建
npm run dev
# 项目打包
用于生成环境的打包,项目成功之后可以打包,然后部署到各个地方
npm run build
# 六、写在最后
这次网站的部署大概经历了前前后后一个月,都是凌晨肝一会,书读的少,做事就非常费劲。希望大家能够通过我的这篇文章少走弯路,那这篇文章就是有价值的,有问题评论区留言。