手把手教你使用 Vuepress 搭建带评论系统的静态博客

1,763 阅读6分钟

想在两个小时之内搭建出来一个带有评论系统和谷歌数据分析的静态博客,同时完成静态博客的自动构建和部署吗🚀?

这篇文章对每个步骤都做了详细的记录,还把每个步骤中踩过的坑梳理了出来,,绝对可以给你提供很多帮助~

后续我还会加上「复制博客文章内容加上版本信息」、「全文检索」等功能,敬请期待💪

博客的最终效果可以这里👉 Vuepress 搭建静态博客全攻略

话不多说,让我们开始吧~

Vuepress 搭建静态博客

可以参照 Vuepress 的官方文档 一步一步来就好了,我这里就简单说下步骤:

1. 安装 Vuepress

你可以全局安装:

# 可以使用 npm 来全局安装
npm i -g Vuepress

# 也可以使用 yarn 来全局安装 yarn global add Vuepress

也可以安装在你静态博客项目的开发依赖里面:

yarn add --dev Vuepress

# 或者 npm i -D Vuepress

2. 初始化博客项目

# 可以使用 npm 来初始化项目
npm init

# 也可以使用 yarn 来初始化项目 yarn init

然后在项目的根目录下新建一个 docs 文件夹,以后我们写的 markdown 文件都会放在 docs 文件夹下。 我们先随便初始化一个文件来看看效果:

echo '# Hello VuePress!' > docs/README.md

执行命令 vuepress dev docs 可以看到启动了一个页面: image

为了后续运行方便,我们可以把这些命令写在项目的 package.json 文件里面的 scripts:

{
  "scripts": {
    "dev": "vuepress dev docs",
    "build": "vuepress build docs"
  },
 }

3. 进一步的配置

目前我们只写了一个 markdown 文档,所以只有一个页面,后续我们的博客会陆续加入很多内容,肯定需要做目录分级,配置导航栏,可以看文档里的这部分

4. 部署

静态博客搭好了,我们就可以把它部署到服务器上正式给别人访问了,你可以使用 GitHub Pages(GitHub 的静态页面托管服务 )来托管自己的静态博客。 可以使用 gh-pages 来将打包好的静态博客快速部署到 GitHub Pages 上。

同样的,我们可以将部署的命令加入到 package.json 文件里面的 scripts,现在,我们的 scripts 就是这样的:

{
  "scripts": {
    "dev": "vuepress dev docs",
    "build": "vuepress build docs",
    "deploy": "npm run build && gh-pages -d docs/.vuepress/dist"
  },
 }

其他第三方静态页面托管服务

你也可以使用其他第三方静态页面托管服务(如 Netlify、Coding Pages、Gitlab Pages 等),这篇文章里面介绍了很多免费的静态页面托管服务。

GitHub Actions 自动构建/部署

大家有注意到 GitHub 悄悄上线了一个 Actions 功能吗?还不了解的同学可以看这篇文章,写的非常全面。

GitHub Actions 是什么

GitHub 官方号称 Actions 可以让你的工作流自动化:GitHub 监听某个事件(可能是某个分支的提交),然后触发你预定义的工作流,让大家在GitHub服务器上直接测试代码、部署代码。所以,我们可以利用这里特性来做 CI/CD,开发者只要写一下 workflow 脚本就可以了,不用费心思去想要用哪个第三方的 CI/CD 服务啦~

actions 其实就是由一些脚本组成,所以它们是可以复用的,GitHub 做了一个官方市场,可以搜索到他人提交的 actions。另外,还有一个 awesome actions 的仓库,也可以找到不少 action。这样一来,你甚至都不用自己写具体的脚本,直接引用别人的脚本就行啦。

话不多说,赶紧用起来!

写 workflow 脚本

首先我们需要到项目仓库的页面上进入 Actions 这个 tab, 选择 Node 环境进入脚本的编辑页面 image image

这里我直接使用了 peaceiris 的 actions-gh-pages,这个 action 可以帮你把打包好的静态文件部署到 GitHub Pages 上去。

最终我的 workflow 脚本如下: image 这里有我的脚本源文件,可以参考一下。

更详细的语法可以去看 GitHub Actions 的官方文档

注意

因为我用的 action 是第三方的,所以 action 可能会经常更改,如果你是过了一段时间才看到这篇文章,peaceiris 的 actions-gh-pages 很可能已经发生了更新,所以脚本的内容建议直接参照它的官方文档来写。

设置 workflow 的环境变量

上面的脚本里面第21行的环境变量是怎么回事呢? image

因为我们需要 GitHub Actions 把构建成果发到 GitHub 仓库,因此需要 GitHub 密钥,相当于是给 GitHub actions 授权。

首先运行下面的命令生成一对密钥:

ssh-keygen -t rsa -b 4096 -C "$(git config user.email)" -f gh-pages -N ""
# You will get 2 files:
#   gh-pages.pub (public key)
#   gh-pages     (private key)

然后:

  1. 在博客项目的仓库的 Settings 栏下,找到 Deploy keys这一项,把你的公钥加进去,注意勾选Allow write access
  2. 同样在博客项目的仓库的 Settings 栏下,找到 Secrets这一项,把你的私钥加进去 image

