经过一段时间的摸索,踩过了无数的坑,终于搭建好了一个属于自己的博客。在此记录一下踩坑过程,让以后的小伙伴们搭建自己博客的时候少走弯路。博客采用了vuepress@2.x版本进行构建,同时对默认主题进行了本地化继承修改,整个主题大的布局未改动,只是对首页和展示页面进行了布局调整,最终效果为当前博客显示效果。小伙伴们还等什么,赶快开始吧!👍

提示

1、博主以前也用vuepress-theme-reco@1.x主题搭建了一个博客,比较喜欢vuepress-theme-reco@1.x风格,由于vuepress-theme-reco@1.x采用的技术都相对目前比较落后,后来便用vuepress 2.X开发了现在的博客,整体页面风格和样式和原来的一模一样,但主题还是继承了vuepress 2.X默认主题,增加了博客,分类,标签功能和其他首页特效插件。

2、博客地址:https://anyfork.github.io/blog-docs/open in new windowhttps://anyfork.gitee.io/blog-docs/open in new window

一 初始化项目

1 环境依赖

注意

2 快速开始

  • 创建项目目录
# 创建项目目录
mkdir blog-docs
# 进入项目目录
cd blog-docs
  • 初始化项目
git init
yarn init
git init
npm init
  • 依赖安装
yarn add -D vuepress@next
npm install -D vuepress@next
  • package.json增加中添加一些 scripts
{
  "scripts": {
    "dev": "vuepress dev docs",
    "build": "vuepress build docs"
  }
}
  • 将默认的临时目录和缓存目录添加到.gitignore文件中
echo 'node_modules' >> .gitignore
echo '.temp' >> .gitignore
echo '.cache' >> .gitignore
echo '.history' >> .gitignore
  • 创建文档目录
mkdir docs
echo '# Hello VuePress' > docs/README.md

3 项目启动

用 vscode 打开项目,新建终端,执行启动脚本命令

yarn dev
npm run docs:dev

详细的创建过程请参考vuepress 官网快速上手open in new window,至此项目搭建基础就调整完了,小伙伴们就可以开开心心的写markdown文件了。

二 功能配置

vuepress 2.x默认主题集成了大部分功能,基本能够满足我们编写markdown的需求,但官方主题主要以文档为主,对于博客功能的拓展需要自己开发。默认主题允许我们通过继承的方式来拓展默认主题,详情参考官网主题拓展open in new window

 博主采用了默认主题预设的布局插槽组件替换2 种方式继承默认主题(即:本地主题开发)。组件替换主要体现在博客首页布局,分类,标签,时间轴布局等,布局插槽主要涉及详情页顶部分类和底部评论等。同时通过vuepress-plugin-blog2插件拓展了博客功能(文章分类,标签,时间轴),vuepress-plugin-blog2hope插件设计比较灵活,可以根据自身需求进行定制,不限于分类和标签等功能,可以自行拓展。

1 布局插槽

提示

1、默认主题内置 11 个布局插槽,分别为:navbar,navbar-before,navbar-after,sidebar,sidebar-top,sidebar-bottom,page,page-top,page-bottom,page-content-top,page-content-bottom,详情参考官网布局插槽open in new window

2、博客中用到了 2 个插槽page-toppage-bottom。前者用于拓展文章页面标题下面文章作者,文章分类,文章标签,字数统计和预估阅读时间等信息,后者主要嵌套giscus评论组件<CommentService />,实现文章评论功能。

  • 配置方法

    1、创建自定义布局组件.vuepress/theme/layouts/layout.vue

    layout 配置
    <template>
      <ParentLayout>
        <template #page-top>
          <div class="title w-[var(--content-width)] my-0 mx-auto">
            <div class="flex items-center justify-between">
              <h1>{{ page.title }}</h1>
              <Icon icon="RollbackOutlined" text="返回" @click="$router.go(-1)" class="cursor-pointer"></Icon>
            </div>
            <BlogItemInfo :page="page"></BlogItemInfo>
          </div>
        </template>
        <template #page-bottom>
          <CommentService />
        </template>
      </ParentLayout>
    </template>
    <script setup lang="ts">
    import ParentLayout from '@vuepress/theme-default/lib/client/layouts/Layout.vue'
    import BlogItemInfo from '../components/Blog/BlogItemInfo.vue'
    import { usePageData } from '@vuepress/client'
    const page = usePageData()
    </script>
    
    

    2、创建你的本地主题.vuepress/theme/index.ts,并覆盖默认主题布局。

    index.ts 配置
    import type { Theme } from '@vuepress/core'
    import { defaultTheme } from '@vuepress/theme-default'
    import { path } from '@vuepress/utils'
    import { AnyForkThemeOptions } from './types/theme'
    
    export const AnyForkTheme = (options: AnyForkThemeOptions): Theme => {
      return {
        name: 'vuepress-theme-AnyFork',
        extends: defaultTheme(options),
        alias: {
          '@theme/Home.vue': path.resolve(__dirname, './components/Home.vue'),
          '@theme/NavbarItems.vue': path.resolve(__dirname, './components/NavbarItems.vue'),
          '@theme/HomeFooter.vue': path.resolve(__dirname, './components/HomeFooter.vue')
        },
        layouts: {
          Layout: path.resolve(__dirname, './layouts/Layout.vue'),
          Category: path.resolve(__dirname, './layouts/Category.vue'),
          Tag: path.resolve(__dirname, './layouts/Tag.vue'),
          Timeline: path.resolve(__dirname, './layouts/Timeline.vue'),
          404: path.resolve(__dirname, './layouts/404.vue')
        }
      }
    }
    

