何喜阳博客

记录使用vuepress2重构个人博客的过程

我之前的博客是用一个php程序做的,安全性不是很好,使用起来也不是很舒服。于是在2022年5月份,我决定动手对博客进行重构,重构前我已经确定好我需要的是什么样的。

必须要具备的: markdown书写、Git托管内容、代码高亮、内容标签聚合、支持静态化(兼顾SEO)

最好能支持的: 无需后端、自动发布、内容搜索、支持代码复制、能有单独的页面、文章详情带目录导航。

最初我计划自己写一套markdown的解析逻辑,自己根据各种配置去进行内容关联,在调研的过程中发现了一些或许能满足我需求的程序,比如: hexo dumi gatsby.js vuepress1 vuepress2 vitepress nuxt.js

最终我选择了vuepress2来重构何喜阳博客,我觉得这个是比较好的,能满足功能需求、操作也相对简单。

在我重构时(2022.05)vuepress2仍处于beta版本。

博客功能的简单实现

vuepress2对博客的支持其实没那么好,不像vuepress1有官方插件,目前比较好的是vuepress2贡献者做的vuepress-plugin-blog2插件,但一两个小地方不是很符合我的想法,而且里面的功能我也不是全部需要,于是我参考这个插件的源码自己实现了一个简易版本的供自己使用。主要步骤如下:

以下内容需要有一定的开发基础,仅当做思路分享,如果没有任何经验建议直接使用vuepress-theme-hopeopen in new window这个主题,内置了vuepress-plugin-blog2等众多插件,上手比较简单。

首先我们要创建一个插件open in new window

通过插件API 的 onInitialized 我们能够读取所有页面内容

// 以下内容都是要写到 onInitialized 中,或通过其调用
const tagMap = new Map()
// 依据内容时间进行排序,也可以在这一步过滤掉不符的内容
const articleSort = (a, b) => (a.frontmatter.date > b.frontmatter.date ? 1 : -1)
const articles = app.pages.sort((a, b) => articleSort(a, b))
articles.forEach((item, index) => {
  if (item?.frontmatter?.tag?.length > 0) {
    // 收集标签信息。
    // 由于我没有使用分类,因此只需要标签,收集分类信息也是这个逻辑
    item.frontmatter?.tag?.forEach((tag) => {
      if (!tagMap.has(tag)) {
        tagMap.set(tag, [])
      }
      tagMap.get(tag).push(item)
    })
  }
  // 处理上一页下一页,在主题模板中可以读取到这个数据
  if (index !== 0) {
    const { path, title } = articles[index - 1]
    articles[index].data.prev = { path, title }
  }
  if (index !== articles.length - 1) {
    const { path, title } = articles[index + 1]
    articles[index].data.next = { path, title }
  }
})

通过以上步骤我们就拿到了一个 articles 的数据,这个数据中包含了所有的文章信息,包括标签信息,上一页下一页信息等。 也拿到了一个 tagMap ,这里包含了所有的标签信息,以及对应标签的文章。接下来我们就可以根据这些内容去创建 文章内容列表、标签列表、标签内容列表了,基本上已经满足一个博客的需求了。

// 按照每页10篇内容,通过createPage生成文章列表页面,标签内容列表也是同样的逻辑
const pageSize = Math.ceil(articles.length / 10)
for (let index = 1; index < pageSize; index++) {
  // frontmatter 有些信息是根据主题模板使用的,layout表示要通过什么模板渲染,和在markdown写是一样的
  const frontmatter = {
    layout: 'ArticleList',
    title: `博客内容 - 第${index + 1}`,
    type: 'article',
    page: index + 1,
    description: `博客内容 - 第${index + 1}`,
  }
  app.pages.push(
    await createPage(app, {
      path: `/article/index_${index + 1}.html`.toLowerCase(),
      frontmatter,
    }),
  )
}
app.pages.push(
  await createPage(app, {
    path: `/article/`,
    frontmatter: {
      layout: 'ArticleList',
      title: `博客内容`,
      page: 1,
      description: '博客内容',
    },
  }),
)

通过 createPageopen in new window 方法我们已经成功创建了内容列表。

主题模板中怎么知道渲染哪些数据呢?此时我们还需要通过 app.writeTempopen in new window 把收集到的内容写入临时文件,在页面模板中读取并展示。

// 写入临时文件。articles是上面的文章列表,article.js 是我们起的名字,在主题模板中使用
app.writeTemp('articles.js', `export default ${JSON.stringify(articles)}`)
// 读取临时文件
import articles from '@temp/articles'
// 读取 frontmatter
import { usePageData, usePageFrontmatter } from '@vuepress/client'
const pageFrontmatter = usePageFrontmatter()