注意事项

  1. Vuepress 作为你的开发依赖加入到项目的 package.json 文件里面进行管理。(否则在 GitHub 的 docker 容器里打包的时候会找不到 Vuepress 这个命令)
  2. 使用 npm 而不是 yarn 来管理依赖包。(因为 npm 是 node 环境自带的包管理工具,无需额外安装,另外, peaceiris 的 actions-gh-pages 也是使用 npm 作为包管理工具的)
  3. 使用 npm ci 来安装依赖包,而不是 npm install

npm ci 和 npm i 的区别

可以看 medium 上的这篇文章 了解一下。

简单来说就是:不像npm installnpm ci永远不会修改您的package-lock.json,它的使用依赖于 package-lock.json文件。所以如果使用npm ci,您将获得可靠的版本。通常这个命令会在 JenkinsGitLab CI之类的持续集成工具中使用。

好了,这样每次你的项目 master 分支一旦有新的提交,就会自动触发这个 workflow,就可以自动完成静态博客的构建和部署了~

也就是说,我们可以直接利用 GitHub 的 markdown 编辑器,直接新建一个文档进行协作,然后提交到 master 分支就行了,完全不用再次去做 git 提交以及本地的构建和部署,简直太方便了 :rocket:

添加评论系统

搭建博客的目的肯定是为了能和更多技术同好交流,所以评论系统是不可或缺的一个功能。

我们可以使用 gitalk 这个开源的评论插件来做博客的评论系统,它是基于 GitHub Issue 来开发的,可以直接使用 GitHub 账号登录,这对程序员来说真的是相当友好了👬

注册 GitHub OAuth Application

首先要申请一个 GitHub OAuth Application。可以点击这里申请.

image
image

注意

Authorization callback URL 一定要填写当前使用插件页面的域名。

注册成功后你会得到一个 Client ID 和 Client Secret,这两个数据我们下一步要用到。

创建评论组件

我们回到工程,在 docs/.vuepress 下新建一个文件夹 components,再在 components 文件夹下建一个 comment 文件夹,然后新建文件 comment.vue,并复制下面的代码。

<template>
  <div class="gitalk-container">
    <div id="gitalk-container"></div>
  </div>
</template>
<script>
export default {
  name: 'comment',
  data() {
    return {};
  },
  mounted() {
    let body = document.querySelector('.gitalk-container');
    let script = document.createElement('script');
    script.src = 'https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.min.js';
    body.appendChild(script);
    script.onload = () => {
      const commentConfig = {
        clientID: 'YOUR_CLINENT_ID',
        clientSecret: 'YOUR_CLINENT_SECRET',
        repo: '此仓库的名称',
        owner: '你的 GitHub 用户名,注意是用户名!!!',
        // 这里接受一个数组,可以添加多个管理员
        admin: ['你的 GitHub 用户名'],
        // id 用于当前页面的唯一标识,一般来讲 pathname 足够了,
    <span class="hljs-comment" style="color: #57A64A; font-style: italic; line-height: 26px;">// 但是如果你的 pathname 超过 50 个字符,GitHub 将不会成功创建 issue,此情况可以考虑给每个页面生成 hash 值的方法.</span>
    id: location.pathname,
    <span class="hljs-attr" style="color: #9CDCFE; line-height: 26px;">distractionFreeMode</span>: <span class="hljs-literal" style="color: #569CD6; line-height: 26px;">false</span>,
  };
  <span class="hljs-keyword" style="color: #569CD6; line-height: 26px;">const</span> gitalk = <span class="hljs-keyword" style="color: #569CD6; line-height: 26px;">new</span> Gitalk(commentConfig);
  gitalk.render(<span class="hljs-string" style="color: #D69D85; line-height: 26px;">'gitalk-container'</span>);
};

}, }; </script>

注意

id 不能超过50个字符,否则后续评论组件初始化的时候会发生 Error:validation failed 的报错。

你可以使用较短的字符来作为 id,例如我自己是使用 document.title.replace(/\s\|\s去冲浪鸭|《|》/g, "") 来作为 id 的,即使用我的文章标题作为 id。

配置组件

在工程根目录下新建一个文件夹 builds,并在里面新建三个文件,分别是 findMarkdown.js, addComponents.js 和 delComponents.js。

findMarkdown.js 文件读取你所有的 Markdown 文件的内容。

// findMarkdown.js
const fs = require('fs')