2 组件替换

提示

1、布局插槽十分实用,但有时候你可能会觉得它不够灵活。默认主题同样提供了替换单个组件的能力。

2、默认主题将所有 非全局的组件 都注册了一个带 @theme 前缀的 alias 。例如,HomeFooter.vue 的别名是 @theme/HomeFooter.vue 。

  • 配置方法

    1、在自定义主题组件目录.vuepress/theme/components下创建自己需要覆盖的组件。

    2、在配置文件.vuepress/theme/index.ts中进行组件覆盖

    index.ts 配置
    import type { Theme } from '@vuepress/core'
    import { defaultTheme } from '@vuepress/theme-default'
    import { path } from '@vuepress/utils'
    import { AnyForkThemeOptions } from './types/theme'
    
    export const AnyForkTheme = (options: AnyForkThemeOptions): Theme => {
      return {
        name: 'vuepress-theme-AnyFork',
        extends: defaultTheme(options),
        alias: {
          '@theme/Home.vue': path.resolve(__dirname, './components/Home.vue'),
          '@theme/NavbarItems.vue': path.resolve(__dirname, './components/NavbarItems.vue'),
          '@theme/HomeFooter.vue': path.resolve(__dirname, './components/HomeFooter.vue')
        },
        layouts: {
          Layout: path.resolve(__dirname, './layouts/Layout.vue'),
          Category: path.resolve(__dirname, './layouts/Category.vue'),
          Tag: path.resolve(__dirname, './layouts/Tag.vue'),
          Timeline: path.resolve(__dirname, './layouts/Timeline.vue'),
          404: path.resolve(__dirname, './layouts/404.vue')
        }
      }
    }
    

3 博客插件

 博客插件使用的是第三方vuepress-plugin-blog2open in new windowhope插件插件。此插件和普通插件使用方式一样,根据官网配置open in new window即可。

注意

vuepress-plugin-blog2博客插件主要用户扩展博客功能,未实现分页功能,需要自行实现分页功能。

  • 项目配置

     //博客插件,https://vuepress-theme-hope.github.io/v2/blog/zh/guide.html
      blogPlugin({
          // 页面过滤器,此函数用于鉴别页面是否作为文章。
          filter: ({ filePathRelative }) => filePathRelative ? filePathRelative?.startsWith("posts/") : false,
          // 获取文章信息的函数。
          getInfo: (page) => ({
              ...page
          }),
          category: [
              {
                  key: "category",
                  getter: (page) => <string[]>page.frontmatter.category || [],
                  layout: "Category",
                  itemLayout: "Category",
                  frontmatter: () => ({ title: "Categories", sidebar: false }),
                  itemFrontmatter: (name) => ({
                      title: `Category ${name}`,
                      sidebar: false,
                  }),
              },
              {
                  key: "tag",
                  getter: (page) => <string[]>page.frontmatter.tag || [],
                  layout: "Tag",
                  itemLayout: "Tag",
                  frontmatter: () => ({ title: "Tags", sidebar: false }),
                  itemFrontmatter: (name) => ({
                      title: `Tag ${name}`,
                      sidebar: false,
                  }),
              },
          ],
    
          type: [
              {
                  key: "article",
                  //需要过滤的条件
                  filter: (page) => !page.frontmatter.archive,
                  path: "/article/",
                  layout: "Layout",
                  frontmatter: () => ({ title: "Articles", sidebar: false }),
                  // sort pages with time and sticky
                  sorter: (pageA, pageB) => {
                      if (pageA.frontmatter.sticky && pageB.frontmatter.sticky)
                          return pageA.frontmatter.sticky as number - (pageB.frontmatter.sticky as number);
                      if (pageA.frontmatter.sticky && !pageB.frontmatter.sticky)
                          return -1;
                      if (!pageA.frontmatter.sticky && pageB.frontmatter.sticky) return 1;
                      if (!pageB.frontmatter.date) return 1;
                      if (!pageA.frontmatter.date) return -1;
                      return (
                          new Date(pageB.frontmatter.date).getTime() - new Date(pageA.frontmatter.date).getTime()
                      );
                  },
              },
              {
                  key: "timeline",
                  // only article with date should be added to timeline
                  filter: (page) => page.frontmatter.date ? true : false,
                  // sort pages with time
                  sorter: (pageA, pageB) => new Date(pageB.frontmatter.date as string).getTime() - new Date(pageA.frontmatter.date as string).getTime(),
                  path: "/timeline/",
                  layout: "Timeline",
                  frontmatter: () => ({ title: "Timeline", sidebar: false }),
              },
          ],
          hotReload: true,
      }),
    

    注意:插件中有categorytype区分,即类别和类型,可以简单理解为文章条件赛选为类别,比如:分类,标签等,类型为文章集合,时间轴等。

  • 客户端组合 API 1、客户端可以通过useBlogCategory()useBlogType()获取当前路由绑定的类别或类型。