接下来就是根据 frontmatter 提供的page页码来决定渲染第x-第y条文章数据了,你也可以在主题模板中自己实现一个页码来切换显示。

我在这里遇到的一点问题是使用 <route-link> 标签切换页码时数据不会变化,可能是我操作不对,目前通过一个折中的方式解决,也在这里写出来,接下来对内容列表做一些改动。

const reload=()=>{
// 此处需要你自己实现,需要把影响数据的逻辑写到reload方法中
}
// 进来直接调用一次
reload()
onMounted(() => {
  window.reload = reload
})
onUnmounted(() => {
  window.reload = null
})

借助 enhanceopen in new window 方法在路由切换时触发 reload 方法来实现数据切换。

// .vuepress/client.(ts/js)
import { defineClientConfig } from '@vuepress/client'

export default defineClientConfig({
  enhance({ app, router, siteData }) {
    router.afterEach(() => {
      // @ts-ignore
      if (!__VUEPRESS_SSR__) {
        //  主动创建的列表页,在列表页切换页码时,数据渲染有问题,暂时通过这个事件来解决
        window['reload'] && window['reload']()
      }
    })
  },
})









 




到这里,博客功能就已经实现了。借助vuepress2已经自动渲染了我们的博客内容,我们还实现了内容列表、内容列表分页,标签列表,上一页下一页等功能。

markdown图片宽高自定义

我在做完博客功能后,要对以前的内容进行整理迁移,同时还需要写几篇新的内容当做作品介绍,但遇到了markdown图片显示不符合预期的问题。 查了一下,markdown的图片是不支持指定宽高的,只能借助模板的样式来固定一个大小,但有些图片需要很宽的显示、有些图片需要很小的显示,如果直接调整图片大小也可以,但会影响到图片放大预览的清晰度,而且后续操作很麻烦。我就通过参数的方式借助JS修改了图片大小

markdown写法,添加?w= &h= &fit= 参数

!['有猫的生活效果图'](./assets/pet-mini-index-screenshot.png?w=200)

在模板里增加逻辑:

const isNumber = (text) => {
   return /^[0-9]*$/.test(text)
 }
 const imgsSetW = () => {
   const imgs = document.querySelectorAll('img')
   imgs.forEach((img) => {
     const src = img.getAttribute('src')
     const query = new URLSearchParams(src.split('?')[1])
     const w = query.get('w')
     const h = query.get('h')
     const fit = query.get('fit')
     // 修改元素的style
     img.style.width = isNumber(w) ? `${w}px` : w
     img.style.height = isNumber(h) ? `${h}px` : h
     img.style.objectFit = fit
     img.style.maxWidth = '100%'
     img.style.maxHeight = '100%'
   })
 }
 onMounted(() => {
   imgsSetW()
 })

同时为了避免图片有较大幅度的宽高变化引起视觉不适,还通过CSS给带参数的图片限定了初始大小:

img {
  &[src*='w='],
  &[src*='fit='] {
    max-width: 200px;
  }
  &[src*='h='],
  &[src*='fit='] {
    max-height: 200px;
  }
}

这就是markdown自定义图片大小功能的实现了,可能不是特别好,如果什么好的方案也请分享给我。

草稿功能实现

草稿功能最初打算使用Frontmatter配置类似 status: draft ,再通过插件API来过滤掉。操作过程中发现通过vuepress2的配置项就能很好的解决草稿功能,

export default defineUserConfig({
 //...其他配置项
  pagePatterns: ['**/*.md', '!**/_*.md','!node_modules'],
})


 

指定所有以 _下划线开头的内容 和 node_modules 的内容不参与渲染,我不需要被渲染的内容暂时通过_开头就行了。

其他

这篇内容主要还是记录我使用vuepress2重构了博客,顺带说了一些我遇到的问题以及解决方案,还有一些内容没写进来,也还有一些功能要做,后续我会再单独分享,博客都已经重构完成了,接下来也要多写内容了。

以上就是全部内容了。如果你觉得内容有帮助,可以扫码打赏,请我吃一顿饭。

微信支付宝

当前页面地址:https://hexiyang.cn/article/refactoring-my-blog-with-vuepress2.html

内容基于 《自由转载-非商用-非衍生-保持署名》 协议发布,允许非商用转载,需要保留作者姓名和当前页面链接。