使用Pagefind为VitePress文档添加离线全文搜索能力

简介: 前言VitePress 相信大家都或多或少听说过或者用过了默认 UI相比 VuePress2.x 好看,启动速度也快(由Vite驱动,当然VuePress也可以切换构建引擎至Vite)做内容定制也相对简单,笔者的很多静态文档站点(使用VuePress1.x),文章内容多的时候启动非常的慢,于是就从之前的 VuePress 迁移到了 VitePress,并做了一个博客主题 @sugarat/theme => 之前也有过介绍一个简约风的VitePress博客主题但是 VitePress 官方目前还没有内置开箱即用的搜索能力(相关PR还在施工中)

前言

VitePress 相信大家都或多或少听说过或者用过了

默认 UI相比 VuePress2.x 好看,启动速度也快(由Vite驱动,当然VuePress也可以切换构建引擎至Vite)

做内容定制也相对简单,笔者的很多静态文档站点(使用VuePress1.x),文章内容多的时候启动非常的慢,于是就从之前的 VuePress 迁移到了 VitePress,并做了一个博客主题 @sugarat/theme => 之前也有过介绍一个简约风的VitePress博客主题

但是 VitePress 官方目前还没有内置开箱即用的搜索能力(相关PR还在施工中)

image.png

文档里首推使用 Algolia DocSearch, 这个需要申请,流程相对较慢,公司内网文档也无法接入使用。

推荐的另一个方案是使用 vitepress-plugin-search 基于 flexsearch 实现,但是默认的UI较丑(与 Algolia 的UI差距较大),对中文没有提供开箱即用的支持,需要进行一定的配置

image.png

目前常用的除了 flexsearch,还有 MiniSearch

笔者之前在逛GitHub的时候还发现了一个 Pagefind(基于Rust实现,检索生成后的HTML页面内容,然后自动构建索引文件)

感觉挺有意思的,就研究使用了一番,然后将其做成了一个可直接使用的 VItePress 插件(也就是本文将介绍的)

本文主要演示下 接入步骤和效果,再简单介绍Pagefind,最后讲解一下插件原理

接入后效果

只需要2步即可完成接入

① 安装 vitepress-plugin-pagefind

npm i vitepress-plugin-pagefind
# or
yarn add vitepress-plugin-pagefind
# or
pnpm add vitepress-plugin-pagefind

② 在.vitepress/config.ts引入使用

import { defineConfig } from 'vitepress'
import { pagefindPlugin } from 'vitepress-plugin-pagefind'
export default defineConfig({
  vite:{
    plugins:[pagefindPlugin()],
  }
})

UI如下(power by vue-command-palette)

image.png

Pagefind介绍

Pagefind是一个完全静态的搜索库,旨在在大型网站上表现良好,同时尽可能地减少用户带宽的使用,且不需要进行任何基础设施托管。

Pagefind在Hugo,Eleventy,Jekyll,Next,Astro,SvelteKit或任何其他SSG之后运行。安装过程始终相同:Pagefind仅需要一个包含构建的静态文件的文件夹,因此在大多数情况下,无需进行配置即可开始使用。

索引后,Pagefind会向您的构建文件添加静态搜索包,该包公开了可以在站点任何位置使用的JavaScript搜索API。Pagefind还提供了一个可无需配置即可使用的预构建UI

总结:框架无关,直接解析静态站点的产物,然后生成索引文件,提供开箱即用的API和组件,支持开箱即用的多语言站点,零配置

生成内容索引

可直接通过npx调用,指定构建后的产物目录即可

以 vitepress 默认产物目录为例

npx pagefind --source docs/.vitepress/dist

一般毫秒级就完成了页面内容的分析与pagefind需要的资源转换

image.png

默认会自动扫描指定目录下所有的html资源,将带有data-pagefind-body属性的元素作为索引的位置,否则的话使用<body>作为索引位置

会自动识别 html 中的 lang 属性,使用对应的分词策略处理,目前已经内置多种语言支持

生成的相关文件默认在_pagefind目录中,内容如下

image.png

使用内置搜索UI

在生成索引的过程中,pagefind也会把内置的搜索框UI相关资源放入其中

在页面中只需要导入相应的js/css资源即可

<link href="/_pagefind/pagefind-ui.css" rel="stylesheet">
<script src="/_pagefind/pagefind-ui.js" type="text/javascript"></script>
<div id="search"></div>
<script>
    window.addEventListener('DOMContentLoaded', (event) => {
        new PagefindUI({ element: "#search" });
    });
</script>

搜索框样式如下

image.png

使用JS API

默认的搜索框样式不满足的话可以,自定义搜索框逻辑,通过JS API调用搜索能力

// 1. Initializing Pagefind
const pagefind = await import("/_pagefind/pagefind.js");
// 2. search docs
const search = await pagefind.search("hello");
// 3. Loading a result
const oneResult = await search.results[0].data();

搜索结果格式如下

image.png

interface SearchResult {
  url: string;
  excerpt: string;
  filters: Record<string,any>;
  meta: Record<string,any>;
  content: string;
  word_count: number;
}

一些不足

  • 仅针对构建后的产物进行索引,开发环境下无法工作
  • 对中文和日语等支持相对英语会差一点(区别见下截图)

image.png

  • 不支跳转至标题

插件实现原理解析

这部分主要介绍 vitepress-plugin-pagefind 的关键实现部分(细节可看源码,如读者有更好的实现思路可以评论区交流)

几个关键步骤:

  1. 替换默认搜索组件
  2. 目标元素上插入检索的标识data-pagefind-body
  3. 插入运行时的script脚本

替换默认搜索组件