2、可以传递参数获取具体的类别信息,比如useBlogCategory('category')

4 自定义容器

提示

1、vueprsss 2.x 中内置了自定义容器,详情参考自定义容器open in new window

2、自定义容器依赖默认主题内置插件@vuepress/plugin-container,相对于vuepress-plugin-container(vuepress 1.x)而言变动不大,只是插件使用方式采用函数式方式集成。

 博客中内置了 2 个自定义容器cardListcardImgList,分别用于展示文本列表和图片列表,具体效果看下图:

cardList 展示效果

- name: 午后南杂
  avatar: https://www.recoluan.com/head.png
  desc: Enjoy when you can, and endure when you must.
  link: https://www.recoluan.com
  bgColor: 'rgb(165 216 243)'
  textColor: '#6854A1'
- name: 西瓜皮儿
  desc: enjoy your grow up!
  avatar: https://coderhdy.com/assets/img/snapshot.png
  link: https://coderhdy.com/
  bgColor: '#FCE5BF'
  textColor: '#7B2532'
- name: 游履平生
  desc: 不积跬步,无以至千里.不积小流,无以成江海
  avatar: https://cdn.staticaly.com/gh/AnyFork/blog-images/main/markdown/202208241822713.png
  link: https://anyfork.gitee.io/blog-docs/
  bgColor: rgb(218 88 88 / 62%)
  textColor: rgb(5 41 14 / 75%)

cardImgList展示效果

- img: https://vuepress-theme-reco.recoluan.com/icon_vuepress_reco.png
  link: https://vuepress-theme-reco.recoluan.com/views/other/theme-example.html
  name: vuepress-theme-reco官网案例
  desc: 官网更多优秀的博客案例
- img: https://vuepress-theme-reco.recoluan.com/icon_vuepress_reco.png
  link: https://vuepress-theme-reco.recoluan.com/views/plugins/
  name: vuepress-theme-reco官方插件
  desc: 官网更多优秀的插件
- img: https://vuepress.vuejs.org/hero.png
  link: https://www.vuepress.cn/
  name: VuePress 中文官网
  desc: VuePress 中文官网地址
