高亮与代码片段 #

高亮概述 #

Algolia自动为搜索结果中的匹配关键词添加高亮标记,帮助用户快速定位相关内容。

高亮效果 #

text
搜索词: "iphone"

结果: <em>iPhone</em> 15 Pro - Apple's latest smartphone

配置高亮 #

设置高亮属性 #

javascript
await index.setSettings({
  attributesToHighlight: ['name', 'description', 'brand']
});

搜索时指定 #

javascript
const results = await index.search('iphone', {
  attributesToHighlight: ['name', 'description']
});

高亮结果结构 #

_highlightResult #

javascript
{
  "objectID": "1",
  "name": "iPhone 15 Pro",
  "description": "Apple iPhone with A17 Pro chip",
  
  "_highlightResult": {
    "name": {
      "value": "<em>iPhone</em> 15 Pro",
      "matchLevel": "full",
      "matchedWords": ["iphone"],
      "fullyHighlighted": false
    },
    "description": {
      "value": "Apple <em>iPhone</em> with A17 Pro chip",
      "matchLevel": "full",
      "matchedWords": ["iphone"],
      "fullyHighlighted": false
    }
  }
}

字段说明 #

字段 说明
value 高亮后的文本
matchLevel 匹配级别
matchedWords 匹配的词列表
fullyHighlighted 是否完全高亮

matchLevel值 #

说明
full 完全匹配
partial 部分匹配
none 无匹配

自定义高亮标签 #

默认标签 #

默认使用 <em> 标签:

html
<em>iPhone</em> 15 Pro

自定义标签 #

javascript
const results = await index.search('iphone', {
  attributesToHighlight: ['name', 'description'],
  highlightPreTag: '<mark>',
  highlightPostTag: '</mark>'
});

// 结果: <mark>iPhone</mark> 15 Pro

使用CSS类 #

javascript
const results = await index.search('iphone', {
  attributesToHighlight: ['name'],
  highlightPreTag: '<span class="highlight">',
  highlightPostTag: '</span>'
});

// 结果: <span class="highlight">iPhone</span> 15 Pro

高亮UI实现 #

基本渲染 #

javascript
function renderHit(hit) {
  const name = hit._highlightResult.name.value;
  
  return `
    <div class="product">
      <h3>${name}</h3>
    </div>
  `;
}

安全渲染 #

javascript
function safeHighlight(highlightResult) {
  // 使用DOM API避免XSS
  const div = document.createElement('div');
  div.innerHTML = highlightResult.value;
  
  // 为高亮标签添加样式
  div.querySelectorAll('em').forEach(el => {
    el.classList.add('highlight');
  });
  
  return div.innerHTML;
}

React组件 #

jsx
function Hit({ hit }) {
  return (
    <div className="hit">
      <h3 
        dangerouslySetInnerHTML={{ 
          __html: hit._highlightResult.name.value 
        }} 
      />
      <p 
        dangerouslySetInnerHTML={{ 
          __html: hit._highlightResult.description?.value 
        }} 
      />
    </div>
  );
}

Vue组件 #

vue
<template>
  <div class="hit">
    <h3 v-html="hit._highlightResult.name.value"></h3>
    <p v-html="hit._highlightResult.description?.value"></p>
  </div>
</template>

代码片段 #

什么是代码片段? #

代码片段(Snippet)是截取匹配词周围的一段文本,用于展示上下文。

text
原文: "The new iPhone 15 Pro features the powerful A17 Pro chip, 
       delivering unprecedented performance for mobile gaming and 
       professional video editing."

搜索: "chip"

片段: "...features the powerful A17 Pro <em>chip</em>, delivering..."

配置代码片段 #

javascript
await index.setSettings({
  attributesToSnippet: ['description:50', 'content:100']
});

搜索时指定 #

javascript
const results = await index.search('chip', {
  attributesToSnippet: ['description:50']
});

片段结果结构 #

javascript
{
  "_snippetResult": {
    "description": {
      "value": "...powerful A17 Pro <em>chip</em>, delivering unprecedented...",
      "matchLevel": "full"
    }
  }
}

片段长度 #

javascript
// 数字表示返回的最大字符数
attributesToSnippet: ['description:50']  // 约50字符
attributesToSnippet: ['content:200']     // 约200字符

自定义省略号 #

默认省略号 #

默认使用 作为省略号。

自定义省略号 #

javascript
const results = await index.search('chip', {
  attributesToSnippet: ['description:50'],
  snippetEllipsisText: '…'
});

高亮与片段组合 #

同时使用 #

javascript
const results = await index.search('iphone', {
  attributesToHighlight: ['name'],
  attributesToSnippet: ['description:100']
});

// 渲染
function renderHit(hit) {
  const name = hit._highlightResult.name.value;
  const description = hit._snippetResult?.description?.value || hit.description;
  
  return `
    <div class="hit">
      <h3>${name}</h3>
      <p>${description}</p>
    </div>
  `;
}

优先级 #

javascript
function getDisplayValue(hit, attribute) {
  // 优先使用片段,其次高亮,最后原始值
  if (hit._snippetResult?.[attribute]) {
    return hit._snippetResult[attribute].value;
  }
  if (hit._highlightResult?.[attribute]) {
    return hit._highlightResult[attribute].value;
  }
  return hit[attribute];
}