通过查看默认布局组件Layout.vue源码其中搜索组件是被VPNavBarSearch.vue引入

咱只需要通过插件添加一个 alias 规则,将其指向自定义的组件即可

这个使用插件的 config 钩子即可

export function pagefindPlugin() {
  return {
    name: 'vitepress-plugin-pagefind',
    enforce: 'pre',
    config: () => ({
      resolve: {
        alias: {
          './VPNavBarSearch.vue': 'vitepress-plugin-pagefind/Search.vue'
        }
      }
    }),
  }
}

添加索引标识

由于vitepress的默认 body元素中包含 navBar,footer,sidebar等等等内容

默认情况下每个页面的代码中都会包含这些公共内容,因此会导致检索出的内容有很多重复信息

理论上只需要检索用户编写的 markdown内容生成的部分

也就是VPContent.vue组件渲染的内容(当然里面包含了3种情况VPHomeVPPageVPDoc这里可以不做区分)

这个通过插件的transform钩子处理一下,构建时替换源码中VPContent的内容

export function pagefindPlugin() {
  return {
    // ... other code
    transform(code, id) {
      if (id.endsWith('theme-default/Layout.vue')) {
        return code.replace('<VPContent>', '<VPContent data-pagefind-body>')
      }
      return code
    }    
  }
}

运行时的脚本注入

因为相关资源是在Build之后才会生成,所以直接在源码中 import 会提示 module not found 导致构建失败

咱可以在搜索组件里写一段脚本,在页面运行后append到页面中,这段逻辑可以写到onBeforeMount周期函数中

import { onBeforeMount } from 'vue'
const addInlineScript = () => {
  const scriptText = `import('/_pagefind/pagefind.js')
        .then((module) => {
          window.__pagefind__ = module
        })
        .catch(() => {
          console.log('not load /_pagefind/pagefind.js')
        })`
  const inlineScript = document.createElement('script')
  inlineScript.innerHTML = scriptText
  document.head.appendChild(inlineScript)
}
onBeforeMount(() => {
  addInlineScript()
})

pagefind我这里采用import(source)动态导入,组件搜索直接使用window.__pagefind__来进行API的调用

最后

目前这一版插件主要是将pagefind做了一个简单的内置,没有对搜索结果进行优化,也不支持多级标题的跳转

后续是准备优化一下插件的本身实现和功能

  • 插件内部的hack实现替换为 VitePress 的 Build Hooks
  • 搜索内容的输入输出支持自定义的转换
  • 跳转支持标题

相关文章
|
1月前
|
存储 自然语言处理 BI
|
1月前
|
存储 安全 前端开发
Elasticsearch 使用误区之六——富文本内容写入前不清洗
【10月更文挑战第6天】在大数据和全文搜索领域,Elasticsearch(简称ES)凭借其强大的搜索和分析能力,成为众多企业和开发者的首选工具。然而,在实际应用中,很多开发者在使用ES时存在一些误区,其中之一便是富文本内容写入前不进行清洗。本文将深入探讨这一误区,并提供一些实用的清洗策略和最佳实践。
59 3
|
新零售 自然语言处理 运维
一文详解 | 开放搜索兼容Elasticsearch做召回引擎
开放搜索发布开源兼容版,支持阿里云Elasticsearch做搜索召回引擎,本文详细介绍阿里云ES用户如何通过接入开放搜索兼容版丰富行业分词库,提升查询语义理解能力,无需开发、算法投入,即可获得淘系同款搜索效果。
1530 0
|
6天前
|
JavaScript 搜索推荐 前端开发
DevDocs具备**一站式搜索、多语言支持、离线访问等**特色功能。
DevDocs具备**一站式搜索、多语言支持、离线访问等**特色功能。
81 56
|
1月前
|
存储 自然语言处理 搜索推荐
SpringBoot 搜索引擎 海量数据 Elasticsearch-7 es上手指南 毫秒级查询 包括 版本选型、操作内容、结果截图(二)
SpringBoot 搜索引擎 海量数据 Elasticsearch-7 es上手指南 毫秒级查询 包括 版本选型、操作内容、结果截图(二)
34 0
|
1月前
|
自然语言处理 搜索推荐 Java
SpringBoot 搜索引擎 海量数据 Elasticsearch-7 es上手指南 毫秒级查询 包括 版本选型、操作内容、结果截图(一)
SpringBoot 搜索引擎 海量数据 Elasticsearch-7 es上手指南 毫秒级查询 包括 版本选型、操作内容、结果截图
49 0
|
2月前
|
JSON 自然语言处理 算法
ElasticSearch基础2——DSL查询文档,黑马旅游项目查询功能
DSL查询文档、RestClient查询文档、全文检索查询、精准查询、复合查询、地理坐标查询、分页、排序、高亮、黑马旅游案例
ElasticSearch基础2——DSL查询文档,黑马旅游项目查询功能
|
2月前
|
存储 自然语言处理 关系型数据库
ElasticSearch基础3——聚合、补全、集群。黑马旅游检索高亮+自定义分词器+自动补全+前后端消息同步
聚合、补全、RabbitMQ消息同步、集群、脑裂问题、集群分布式存储、黑马旅游实现过滤和搜索补全功能
ElasticSearch基础3——聚合、补全、集群。黑马旅游检索高亮+自定义分词器+自动补全+前后端消息同步
|
前端开发 JavaScript Java
这是一款面向开发者的离线文档查看工具 非常好用!
你要知道的一款面向开发者的离线文档查看工具
158 0
这是一款面向开发者的离线文档查看工具 非常好用!
|
JavaScript 前端开发 Java
40jqGrid 实时数据处理- 搜索数据
40jqGrid 实时数据处理- 搜索数据
35 0