- img: https://vuepress.vuejs.org/hero.png
  link: https://vuepress-community.netlify.app/zh/
  name: VuePress 官方社区
  desc: 官网更多优秀的插件
  • 自定义容器实现方式

    1、在自定义主题配置文件目录.vuepress/theme/config/container.ts下创建自定义容器文件。

    import type { ContainerPluginOptions, MarkdownItContainerRenderFunction } from '@vuepress/plugin-container'
    const yaml = require('js-yaml')
    const CARD_LIST = 'cardList'
    const CARD_IMG_LIST = 'cardImgList'
    // 渲染md容器的卡片列表
    function renderCardList(tokens: any, idx: number) {
      const type = tokens[idx].type.split('_')[1]
      const END_TYPE = `container_${type}_close`,
        _tokens$idx = tokens[idx],
        nesting = _tokens$idx.nesting,
        info = _tokens$idx.info
      if (nesting === 1) {
        // 渲染开头的 ':::' 标记
        let yamlStr = ''
        for (let i = idx; i < tokens.length; i++) {
          let _tokens$i = tokens[i],
            type = _tokens$i.type,
            content = _tokens$i.content,
            _info = _tokens$i.info
          if (type === END_TYPE) break // 遇到结束的 ':::' 时
          if (!content) continue
          if (type === 'fence' && _info === 'yaml') {
            // 是代码块类型,并且是yaml代码
            yamlStr = content
          }
        }
        if (yamlStr) {
          // 正确解析出yaml字符串后
          const dataObj = yaml.load(yamlStr) // 将yaml字符串解析成js对象
          let dataList: Array<string> = []
    
          if (dataObj) {
            // 正确解析出数据对象
            dataList = Array.isArray(dataObj) ? dataObj : dataObj.list
          }
    
          if (dataList && dataList.length) {
            // 有列表数据
    
            // 每行显示几个
            let row = Number(info.split(' ').pop())
            if (!row || row > 4 || row < 1) {
              row = 3 // 默认 3
            }
    
            let listDOM = ''
            if (type === CARD_LIST) {
              // 普通卡片列表
              listDOM = getCardListDOM(dataList, row)
            } else if (type === CARD_IMG_LIST) {
              // 卡片图片列表
              listDOM = getCardImgListDOM(dataList, row)
            }
    
            return `<div class="${type}Container"><div class="card-list">${listDOM}</div>`
          }
        }
      } else {
        // 渲染':::' 结尾
        return '</div>'
      }
    }
    
    // 将数据解析成 DOM 结构 - 普通卡片列表
    function getCardListDOM(dataList: Array<string>, row: number) {
      let listDOM = ''
      dataList.forEach((item: any) => {
        listDOM += ` <${item.link ? 'a href="' + item.link + '" target="_blank"' : 'span'} class="card-item ${row ? 'row-' + row : ''}" style="${item.bgColor ? 'background-color:' + item.bgColor + ';' : ''}${item.textColor ? 'color:' + item.textColor + ';' : ''}" > ${
          item.avatar ? '<img src="' + item.avatar + '" class="no-zoom">' : ''
        } <div> <p class="name">${item.name}</p> <p class="desc">${item.desc}</p> </div> </${item.link ? 'a' : 'span'}> `
      })
      return listDOM
    }
    
    // 将数据解析成 DOM 结构 - 图文卡片列表
    function getCardImgListDOM(dataList: Array<string>, row: number) {
      let listDOM = ''
      dataList.forEach((item: any) => {
        listDOM += ` <div class="card-item ${row ? 'row-' + row : ''}" > <div class="box-img"> <a href="${item.link}" target="_blank"> <img src="${item.img}" class="no-zoom"> </a> </div> <div class="box-info"> <a href="${item.link}" target="_blank"> <p class="name">${item.name}</p> ${
          item.desc ? `<p class="desc">${item.desc}</p>` : ''
        }
      </a>
      </div>
     ${item.avatar || item.author ? `<div class="box-footer"><a href="${item.link}" target="_blank">${item.avatar ? `<img src="${item.avatar}" class="no-zoom">` : ''} ${item.author ? `<span>${item.author}</span>` : ''}</a></div>` : ''}</div> `
      })
      return listDOM
    }
    
    export const resolveContainerOptions = (type: string): ContainerPluginOptions => {
      const render: any = renderCardList
      return { type, render }
    }
    

    2、在插件配置文件.vuepress/theme/plugins.ts中进行插件配置

    import { containerPlugin } from '@vuepress/plugin-container'
    import { resolveContainerOptions } from './container'
    export const plugins = [
      //自定义容器cardList
      containerPlugin(resolveContainerOptions('cardList')),
      //自定义容器cardImgList
      containerPlugin(resolveContainerOptions('cardImgList'))
    ]
    

    3、cardList配置

    ::: cardList 3
    
        ```yaml
        - name: 午后南杂
          avatar: https://www.recoluan.com/head.png
          desc: Enjoy when you can, and endure when you must.
          link: https://www.recoluan.com
          bgColor: 'rgb(165 216 243)'
          textColor: '#6854A1'
        - name: 西瓜皮儿
          desc: enjoy your grow up!
          avatar: https://coderhdy.com/assets/img/snapshot.png
          link: https://coderhdy.com/
          bgColor: '#FCE5BF'
          textColor: '#7B2532'
        - name: 游履平生
          desc: 不积跬步,无以至千里.不积小流,无以成江海
          avatar: https://cdn.staticaly.com/gh/AnyFork/blog-images/main/markdown/202208241822713.png
          link: https://anyfork.gitee.io/blog-docs/
          bgColor: rgb(218 88 88 / 62%)
          textColor: rgb(5 41 14 / 75%)
        ```
    
    :::
    

    4、cardImgList配置方式

    ::: cardImgList 2
    
        ```yaml
        - img: https://vuepress-theme-reco.recoluan.com/icon_vuepress_reco.png
          link: https://vuepress-theme-reco.recoluan.com/views/other/theme-example.html
          name: vuepress-theme-reco官网案例
          desc: 官网更多优秀的博客案例
        - img: https://vuepress-theme-reco.recoluan.com/icon_vuepress_reco.png
          link: https://vuepress-theme-reco.recoluan.com/views/plugins/
          name: vuepress-theme-reco官方插件
          desc: 官网更多优秀的插件
        - img: https://vuepress.vuejs.org/hero.png
          link: https://www.vuepress.cn/
          name: VuePress 中文官网
          desc: VuePress 中文官网地址
        - img: https://vuepress.vuejs.org/hero.png
          link: https://vuepress-community.netlify.app/zh/
          name: VuePress 官方社区
          desc: 官网更多优秀的插件
        ```
    
    :::
    

三 插件安装

 vuepress 允许我们通过扩展插件的方式来拓展功能,通过插件可以丰富我们博客功能,使页面效果更加炫酷增强网站的逼格,详细情况参考vuepress@2.x 插件配置方式open in new windowvuepress@2.x 内置插件open in new windowVuepress 2.x插件 API 变化比较大,使用方式也采用函数式调用,因此社区Vuepress 1.x插件不能使用。博主博客开发过程中针对Vuepress 1.x的部分插件进行了兼容Vuepress 2.x适配,以下是博客中用到的插件:

1 PWA 插件官方插件

提示

1、pwaopen in new window插件为vuepress 2.x官方开发的插件,使你的 VuePress 站点成为一个渐进式 Web 应用 (PWA)open in new window,该插件使用workbox-buildopen in new window 来生成 Service Worker 文件,并通过register-service-workeropen in new window 来注册 Service Worker

2、pwa-popupopen in new window 插件为vuepress 2.x官方开发的插件,提供一个弹窗组件,允许用户手动刷新 PWA Service Worker 。 该插件必须和 pwa 插件 一起使用,并且 skipWaiting 配置项不能设置为 true 。当新的 Service Worker 就绪时,会在页面右下角出现一个弹窗,询问用户是否需要激活处于 Waiting 状态的 Service Worker 。