function findMarkdown(dir, callback) { fs.readdir(dir, function (err, files) { if (err) throw err

files.forEach(<span class="hljs-function" style="color: #DCDCDC; line-height: 26px;">(<span class="hljs-params" style="color: #DCDCDC; line-height: 26px;">fileName</span>) =&gt;</span> {
  <span class="hljs-keyword" style="color: #569CD6; line-height: 26px;">let</span> innerDir = <span class="hljs-string" style="color: #D69D85; line-height: 26px;">`<span class="hljs-subst" style="color: #DCDCDC; line-height: 26px;">${dir}</span>/<span class="hljs-subst" style="color: #DCDCDC; line-height: 26px;">${fileName}</span>`</span>

  <span class="hljs-keyword" style="color: #569CD6; line-height: 26px;">if</span> (fileName.indexOf(<span class="hljs-string" style="color: #D69D85; line-height: 26px;">'.'</span>) !== <span class="hljs-number" style="color: #B8D7A3; line-height: 26px;">0</span>) {
    fs.stat(innerDir, <span class="hljs-function" style="color: #DCDCDC; line-height: 26px;"><span class="hljs-keyword" style="color: #569CD6; line-height: 26px;">function</span> (<span class="hljs-params" style="color: #DCDCDC; line-height: 26px;">err, stat</span>) </span>{

      <span class="hljs-keyword" style="color: #569CD6; line-height: 26px;">if</span> (stat.isDirectory()) {
        findMarkdown(innerDir, callback)
      } <span class="hljs-keyword" style="color: #569CD6; line-height: 26px;">else</span> {
        <span class="hljs-comment" style="color: #57A64A; font-style: italic; line-height: 26px;">// 跳过readme 文件,当然你也可以自行修改</span>
        <span class="hljs-keyword" style="color: #569CD6; line-height: 26px;">if</span> (<span class="hljs-regexp" style="color: #9A5334; line-height: 26px;">/\.md$/</span>.test(fileName) &amp;&amp; !<span class="hljs-regexp" style="color: #9A5334; line-height: 26px;">/README/</span>.test(fileName))
          callback(innerDir);
      }
    })
  }

})

}) }

module.exports = findMarkdown

addMarkdown.js 文件将 comment 组件注册到每个 Markdown 文件的最后。

// addMarkdown.js
const fs = require('fs')
const findMarkdown = require('./findMarkdown')
const rootDir = './docs'

findMarkdown(rootDir, writeComponents)

function writeComponents(dir) { if (!/README/.test(dir)) { fs.appendFile(dir, \n \n &lt;comment-comment/&gt; \n, (err) => { if (err) throw err console.log(add components to <span class="hljs-subst" style="color: #DCDCDC; line-height: 26px;">${dir}</span>) }) } }

delMarkdown.js 文件在编译后执行,目的是将每个 Markdown 文件的 comment 组件移除,因为我们只想让 comment 组件打包到编译后的文件中,而非工程文件。

// delMarkdown.js
const fs = require('fs')
const findMarkdown = require('./findMarkdown')
const rootDir = './docs'

findMarkdown(rootDir, delComponents)

function delComponents(dir) { fs.readFile(dir, 'utf-8', (err, content) => { if (err) throw err

fs.writeFile(dir, content.replace(<span class="hljs-regexp" style="color: #9A5334; line-height: 26px;">/\n \n &lt;comment-comment\/&gt; \n /g</span>, <span class="hljs-string" style="color: #D69D85; line-height: 26px;">''</span>), (err) =&gt; {
  <span class="hljs-keyword" style="color: #569CD6; line-height: 26px;">if</span> (err) <span class="hljs-keyword" style="color: #569CD6; line-height: 26px;">throw</span> err
  <span class="hljs-built_in" style="color: #4EC9B0; line-height: 26px;">console</span>.log(<span class="hljs-string" style="color: #D69D85; line-height: 26px;">`del components from <span class="hljs-subst" style="color: #DCDCDC; line-height: 26px;">${dir}</span>`</span>)
})

}) }

修改构建脚本

修改 build 的脚本:

{
  "build": "node ./builds/addComponents.js && vuepress build docs && node ./builds/delComponents.js"
}

好了,这样你的博客就有一个评论系统了,效果如下: image

每次有人在一篇评论数为0的文章下面评论,就会自动以当前文章为标题,当前文章的网址为内容生成一条 GitHub Issue,评论就会被关联到这条 GitHub Issue 上。

配置 Google Analytics

博客搭建好了,也有人访问了,那我们要怎么统计用户的访问情况呢?可以使用谷歌出品的 Google Analytics (GA)。

Google Analytics (GA)是一个对用户活动进行追踪的工具,利用 GA 我们可以收集到博客当前有多少实时活跃用户,博客的总访问量,以及分析用户的一些访问行为,便于我们对博客网站做一些优化,而且它还是免费的!赶快用起来!

下载 google-analytics 插件

Vuepress 官方已经为我们准备好了 google-analytics 插件。

yarn add -D @vuepress/plugin-google-analytics
# OR npm install -D @vuepress/plugin-google-analytics

安装完在 config 配置文件里面配置一下 plugins

module.exports = {
  plugins: [
    [
      '@vuepress/google-analytics',
      {
        'ga': '' // UA-00000000-0
      }
    ]
  ]
}

注册 GA,获取追踪 ID

上面那个 ga ID从哪里获取呢?别着急,我们需要到 Google Analytics 的官网上去注册一下我们的博客应用: image

进去后选择 web app: image image

把获取到的跟踪ID 填到上面👆的ga这一项里面就好了。

配置全文搜索

待补充...

给 Vuepress 博客做 SEO 优化

待补充...

复制文字时显示版权信息

待补充...