2026.06.11 (四)

✨ GPT-5.5 摘要  

记录一次搜索结果改造:不再用 JavaScript 字符串单独拼卡片,而是改用共享 Liquid 归档卡片,让日期、分类、浏览量和搜索覆盖层布局都跟普通列表使用同一套规则。

搜索结果看起来太旧。

首页和分类页里的文章卡片已经比较统一。标题下面有日期,有分类徽章,浏览量也在同一行元信息里。

但一打开搜索,就会出现另一套旧 UI。

它只显示标题和摘要。看不出文章是什么时候写的,也没有分类和浏览量。同一篇文章在普通列表里有上下文,在搜索结果里却少了这些信息。

原因是搜索结果还在用单独的 JavaScript HTML 字符串渲染。

JavaScript 字符串渲染让搜索结果显得过时

搜索本身仍然由 Lunr 负责。

静态 Jekyll 博客在构建时生成搜索 store,再由客户端 JavaScript 在浏览器里查询,这是合理的。

问题是结果卡片也由 JavaScript 直接生成。

普通列表由 _includes/archive-single.html 渲染,搜索结果则由 assets/js/lunr/lunr-en.js 拼接字符串。

结构大概是这样:

Regular listing
-> Liquid include
-> archive__item
-> date, category, views

Search results
-> Lunr JS
-> hand-built HTML string
-> title and excerpt only

这就是问题所在。

普通列表加上分类徽章,搜索结果不会跟着变。日期显示规则改了,搜索结果也不会跟着变。浏览量元信息调整了,搜索结果还是原样。

同一个文章卡片被维护了两份。

把文章卡片拆成共享 include

我没有继续扩大搜索 JavaScript,而是让它少生成 HTML。

新增了共享卡片 include。

_includes/archive-item-card.html
_includes/archive-item-meta-row.html
_includes/archive-item-categories.html

archive-item-card.html 接收一个文档,输出和现有归档列表相同的结构。

list__item
archive__item
archive__item-title
archive__item-meta-row
archive__item-excerpt

archive-item-meta-row.html 把日期、浏览量和分类放在同一行。日期和浏览量继续复用 page__meta.html,分类由 archive-item-categories.html 处理。

原来的 _includes/archive-single.html 则变成薄 wrapper。

{% include archive-item-card.html document=post type=include.type context="archive" %}

普通列表和搜索结果现在使用同一个 include 生成的卡片 HTML。

把完整卡片 HTML 放进 Lunr store

Lunr 仍然负责搜索。

但搜索结果 HTML 不再由 JavaScript 生成。

Jekyll 构建时,assets/js/lunr/lunr-store.js 会为每个文档渲染卡片 include,并把结果放进 html 字段。

title
excerpt
categories
tags
lang
locale
html
url
teaser

搜索 JavaScript 现在只取出 entry.html 并插入页面。

以前搜索 JS 里混着标题链接、teaser 图片、摘要截断和 HTML 字符串拼接。

var renderSearchResult = function (entry) {
  return entry.html || '';
};

搜索 JS 剩下的职责是搜索、locale 过滤、结果数量显示和结果插入。

搜索结果浏览量只显示,不集计

搜索结果里要不要显示浏览量。

最初我考虑过不显示。搜索结果可能变得太吵,而且动态插入 DOM 后还要处理浏览量填充时机。

但如果要和普通列表一致,只缺浏览量也很奇怪。

所以搜索结果也显示浏览量,但显示和集计分开。

Search result exposure is not a visit.
The view count in search results is display-only.
Actual visit tracking belongs only to the currently opened page.

这个博客的浏览量元素带有 data-page-view-path。真正用于集计的是当前页面里带 data-page-view-track="true" 的元素。

搜索结果卡片只有 data-page-view-path,没有 data-page-view-track="true"

所以文章出现在搜索结果里,不会增加那篇文章的浏览量。

它只显示数字。

动态搜索结果也重新应用浏览量

搜索结果在页面首次加载时并不存在于 DOM 中。

用户输入搜索词后,结果才会插入。如果浏览量脚本只在页面加载时收集一次元素,搜索结果中的浏览量就不会被填上。

所以我也修改了 assets/js/custom/visitor-stats.js