3、pwapwa-popup为官方开发的插件,并未集成的默认主题中,因此需要自己安装依赖。

2 DocSearch 插件官方插件

相关信息

1、将 Algolia DocSearch 集成到 VuePress 中,为你的文档网站提供搜索功能。

2、当你正确配置该插件后,默认主题会把 DocSearch 按钮添加到导航栏。

3、DocSearch为官方开发的插件,并未集成的默认主题中,因此需要自己安装依赖。

  • DocSearchopen in new window插件依赖安装和插件配置:

  • 安装依赖

     npm i -D @vuepress/plugin-docsearch@next
    
  • 插件配置

    import { docsearchPlugin } from '@vuepress/plugin-docsearch'
    module.exports = {
      plugins: [
        //docsearch插件,https://v2.vuepress.vuejs.org/zh/reference/plugin/docsearch.html
        docsearchPlugin({
          apiKey: 'e3224f6a8f05632af9c14c97******b54',
          indexName: 'anyfork',
          appId: '09V****WK61N',
          placeholder: '搜索文档',
          locales: {
            '/': {
              placeholder: '搜索文档',
              translations: {
                button: {
                  buttonText: '搜索文档'
                }
              }
            }
          }
        })
      ]
    }
    
  • 更多详细配置,请参考DocSearchopen in new window官网配置,Algolia配置请参考:VuePress 博客优化之开启 Algolia 全文搜索

3 ReadingTime 插件Hope插件

提示

1、Hope插件vuepress-theme-hope主题作者开发的插件库。

2、Hope插件官网地址:https://vuepress-theme-hope.github.io/v2/zh/config/plugins/intro.htmlopen in new window

3、ReadingTime插件统计文章字数和预估阅读时间,详情参考:https://vuepress-theme-hope.github.io/v2/reading-time/zh/open in new window

yarn add -D vuepress-plugin-reading-time2@next
npm i -D vuepress-plugin-reading-time2@next
pnpm add -D vuepress-plugin-reading-time2@next@next
  • 插件配置
import { readingTimePlugin } from 'vuepress-plugin-reading-time2'
module.exports = {
  plugins: [readingTimePlugin({})]
}

4 SEO 插件Hope插件

提示

1、Hope插件vuepress-theme-hope主题作者开发的插件库。

2、Hope插件官网地址:https://vuepress-theme-hope.github.io/v2/zh/config/plugins/intro.htmlopen in new window

3、SEO插件本插件会通过向网站<head> 注入标签,让你的网站完全支持 开放内容协议 OGP 和 JSON-LD 1.1,以全面增强站点的搜索引擎优化性,详情参考:https://vuepress-theme-hope.github.io/v2/seo/zh/guide.htmlopen in new window

yarn add -D vuepress-plugin-seo2@next
npm i -D vuepress-plugin-seo2@next
pnpm add -D vuepress-plugin-seo2@next
  • 插件配置
import { seoPlugin } from 'vuepress-plugin-seo2'
module.exports = {
  plugins: [
    // seo插件,https://vuepress-theme-hope.github.io/v2/seo/
    seoPlugin({
      hostname: 'https://anyfork.github.io/',
      author: {
        name: '游履平生',
        url: 'https://anyfork.github.io/blog-docs/'
      }
    })
  ]
}

5 SiteMap 插件Hope插件

提示

1、Hope插件vuepress-theme-hope主题作者开发的插件库。

2、Hope插件官网地址:https://vuepress-theme-hope.github.io/v2/zh/config/plugins/intro.htmlopen in new window

3、siteMap插件本插件会为你的网站自动生成 Sitemap。为了使插件正常工作,你需要将部署的域名传递给插件的 hostname 选项。插件会自动根据页面的 Git 的时间戳生成页面的最后更新时间,同时会根据站点的多语言配置声明页面的其他语言版本替代地址。,详情参考:https://vuepress-theme-hope.github.io/v2/sitemap/zh/open in new window

yarn add -D vuepress-plugin-sitemap2@next
npm i -D vuepress-plugin-sitemap2@next
pnpm add -D vuepress-plugin-sitemap2@next
- 插件配置
import { sitemapPlugin } from 'vuepress-plugin-sitemap2'
module.exports = {
  plugins: [
    // siteMap站点地图插件,https://vuepress-theme-hope.github.io/v2/sitemap/zh/config.html
    sitemapPlugin({
      hostname: 'https://anyfork.github.io/blog-docs/',
      extraUrls: ['https://anyfork.gitee.io/blog-docs/']
    })
  ]
}

提示

1、Hope插件vuepress-theme-hope主题作者开发的插件库。

2、Hope插件官网地址:https://vuepress-theme-hope.github.io/v2/zh/config/plugins/intro.htmlopen in new window

3、copyright插件此插件可以在访问者从你的站点复制内容时,自动追加版权信息,也可以禁止站点的复制或者选择。详情参考:https://vuepress-theme-hope.github.io/v2/copyright/zh/open in new window