高级用法 #

多属性高亮 #

javascript
const results = await index.search('apple', {
  attributesToHighlight: ['name', 'brand', 'description']
});

// 检查哪个属性匹配
function getMatchedAttributes(hit) {
  const matched = [];
  
  for (const [attr, result] of Object.entries(hit._highlightResult)) {
    if (result.matchLevel !== 'none') {
      matched.push(attr);
    }
  }
  
  return matched;
}

嵌套属性高亮 #

javascript
// 数据结构
{
  "name": "iPhone",
  "specs": {
    "display": "6.1 inch",
    "chip": "A17 Pro"
  }
}

// 高亮嵌套属性
const results = await index.search('chip', {
  attributesToHighlight: ['specs.chip']
});

// 结果
{
  "_highlightResult": {
    "specs": {
      "chip": {
        "value": "A17 <em>Pro</em>"
      }
    }
  }
}

数组属性高亮 #

javascript
// 数据结构
{
  "tags": ["phone", "apple", "5g"]
}

// 高亮数组
const results = await index.search('apple', {
  attributesToHighlight: ['tags']
});

// 结果
{
  "_highlightResult": {
    "tags": [
      { "value": "phone", "matchLevel": "none" },
      { "value": "<em>apple</em>", "matchLevel": "full" },
      { "value": "5g", "matchLevel": "none" }
    ]
  }
}

高亮样式 #

CSS样式 #

css
/* 默认em标签 */
em {
  background-color: #fff3cd;
  color: #856404;
  padding: 0 2px;
  border-radius: 2px;
  font-style: normal;
}

/* 自定义类 */
.highlight {
  background-color: #ffeb3b;
  font-weight: bold;
}

/* 深色主题 */
.dark-theme em {
  background-color: #5468ff;
  color: white;
}

动画效果 #

css
em {
  animation: highlight-fade 2s ease-out;
}

@keyframes highlight-fade {
  0% {
    background-color: #ffeb3b;
  }
  100% {
    background-color: transparent;
  }
}

高亮工具函数 #

高亮处理类 #

javascript
class HighlightProcessor {
  constructor(options = {}) {
    this.preTag = options.preTag || '<em>';
    this.postTag = options.postTag || '</em>';
  }
  
  process(hit, attribute) {
    const highlight = hit._highlightResult?.[attribute];
    const snippet = hit._snippetResult?.[attribute];
    
    if (snippet && snippet.matchLevel !== 'none') {
      return this.wrap(snippet.value);
    }
    
    if (highlight && highlight.matchLevel !== 'none') {
      return this.wrap(highlight.value);
    }
    
    return hit[attribute];
  }
  
  wrap(value) {
    // 确保高亮标签正确
    return value;
  }
  
  stripTags(value) {
    return value
      .replace(new RegExp(this.preTag, 'g'), '')
      .replace(new RegExp(this.postTag, 'g'), '');
  }
  
  getMatchedWords(hit, attribute) {
    return hit._highlightResult?.[attribute]?.matchedWords || [];
  }
  
  isFullyHighlighted(hit, attribute) {
    return hit._highlightResult?.[attribute]?.fullyHighlighted || false;
  }
}

使用示例 #

javascript
const processor = new HighlightProcessor({
  preTag: '<mark>',
  postTag: '</mark>'
});

const displayValue = processor.process(hit, 'name');
const matchedWords = processor.getMatchedWords(hit, 'name');

完整示例 #

搜索结果组件 #

javascript
class SearchResultRenderer {
  constructor(container) {
    this.container = container;
  }
  
  render(results) {
    this.container.innerHTML = results.hits
      .map(hit => this.renderHit(hit))
      .join('');
  }
  
  renderHit(hit) {
    return `
      <article class="search-result">
        <h3 class="result-title">
          ${this.getHighlightedValue(hit, 'name')}
        </h3>
        <p class="result-description">
          ${this.getSnippetValue(hit, 'description', 150)}
        </p>
        <div class="result-meta">
          <span class="brand">${hit.brand}</span>
          <span class="price">$${hit.price}</span>
          <span class="rating">★ ${hit.rating}</span>
        </div>
      </article>
    `;
  }
  
  getHighlightedValue(hit, attribute) {
    if (hit._highlightResult?.[attribute]) {
      return hit._highlightResult[attribute].value;
    }
    return hit[attribute] || '';
  }
  
  getSnippetValue(hit, attribute, maxLength = 100) {
    if (hit._snippetResult?.[attribute]) {
      return hit._snippetResult[attribute].value;
    }
    
    const value = hit[attribute] || '';
    if (value.length <= maxLength) {
      return value;
    }
    
    return value.substring(0, maxLength) + '…';
  }
}

总结 #

高亮与代码片段要点:

要点 说明
高亮配置 attributesToHighlight
片段配置 attributesToSnippet
结果结构 _highlightResult, _snippetResult
自定义标签 highlightPreTag, highlightPostTag
省略号 snippetEllipsisText
安全渲染 避免XSS攻击

接下来,让我们学习 同义词管理

最后更新:2026-03-28