它不再在文件顶部固定 document.querySelectorAll("[data-page-view-path]"),而是在每次渲染浏览量时重新查找元素。

同时缓存 analytics payload。

latestAnalyticsPayload

搜索结果渲染后,搜索脚本会发出 hyuk:search-results-rendered 事件。

visitor stats 监听这个事件;如果已有 payload,就把浏览量重新应用到新的 DOM。

它不会再次调用 API。

如果每次搜索输入变化都请求浏览量 API,搜索 UI 就会干扰浏览量系统。搜索结果变化很频繁,网络请求不应该跟着每个按键增加。

现在流程是:

Page load
-> receive analytics payload once
-> render views for existing listing/current page

Search results render
-> dispatch event
-> reuse cached payload for newly inserted DOM

这样搜索结果不会污染计数器。

多语言搜索元信息也按 locale 对齐

完成多语言博客工作后,站点有 /en//ja//zh-Hans/ 等 locale 页面和 collection。搜索也不能混用 locale。

搜索 store 放入 langlocale,卡片 HTML 也根据文档 locale 输出元信息标签。英文页面显示 Views,日文页面显示 閲覧数,简体中文页面显示 浏览量

分类也一样。

locale prefix 本身不能显示成分类徽章,所以 archive-item-categories.html 会跳过 locale segment,只显示真实分类。

恢复搜索覆盖层的侧边栏和更宽结果区域

只同步卡片还不够。

打开搜索时,结果区域仍然很窄。虽然卡片 HTML 和普通列表一样,但右侧空间没有被使用。

还有一个更大的问题:打开搜索后,侧边分类也消失了。

搜索覆盖层不是覆盖在现有正文上。打开搜索时,.initial-content 被隐藏,另一个 #site-search 区域被显示。

因此普通页面里的 sidebar 也一起消失。即使用同一张卡片,如果外部布局不同,搜索页面还是会像另一套界面。

所以我在 _includes/search/search_form.html 里也加入 sidebar。

<div class="search-content__inner-wrap">
  {% include sidebar.html %}
  <div class="archive search-content__archive">
    ...
  </div>
</div>

结果区域也包在 archive search-content__archive 里,让搜索结果在 archive 布局中流动。

然后去掉了缩窄卡片的 CSS。

Minimal Mistakes 的搜索 CSS 里有这样的规则:

.search-content .archive__item {
  @include breakpoint($large) {
    width: 75%;
  }

  @include breakpoint($x-large) {
    width: 50%;
  }
}

搜索结果显旧不只是卡片 HTML 的问题。在大屏上,结果卡片本身被压到一半宽度。

自定义 SCSS 中让搜索结果卡片重新使用全宽。

.search-content .archive__item {
  width: 100%;
}

还有一个布局问题。

普通 archive 会为右侧 sidebar 保留 padding-inline-end。搜索覆盖层没有右侧 sidebar。保留这个 padding 会让搜索结果再次变窄。

所以只在搜索覆盖层的 archive 上取消右侧 padding。

.search-content .search-content__archive {
  padding-inline-end: 0;
}

卡片渲染规则和普通列表共享,覆盖层布局则按搜索界面调整。

检查桌面和移动宽度

运行了构建和语法检查。

bundle exec jekyll build
node --check _site/assets/js/lunr/lunr-store.js
node --check _site/assets/js/lunr/lunr-en.js
node --check _site/assets/js/custom/visitor-stats.js

生成的搜索 store 包含 page__viewsdata-page-view-path

搜索卡片 HTML 中没有 data-page-view-track="true",避免搜索曝光和访问集计混在一起。

之后在各 locale 页面打开搜索,输入了这些查询:

References
Keymory
Daily review

结果卡片在同一行显示日期、分类和浏览量,和普通列表一致。标签也按 locale 显示,例如 Views閲覧数浏览量

搜索结果中没有 data-page-view-track="true"。实际文章页里,当前页面用的 tracking element 仍然只有一个。

搜索覆盖层也重新包含 sidebar。桌面 1280px 宽度下,sidebar 有 22 个项目,搜索结果卡片宽度约为 974px。

移动端 390px 宽度下,sidebar 堆叠在上方,结果卡片用 345px 宽度显示标题、元信息行和摘要。

浏览器控制台没有错误。

留下评论