yarn add -D vuepress-plugin-copyright2@next
npm i -D vuepress-plugin-copyright2@next
pnpm add -D vuepress-plugin-copyright2@next
  • 插件配置
import { copyrightPlugin } from 'vuepress-plugin-copyright2'
module.exports = {
  plugins: [
    //复制加版权插件,https://vuepress-theme-hope.github.io/v2/copyright/zh/config.html
    copyrightPlugin({
      hostname: 'https://anyfork.github.io/blog-docs/',
      author: '游履平生',
      global: true
    })
  ]
}

7 CopyCode 插件Hope插件

提示

1、Hope插件vuepress-theme-hope主题作者开发的插件库。

2、Hope插件官网地址:https://vuepress-theme-hope.github.io/v2/zh/config/plugins/intro.htmlopen in new window

3、copyCode插件此插件会自动添加复制按钮到每个代码块的右下角。默认情况下,按钮仅在桌面模式显示,如果你需要在移动端展示这个按钮,请将 showInMobile 设置为 true。在用户点击复制按钮后,屏幕上会显示一个复制成功的提示。默认的提示时长为 2000ms,如果你需要更改这个时长,请设置 duration(单位 ms),如果你不需要这个提示,请将 duration 设置为 0。详情参考:https://vuepress-theme-hope.github.io/v2/copy-code/zh/open in new window

yarn add -D vuepress-plugin-copy-code2@next
npm i -D vuepress-plugin-copy-code2@next
pnpm add -D vuepress-plugin-copy-code2@next
  • 插件配置
import { copyCodePlugin } from 'vuepress-plugin-copy-code2'
module.exports = {
  plugins: [
    //代码复制插件,https://vuepress-theme-hope.github.io/v2/copy-code/zh/
    copyCodePlugin({
      selector: '.theme-default-content div[class*="language-"] pre',
      locales: {
        '/': {
          copy: '复制成功!',
          hint: 'copy!'
        }
      }
    })
  ]
}

8 Waline 评论插件Hope插件

提示

1、Hope插件vuepress-theme-hope主题作者开发的插件库。

2、Hope插件官网地址:https://vuepress-theme-hope.github.io/v2/zh/config/plugins/intro.htmlopen in new window

3、Giscus插件Giscus 是一个基于 GitHub Discussion 的评论系统,启用简便。详情参考:https://vuepress-theme-hope.github.io/v2/comment/zh/guide/giscus.htmlopen in new window,原本博主采用的是Giscus作为评论插件,但发现其没有浏览量统计功能,后采用Waline插件,不仅具有评论功能,还能进行浏览量统计。

4、Waline插件是对valine插件的封装,增加了服务端功能,解决了valine数据不安全问题,同时也具备valine一切功能。可以使用 hope 插件用法,单页面集成好了浏览量功能,通过寻找默认类选择器waline-pageview-count进行数据自动填充,但对于首页列表未实现浏览量功能,如果需要,自行实现即可。插件配置地址:https://vuepress-theme-hope.github.io/v2/comment/zh/config/waline.htmlopen in new window,亦可单独使用,详见waline 官网open in new window

yarn add -D vuepress-plugin-comment2@next
npm i -D vuepress-plugin-comment2@next
pnpm add -D vuepress-plugin-comment2@next
  • 插件配置

提示

1、可以按照hope插件配置流程进行配置,采用waline官方默认的LeanCloud 数据库和Vercel服务端部署方式。博主尝试过国内效果不太好,不开梯子情况下Vercel服务端地址无法访问,于是便放弃了。

2、按照waline 官网open in new window服务端部署方式进行服务端部署。各种方式都尝试了一下,结果如下:1 腾讯云云函数计算今年 8 月开始收费,最低每月 19.9,放弃了。2 百度云按照官网流程部署,程序报错,依赖版本问题,便放弃了。3 阿里云部署成功了,云函数计算每月 100 万免费次数,可以尝试,但服务 url 需要自定义域名进行映射,不然没法直接使用,会直接下载 html,需要有阿里云域名或者说阿里云服务器,博主没有阿里云服务器便放弃了。上面的方式阿里云最合适,有条件的小伙伴可以尝试。

3、博主推荐通过自己部署waline服务器的方式,主要原因是:部署简单,灵活,访问速度更快,还能自定义数据库,前提是自己要有云服务器和域名。详细部署方式参考官网独立部署open in new window或者Waline 服务端独立部署解决方案。按照上述操作即可,只需要配置好数据库环境变量,上线部署即可。

import { commentPlugin } from 'vuepress-plugin-comment2'
module.exports = {
  plugins: [
    commentPlugin({
      provider: 'Waline',
      //独立部署的waline服务器访问地址
      serverURL: 'XXXX',
      //是否开启访问量
      pageview: true
    })
  ]
}

9 Markdown 语法扩展插件Hope插件

提示

1、Hope插件vuepress-theme-hope主题作者开发的插件库。

2、Hope插件官网地址:https://vuepress-theme-hope.github.io/v2/zh/config/plugins/intro.htmlopen in new window

3、Markdown语法扩展插件通过安装并启用此插件,你可以在 Markdown 中使用更多的语法。详情参考:https://vuepress-theme-hope.github.io/v2/md-enhance/zh/open in new window

