Vue 3 实战——搜索框检索高亮的优雅实现

下面整理一篇关于 Vue 3 中实现搜索框检索高亮的文章,涵盖思路、实现步骤、完整代码示例及常用优化方案。


前言

在日常后台系统或内容型产品中,搜索关键词高亮 是一项非常常见的需求。用户在搜索框输入文字后,列表中匹配的内容需要将关键词部分以醒目的样式(如黄色背景)标记出来,帮助用户快速定位。

本文将基于 Vue 3 的组合式 API,从零实现一个支持实时搜索关键词高亮的功能组件,并附上可直接使用的代码与优化建议。

效果预览

  • 输入关键词,列表实时过滤出包含关键词的条目
  • 匹配到的文字自动高亮(可自定义样式)
  • 支持多个关键词高亮
  • 支持忽略大小写
  • 组件化、可复用

思路分析

整体实现可以拆分为三个核心点:

  1. 数据过滤:根据输入的关键词,对列表数据进行筛选。
  2. 高亮渲染 :将匹配到的关键词用 <span> 等标签包裹,并通过 CSS 类名添加高亮样式。
  3. 安全性 :避免直接使用 v-html 造成的 XSS 风险(需要对用户输入进行转义,但在纯前端本地搜索场景一般风险可控;若数据来源包含用户生成内容,则必须转义)。

实现的关键函数是:将文本中的关键词替换为带高亮标签的 HTML 字符串 ,然后通过 v-html 渲染。

实现步骤与代码

1. 基础搜索组件结构

我们创建一个 SearchHighlight.vue 组件,包含一个搜索框和一个结果列表。

vue 复制代码
<template>
  <div class="search-container">
    <input
      v-model="keyword"
      type="text"
      placeholder="请输入关键词搜索..."
      class="search-input"
    />
    <ul class="list">
      <li
        v-for="item in filteredList"
        :key="item.id"
        class="list-item"
      >
        <!-- 这里使用 v-html 渲染高亮文本 -->
        <span v-html="highlightText(item.name)"></span>
      </li>
    </ul>
    <div v-if="!filteredList.length" class="empty">无匹配结果</div>
  </div>
</template>

2. 编写组合式逻辑

vue 复制代码
<script setup>
import { ref, computed } from 'vue'

// 原始数据(模拟)
const originalList = [
  { id: 1, name: 'Vue 3 组合式 API 实战' },
  { id: 2, name: 'React Hooks 深入浅出' },
  { id: 3, name: 'JavaScript 高级程序设计' },
  { id: 4, name: 'TypeScript 类型体操' },
  { id: 5, name: 'Vue 与 React 对比' },
]

const keyword = ref('')

// 过滤后的列表(忽略大小写)
const filteredList = computed(() => {
  const kw = keyword.value.trim().toLowerCase()
  if (!kw) return originalList
  return originalList.filter((item) =>
    item.name.toLowerCase().includes(kw)
  )
})

/**
 * 高亮文本处理函数
 * @param text 原始文本
 * @returns 包含高亮 span 的 HTML 字符串
 */
function highlightText(text) {
  const kw = keyword.value.trim()
  if (!kw) return text

  // 1. 对特殊正则字符进行转义,防止正则报错
  const escapedKw = escapeRegExp(kw)
  // 2. 创建全局、忽略大小写的正则
  const regex = new RegExp(`(${escapedKw})`, 'gi')
  // 3. 替换为带高亮类的 span
  return text.replace(regex, '<span class="highlight">$1</span>')
}

/**
 * 转义正则特殊字符
 */
function escapeRegExp(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}
</script>

3. 添加样式

vue 复制代码
<style scoped>
.search-container {
  max-width: 500px;
  margin: 20px auto;
  font-family: 'Segoe UI', sans-serif;
}

.search-input {
  width: 100%;
  padding: 12px 16px;
  font-size: 16px;
  border: 1px solid #ccc;
  border-radius: 8px;
  outline: none;
  transition: border 0.2s;
}
.search-input:focus {
  border-color: #4a90e2;
}

.list {
  list-style: none;
  padding: 0;
  margin-top: 16px;
}

.list-item {
  padding: 10px 12px;
  border-bottom: 1px solid #eee;
}

.empty {
  text-align: center;
  color: #999;
  margin-top: 20px;
}

