开发一个美观的 VitePress 图片预览插件

前言

笔者维护的 VitePress 博客主题已经集成了非常多的功能,为便于在主题之外复用,因此有计划将其一部分功能分离出来,形成独立的插件。

现在又有AI加持,再已经有通用插件模板前提下,使用AI就能完成95%的插件工作量!

分离的 图片预览插件,效果如下:

组件样式实现参考了 Element Plus Image Viewer

接下来先简单介绍一下用法,再快速讲解核心原理。

插件开发基于之前创建的一个通用模板,vitepress-plugin-slot-inject-template,在模板的基础上,插件95%的代码由 Gemini 3.0 生成。

如何使用

只需要 2 步:

  1. 安装插件
sh 复制代码
pnpm add vitepress-plugin-image-preview
  1. 配置插件

引入插件在 .vitepress/config.mts VitePress 配置文件中

ts 复制代码
import { defineConfig } from 'vitepress'
import { ImagePreviewPlugin } from 'vitepress-plugin-image-preview'

export default defineConfig({
  vite: {
    plugins: [
      ImagePreviewPlugin()
    ]
  }
})

实现原理

这里只阐述关键点,细节与之前的公告插件类似,这里不做赘述。

VitePress 默认主题 Layout.vue 组件预设的一些插槽,只需将实现自定义组件注入到对应插槽为止即可。

所有的 slots 在 VitePress 文档里也有介绍

注入自定义组件

利用插件的 transform 钩子,将我们的 <ImagePreview /> 组件插入到 Layout.vue 的特定插槽位置

图片预览组件我这里使用的是 doc-before 和 page-top 两个插槽。

使用 alias 保证引入组件的路径正确映射。

ts 复制代码
// 仅包含关键代码
const componentName = 'ImagePreview'
const componentFile = `${componentName}.vue`
const aliasComponentFile = `${getDirname()}/components/${componentFile}`
function ImagePreviewPlugin(options = {}) {
  return {
    // 添加alias
    config: () => {
      return {
        resolve: {
          alias: {
            [`./${componentFile}`]: aliasComponentFile
          }
        }
      }
    },
    transform(code, id) {
      // 筛选出 Layout.vue
      if (id.endsWith('vitepress/dist/client/theme-default/Layout.vue')) {
        let transformResult = code

        // 插入组件
        const slots = [options.slots || ['doc-before', 'page-top']].flat()
        for (const slot of slots) {
          const slotPosition = `<slot name="${slot}" />`
          // 添加 ClientOnly 目的是避免组件在SSG的时候被渲染
          transformResult = transformResult.replace(slotPosition, `${slotPosition}\n<ClientOnly><${componentName} /></ClientOnly>`)
        }

        // 导入组件
        const setupPosition = '<script setup lang="ts">'
        transformResult = transformResult.replace(setupPosition, `${setupPosition}\nimport ${componentName} from './${componentFile}'`)
        return transformResult
      }
    },
  }
}

插件配置传递

采用虚拟模块的方式传递配置。

组件中导入配置:

ts 复制代码
import options from 'virtual:image-preview-options'

插件中处理虚拟模块:

ts 复制代码
const virtualModuleId = 'virtual:image-preview-options'
const resolvedVirtualModuleId = `\0${virtualModuleId}`
function ImagePreviewPlugin(options = {}) {
  return {
    // 省略其它无关代码...
    resolveId(id) {
      if (id === virtualModuleId) {
        return resolvedVirtualModuleId
      }
    },
    load(this, id) {
      if (id === resolvedVirtualModuleId) {
        return `export default ${stringify(options)}`
      }
    },
  }
}

核心交互实现

图片预览的核心逻辑在于监听图片的点击事件,获取图片列表,并显示预览遮罩。

  1. 事件监听 :在 onMounted时,给内容的容器注册点击事件,在点击的时候获取容器中所有的图片元素,然后做后续操作。
ts 复制代码
onMounted(() => {
  const wrapperId = imagePreviewOptions?.wrapperId || '#VPContent'
  const docDomContainer = document.querySelector(wrapperId)
  docDomContainer?.addEventListener('click', previewImage)
})

function previewImage(e: Event) {
  const target = e.target as HTMLElement
  const currentTarget = e.currentTarget as HTMLElement
  if (target.tagName.toLowerCase() === 'img') {
    const selector = imagePreviewOptions?.selector || '.content-container .main img,.VPPage img'
    const imgs = currentTarget.querySelectorAll<HTMLImageElement>(selector)
    const idx = Array.from(imgs).findIndex(el => el === target)
    const urls = Array.from(imgs).map(el => el.src)
    // 省略其它逻辑
  }
}
  1. 预览组件 :参考了 Element Plus 的 图片预览组件的样式与功能,这部分完全由 AI 实现(Gemini 3.0),还原度非常高。

插件模板介绍

在开发插件的过程中,笔者把此类基于 slot 位置注入的插件分离了一个模板 vitepress-plugin-slot-inject-template

有相关诉求的朋友,可以基于此模板,配合 AI 快速的开发各种基于插槽就可以实现的组件能力。

最后

插件完整源码 vitepress-plugin-image-preview

最后再感叹一句,AI 太牛逼了,效率起飞。

欢迎评论区交流&指导。

相关推荐
m0_敲代码的彭于晏1683 小时前
Vercel 前端部署平台深度评测
前端
2301_32241428043 小时前
基于SpringBoot+Vue的休闲零食超市管理系统的设计与实现59004-计算机毕设 原创(免费领源码+部署教程+开发环境)
java·vue.js·spring boot·redis·mysql·决策树·推荐算法
怕浪猫4 小时前
外包和大厂的前端代码,差在凌晨三点
前端·面试·github
m0_敲代码的彭于晏1684 小时前
Vercel 前端部署与全栈开发实战指南
前端·状态模式
徐小夕4 小时前
花了3个月,我们做了一款 AI 版的“飞书文档”
前端·算法·github
贾伟康5 小时前
【化学实验室|17】HarmonyOS ArkTS 亮暗色与视觉令牌实战:集中颜色、间距和交互状态避免页面割裂
前端·移动开发·harmonyos·arkts·arkui
Csvn5 小时前
SSR/SSG/ISR(上):峡谷与渲染策略的时间预算
前端
Csvn5 小时前
SSR/SSG/ISR(下):可交互性、新鲜度与回源账
前端
Elcker8 小时前
YnuoAgent(依诺):一个 npm 包,跑起属于自己的 AI 研发平台
前端·人工智能·npm
酷炫码神8 小时前
Day 8|Vue3 响应式:告别手动操作 DOM,让数据驱动界面
javascript·vue.js·ecmascript