yarn add -D vuepress-plugin-md-enhance@next
npm i -D vuepress-plugin-md-enhance@next
pnpm add -D vuepress-plugin-md-enhance@next
  • 插件配置
import { mdEnhancePlugin } from 'vuepress-plugin-md-enhance'
module.exports = {
  plugins: [
    //markdown 增强插件,https://vuepress-theme-hope.github.io/v2/md-enhance/zh/guide/
    mdEnhancePlugin({
      // 启用自定义容器
      container: true
    })
  ]
}

你可以在选项中 enableAll 设置为 true 来启用插件的所有功能,不建议开启全部扩展功能,可以根据实际情况进行开启

10 音乐播放器插件AnyFork插件

提示

1、AnyFork插件指博主自己开发适配的插件。

2、AnyFork插件官网地址:https://github.com/AnyFork/vuepress-pulgins/tree/2.X/packages/%40anyforkopen in new window

3、音乐播放器插件可以在网站全局注册音乐播放器组件,可以配置本地或线上音乐,实现音乐在线播放。

yarn add -D @anyfork/vuepress-plugin-bgm-player-next
npm i -D @anyfork/vuepress-plugin-bgm-player-next
  • 插件配置
import { bgmMusicPlayer } from '@anyfork/vuepress-plugin-bgm-player-next'
module.exports = {
  plugins: [
    //音乐播放器插件。
    bgmMusicPlayer({
      audios: [
        {
          name: '卡农',
          artist: '卡农钢琴版',
          url: '/blog-docs/music/canon/canon.mp3',
          cover: '/blog-docs/music/canon/canon.jpg'
        },
        {
          name: '风居住的街道',
          artist: '风居住的街道 钢琴版 - 钢琴曲',
          url: '/blog-docs/music/wind/wind.mp3',
          cover: '/blog-docs/music/wind/wind.jpg'
        },
        {
          name: '夜的钢琴曲',
          artist: '夜的钢琴曲五 - 石进',
          url: '/blog-docs/music/night/night.mp3',
          cover: '/blog-docs/music/night/night.jpg'
        }
      ],
      autoShrink: true,
      floatStyle: { bottom: '100px', 'z-index': '999999' }
    })
  ]
}

11 鼠标点击特效插件AnyFork插件

提示

1、AnyFork插件指博主自己开发适配的插件。

2、AnyFork插件官网地址:https://github.com/AnyFork/vuepress-pulgins/tree/2.X/packages/%40anyforkopen in new window

3、鼠标点击特效插件鼠标点击时触发点击特效。详情参考:https://github.com/AnyFork/vuepress-pulgins/tree/2.X/packages/%40anyfork/vuepress-plugin-cursor-effects-nextopen in new window

yarn add -D  @anyfork/vuepress-plugin-cursor-effects-next
npm i -D @anyfork/vuepress-plugin-cursor-effects-next
  • 插件配置
import { cursorEffects } from '@anyfork/vuepress-plugin-cursor-effects-next'
module.exports = {
  plugins: [
    //鼠标点击特效插件
    cursorEffects({
      size: 4,
      shape: 'star'
    })
  ]
}

12 动态标题特效插件AnyFork插件

提示

1、AnyFork插件指博主自己开发适配的插件。

2、AnyFork插件官网地址:https://github.com/AnyFork/vuepress-pulgins/tree/2.X/packages/%40anyforkopen in new window

3、动态标题特效插件当今日或离开页面 tabs 时 title 发生变化。详情参考:https://github.com/AnyFork/vuepress-pulgins/tree/2.X/packages/%40anyfork/vuepress-plugin-dynamic-title-nextopen in new window

yarn add -D  @anyfork/vuepress-plugin-dynamic-title-next
npm i -D @anyfork/vuepress-plugin-dynamic-title-next
  • 插件配置
import { dynamicTitle } from '@anyfork/vuepress-plugin-dynamic-title-next'
module.exports = {
  plugins: [dynamicTitle()]
}

13 看板娘插件AnyFork插件

提示

1、AnyFork插件指博主自己开发适配的插件。

2、AnyFork插件官网地址:https://github.com/AnyFork/vuepress-pulgins/tree/2.X/packages/%40anyforkopen in new window

3、看板娘插件当今日或离开页面 tabs 时 title 发生变化。详情参考:https://github.com/AnyFork/vuepress-pulgins/tree/2.X/packages/%40anyfork/vuepress-plugin-kan-ban-niang-nextopen in new window

yarn add -D  @anyfork/vuepress-plugin-kan-ban-niang-next
npm i -D @anyfork/vuepress-plugin-kan-ban-niang-next
  • 插件配置
import { kanBanNiang } from '@anyfork/vuepress-plugin-kan-ban-niang-next'
module.exports = {
  plugins: [kanBanNiang()]
}

14 背景彩带插件AnyFork插件

提示

1、AnyFork插件指博主自己开发适配的插件。

2、AnyFork插件官网地址:https://github.com/AnyFork/vuepress-pulgins/tree/2.X/packages/%40anyforkopen in new window