/* 高亮样式 */
:deep(.highlight) {
  background-color: #ffeb3b;
  color: #333;
  font-weight: 600;
  border-radius: 2px;
  padding: 0 2px;
}
</style>

注意:因为高亮的 <span> 是通过 v-html 动态插入的,scoped 样式无法直接作用到动态内容上,所以这里使用了 :deep() 或直接写全局样式(若组件隔离要求不高,可用全局 class)。

4. 完整组件代码

将上述模板、脚本、样式合并,即可得到一个完整的 SearchHighlight.vue 组件。在父组件中直接引入使用即可。

vue 复制代码
<template>
  <SearchHighlight />
</template>

功能扩展与优化

支持多个关键词高亮

有时需求允许用户输入多个关键词(如用空格分隔),每个关键词都高亮。只需调整 highlightText 函数:

javascript 复制代码
function highlightText(text) {
  const keywords = keyword.value.trim().split(/\s+/).filter(Boolean)
  if (!keywords.length) return text

  // 将多个关键词用 | 连接,构建一个正则表达式
  const escaped = keywords.map(escapeRegExp).join('|')
  const regex = new RegExp(`(${escaped})`, 'gi')
  return text.replace(regex, '<span class="highlight">$1</span>')
}

防止 XSS 攻击

如果高亮的文本内容可能包含用户输入的 HTML(例如用户名被恶意注入),直接使用 v-html 会有风险。安全的做法是:

javascript 复制代码
function escapeHtml(str) {
  return str
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#039;')
}

function highlightText(text) {
  const safeText = escapeHtml(text) // 先转义原文
  const kw = escapeHtml(keyword.value.trim()) // 关键词也转义
  if (!kw) return safeText
  const escapedKw = escapeRegExp(kw)
  const regex = new RegExp(`(${escapedKw})`, 'gi')
  return safeText.replace(regex, '<span class="highlight">$1</span>')
}

这样就杜绝了潜在脚本注入的可能。

虚拟滚动优化

当列表数据量很大时,频繁的 DOM 更新可能造成卡顿。可以结合虚拟滚动库(如 vue-virtual-scroller)只渲染可视区域内的节点,大幅提升性能。

防抖搜索

如果过滤逻辑非常复杂(比如前端模糊搜索大数据集),可以对 keyword 的更新做防抖,减少 computed 触发频率:

javascript 复制代码
import { ref, watch } from 'vue'
import { useDebounceFn } from '@vueuse/core' // 或自行实现

const keyword = ref('')
const debouncedKeyword = ref('')

const updateKeyword = useDebounceFn((val) => {
  debouncedKeyword.value = val
}, 300)

watch(keyword, (val) => updateKeyword(val))

// 之后的过滤和高亮都基于 debouncedKeyword 进行计算

总结

通过上述方法,我们实现了一个简洁、实用且安全的 Vue 3 搜索高亮功能。核心在于:

  • 使用 computed 实时过滤数据
  • 使用正则替换生成高亮 HTML,并通过 v-html 渲染
  • 注意转义特殊字符和 XSS 防护
  • 可按需扩展多关键词、虚拟滚动、防抖等功能

这种模式可以轻松封装成通用组件或组合式函数,在各个项目中复用,大幅提升开发效率与用户体验。


------个人观点 · 仅供参考------

相关推荐
_thought1 小时前
踩坑记录:Vue Devtools(Vue2版)在火狐浏览器上,未在控制台显示
前端·javascript·vue.js
pancakenut1 小时前
从盒模型到画布:以mapbox为例
前端·canvas
珎珎啊2 小时前
前端-闭包
前端
军军君012 小时前
数字孪生监控大屏实战模板:交通云实时数据监控平台
前端·javascript·css·vue.js·typescript·前端框架·echarts
DanCheOo2 小时前
从脚本到 CLI 工具:用 Node.js 打造你的第一个 AI 命令行工具
前端·aigc
木斯佳2 小时前
前端八股文面经大全:腾讯PCG前端暑期二战一面·深度解析(2026-04-22)·面经深度解析
前端·面经·实习
十一.3662 小时前
012-014 对state的理解,初始化state,react中的事件绑定
前端·react.js·前端框架
你脸上有BUG2 小时前
SSE库选型+fetch-event-source示例
前端·sse·通知订阅
Never_every992 小时前
8 个高清 4K 视频素材网址!无水印可商用
大数据·前端·音视频·视频