高级用法

全文搜索

使用 Nuxt Content 在你的网站中实现全文搜索。

内置 FTS5 搜索

Content 模块提供 useSearchCollection,这是一个由 SQLite FTS5 驱动的零依赖组合式函数。它会基于你的内容章节构建倒排索引,并提供带前缀匹配和摘要的即时排序搜索。

SearchExample.vue
<script setup lang="ts">
const { status, search } = useSearchCollection('docs')
const query = ref('')
const results = ref([])

watch(query, async (value) => {
  results.value = value
    ? await search(value, { snippet: { columns: ['content'], around: 40 } })
    : []
})
</script>

<template>
  <UInput v-model="query" :disabled="status !== 'ready'" placeholder="搜索..." />
  <ul>
    <li v-for="result in results" :key="result.id">
      <NuxtLink :to="result.id">{{ result.title }}</NuxtLink>
      <p v-if="result.snippets?.content" v-html="result.snippets.content" />
    </li>
  </ul>
</template>
阅读更多关于 useSearchCollection 组合式函数的信息。

外部库

你也可以使用 queryCollectionSearchSections 来获取原始章节数据,并将其传递给像 Fuse.jsMiniSearch 这样的搜索库。这种方式可以容忍拼写错误并进行模糊匹配,但代价是需要将所有章节加载到内存中。

MiniSearch

MiniSearch 是一个轻量级的全文搜索库,支持前缀匹配、模糊搜索和字段权重提升。

<script setup lang="ts">
import MiniSearch from 'minisearch'

const query = ref('')
const { data } = await useAsyncData('search', () => queryCollectionSearchSections('docs'))

const miniSearch = new MiniSearch({
  fields: ['title', 'content'],
  storeFields: ['title', 'content'],
  searchOptions: {
    prefix: true,
    fuzzy: 0.2,
  },
})

// 将数据添加到 MiniSearch 实例中
miniSearch.addAll(toValue(data.value))
const result = computed(() => miniSearch.search(toValue(query)))
</script>

<template>
  <UContainer class="p-4">
    <UCard>
      <UInput v-model="query" placeholder="搜索..." />
      <ul>
        <li v-for="link of result" :key="link.id" class="mt-2">
          <NuxtLink :to="link.id">{{ link.title }}</NuxtLink>
          <p class="text-gray-500 text-xs">{{ link.content }}</p>
        </li>
      </ul>
    </UCard>
  </UContainer>
</template>

Fuse.js

Fuse.js 是一个模糊搜索库,使用 Bitap 算法进行可容忍拼写错误的匹配,并支持可配置阈值。

<script setup lang="ts">
import Fuse from 'fuse.js'

const query = ref('')
const { data } = await useAsyncData('search-data', () => queryCollectionSearchSections('docs'))

const fuse = new Fuse(data.value, {
  keys: ['title', 'description']
})

const result = computed(() => fuse.search(toValue(query)).slice(0, 10))
</script>

<template>
  <UContainer class="p-4">
    <UCard>
      <UInput v-model="query" placeholder="搜索..." class="w-full" />
      <ul>
        <li v-for="link of result" :key="link.item.id" class="mt-2">
          <UButton variant="ghost" class="w-full" :to="link.item.id">
            {{ link.item.title }}
            <span class="text-gray-500 text-xs">
              {{ link.item.content?.slice(0, 100) }}...
            </span>
          </UButton>
        </li>
      </ul>
    </UCard>
  </UContainer>
</template>

Nuxt UI

Nuxt UI 提供了一个 ContentSearch 组件,它由 fuse.js 驱动,并可直接接收 queryCollectionSearchSections 的输出。

<script setup lang="ts">
const { data: navigation } = await useAsyncData('navigation', () => queryCollectionNavigation('docs'))
const { data: files } = await useAsyncData('search', () => queryCollectionSearchSections('docs'))

const searchTerm = ref('')
</script>

<template>
  <UContentSearch
    v-model:search-term="searchTerm"
    :files="files"
    :navigation="navigation"
    :fuse="{ resultLimit: 42 }"
  />
</template>