3、背景彩带插件背景彩带。详情参考:https://github.com/AnyFork/vuepress-pulgins/tree/2.X/packages/%40anyfork/vuepress-plugin-ribbon-nextopen in new window

yarn add -D  @anyfork/vuepress-plugin-ribbon-next
npm i -D @anyfork/vuepress-plugin-ribbon-next
  • 插件配置
import { ribbon } from '@anyfork/vuepress-plugin-ribbon-next'
module.exports = {
  plugins: [
    //彩带
    ribbon({
      size: 120, // 默认数据
      opacity: 0.3, //  透明度
      zIndex: -1, //  层级
      option: {
        // 色带HSL饱和度
        colorSaturation: '80%',
        // 色带HSL亮度量
        colorBrightness: '60%',
        // 带状颜色不透明度
        colorAlpha: 0.65,
        // 在HSL颜色空间中循环显示颜色的速度有多快
        colorCycleSpeed: 6,
        // 从哪一侧开始Y轴 (top|min, middle|center, bottom|max, random)
        verticalPosition: 'max',
        // 到达屏幕另一侧的速度有多快
        horizontalSpeed: 200,
        // 在任何给定时间,屏幕上会保留多少条带
        ribbonCount: 3,
        // 添加笔划以及色带填充颜色
        strokeSize: 0,
        // 通过页面滚动上的因子垂直移动色带
        parallaxAmount: -0.5,
        // 随着时间的推移,为每个功能区添加动画效果
        animateSections: true
      },
      //  点击彩带  true显示  false为不显示
      ribbonShow: false,
      // 滑动彩带
      ribbonAnimationShow: true
    })
  ]
}

15 樱花特效插件AnyFork插件

提示

1、AnyFork插件指博主自己开发适配的插件。

2、AnyFork插件官网地址:https://github.com/AnyFork/vuepress-pulgins/tree/2.X/packages/%40anyforkopen in new window

3、樱花特效插件背景彩带。详情参考:https://github.com/AnyFork/vuepress-pulgins/tree/2.X/packages/%40anyfork/vuepress-plugin-sakura-nextopen in new window

yarn add -D  @anyfork/vuepress-plugin-sakura-next
npm i -D @anyfork/vuepress-plugin-sakura-next
  • 插件配置
import { sakura } from '@anyfork/vuepress-plugin-sakura-next'
module.exports = {
  plugins: [
    //樱花特效
    sakura({
      sakura_zindex: 1,
      sakura_img: '/blog-docs/images/blue.png'
    })
  ]
}

四 项目部署

vuepress项目和普通 vue 都属于单页面应用,项目打包部署方式也一致,可以采用本地部署,也可以采用云服务器进行部署。下面总结 2 种部署方式:

  • 1 本地 nginx 部署静态文件 。
  • 2 通过Gitee Actions实现自动部署到Github PagesGitee Pages

注意

1、如果项目设置了base,不可直接在本地开启一个服务器(eg:http-server)进行部署,需要通过 nginx 服务器进行反向代理。

2、如果项目没有设置base,可以通过本地静态服务器或 nginx 服务器进行代理。

3、base类型: string,默认值: /,部署站点的基础路径,如果你想让你的网站部署到一个子路径下,你将需要设置它。如 Github pages,如果你想将你的网站部署到 https://foo.github.io/bar/,那么 base 应该被设置成 “/bar/”,它的值应当总是以斜杠开始,并以斜杠结束。base 将会自动地作为前缀插入到所有以 / 开始的其他选项的链接中,所以你只需要指定一次。

1 nginx 服务器部署

  • base/时,相应的 nginx 配置十分简单,直接指定端口如下面示例的 8080,并指定 root 路径(即 build 后放在服务器的路径)nginx.conf 配置如下:

    server {
      listen 8080;
      location / {
          root /root/doc/dist;
          try_files $uri $uri/ /index.html;
          index index.html index.htm;
        }
    }
    
  • base/blog-docs时,比如本站,配置的为/blog-docs,配置也很简单,只需要上面的 location 由/改为/blog-docs/,注意最后一个斜杠。nginx.conf 配置如下:

    server {
      listen 80;
      server_name localhost;
      client_max_body_size 20m;
      charset utf-8;
        #配置了转发
      location /blog-docs/ {
        proxy_pass http://localhost:8081/;
      }
    }
    #在8081上起了vuepress
    server {
      listen 8081;
      location / {
          root /root/doc/dist;
          try_files $uri $uri/ /index.html;
          index index.html index.htm;
      }
    }
    

2 部署在Github PagesGitee Pages

 项目采用Github PagesGitee Pages方式进行代理部署,依靠Github强大的工作流机制实现自动编译打包部署。大致部署流程:本地通过 git 提交源代码,Github Actions触发工作流workflows实现分支代码检出,依赖安装,打包编译,创建gh-pages分支,上传dist编译后代码到gh-pagesgh-pages分支部署到``Github Pages,同步Github仓库代码至Giee,Gitee Pages部署。上述流程实现了自动编译部署和github代码同步部署至gitee`。

具体实现方式,请参考:GitHub Actions 自动部署 GitHub Pages 并同步 Gitee Pages

评论
Powered by Waline v2.6.3
卡农
卡农钢琴版