- 后端
【免费下载链接】lo
💥 A Lodash-style Go library based on Go 1.18+ Generics (map, filter, contains, find...)
lo是基于 Go 1.18+ 泛型的 Lodash 风格工具库(当前仓库位于GitHub_Trending/lo/lo),其文档站点围绕每个 helper 函数建立了"一份数据文件、一个页面卡片"的自动化体系。本文以仓库根目录 docs/CLAUDE.md 为骨架,结合docs/data/目录下的真实文档文件、Go 源码与校验脚本,完整讲解如何为新增 helper 编写规范、可被站点自动渲染、且与源码保持同步的文档。读完本文,你将掌握 frontmatter 字段的准确含义、variantHelpers/similarHelpers的区分规则、Go Playground 示例的接入流程,以及如何通过仓库自带的校验脚本保证文档质量。
一、文档体系总览:一份数据文件驱动一个页面卡片
lo 库的 helper 文档采用"数据文件 + 渲染组件"分离的架构:
- 数据层:每个 helper 对应
docs/data/目录下一个 Markdown 文件,命名模式为{category}-{slug}.md,例如core-map.md、core-switch.md、mutable-fill.md、parallel-map.md、it-map.md。 - 渲染层:Docusaurus 站点通过自定义插件 docs/plugins/helpers-pages/index.ts 读取这些数据文件,
loadContent()阶段调用readAllHelperMarkdownFiles()解析 frontmatter 并按category → subCategory → position → name排序;随后页面(如 docs/docs/core/slice.md)通过<HelperList category="core" subCategory="slice" />组件,把同组 helper 渲染成一张张 HelperCard(实现见 HelperCard.tsx)。
因此,新增一个 helper 时的标准动作是:在docs/data/下创建/更新对应的 Markdown 数据文件,确保 frontmatter 完整且与源码、文件名严格一致——其余展示逻辑全部由插件自动完成。
二、Frontmatter 格式与字段详解
每个文档文件必须以 YAML frontmatter 开头,标准模板如下(取自 docs/CLAUDE.md):
--- name: HelperName slug: helpername sourceRef: file.go#L123 category: core subCategory: slice signatures: - "func HelperName(params) returnType" - "func (receiver *Type) MethodName(params) returnType" - "func HelperNameI(params) returnType" - "func HelperNameWithContext(params) returnType" playUrl: https://go.dev/play/p/EXAMPLE variantHelpers: - core#slice#helpername - core#slice#helpername2 - core#slice#helpername3 - core#slice#helpername4 - core#slice#helpername5 - core#slice#helpernamei - core#slice#helpernamewithcontext similarHelpers: - mutable#slice#helpername - core#slice#filterhelpername - core#slice#zipx position: 0 ---各字段的准确含义如下:
| 字段 | 说明 | 约束 |
|---|---|---|
name | helper 的显示名称(PascalCase) | 必须与源码中导出的函数名一致 |
slug | URL 友好的名称(kebab-case) | 必须与文件名去掉{category}-前缀后一致 |
sourceRef | 源码文件引用及行号,格式file.go#L123 | 指向该函数在 Go 源码中的声明位置 |
category | 顶层包分类:core、mutable、parallel、it等 | 必须与文件名前缀一致 |
subCategory | 功能分类(如condition、map、find、slice等) | 决定 helper 归入站点的哪个页面 |
signatures | 函数签名字符串数组 | 只列本包/本分类的签名,不混入其他子包 |
playUrl | Go Playground 可运行示例 URL | 每个 helper 都应有;无法运行时可留空 |
variantHelpers | 同一 helper 的不同签名/参数版本数组 | 必须至少包含默认 helper 本身 |
similarHelpers | 相关但不同的 helper 数组 | 可为空,见下文双向引用规则 |
position | 在页面列表中的排序位置(0、10、20、30...) | 与源码中函数声明顺序保持一致,按页面重置 |
以仓库中的真实文件 docs/data/core-filter.md 为例:
--- name: Filter slug: filter sourceRef: slice.go#L12 category: core subCategory: slice playUrl: https://go.dev/play/p/Apjg3WeSi7K similarHelpers: - core#slice#reject - core#slice#filtererr - core#slice#filtermap - core#slice#filterreject - core#slice#rejectmap - core#slice#takefilter - parallel#slice#filter - mutable#slice#filter variantHelpers: - core#slice#filter position: 0 signatures: - "func Filter[T any, Slice ~[]T](collection Slice, predicate func(item T, index int) bool) Slice" ---对照 slice.go 中的实现,sourceRef: slice.go#L12恰好指向func Filter(...)的声明行,position: 0对应 Filter 是slice.go中按源码顺序排列的第一个文档化 helper(Map 紧随其后,position: 10,见 docs/data/core-map.md)。
category 与 subCategory 的取值约束
从 docs/plugins/helpers-pages/index.ts 的类型定义可以看出,category目前取值限定为core | mutable | parallel(it在类型注释中暂时被注释掉),subCategory限定为slice、map、channel、string、function、find、condition、intersect、type、tuple、math、retry、error-handling、concurrency、time。新增 helper 时不要随意引入新的分类值,否则会与插件类型定义和页面路由(/docs/{type}/{category})不一致。
三、保持 sourceRef 与源码行号同步
sourceRef(格式file.go#L123)精确指向 Go 源码中的某个函数声明行。任何对.go文件的修改——无论是新增、删除还是重排函数——都会让该文件中所有后续内容的行号整体偏移,导致的不只是被编辑函数,而是同文件内所有已文档化 helper 的sourceRef全部过期。
因此,每当 Go 源码被修改时,必须按以下流程同步:
- 对每个变更过的
.go文件运行gopls symbols <file>,列出全部符号(函数/方法/类型)及其当前行号; - 将每个符号与
docs/data/*.md中记录该文件 helper 的sourceRef字段逐一比对; - 更新所有行号不再匹配的
sourceRef。
值得说明的是,仓库提供了自动化兜底:脚本 docs/scripts/check-function-signatures.js 会遍历全部 Go 文件(自动跳过_test.go与docs/目录),用正则^func\s+Name(?:\(|\[)找到函数真实声明位置,并把expectedSourceRef = ${file}#L${line}与 frontmatter 中的sourceRef对比,不一致时输出[sourceRef-outdated]警告,--check模式下直接以非零码退出。这意味着 sourceRef 的准确性是可由 CI 强制保证的。
四、正文内容结构:三要素模板
frontmatter 之后是正文,标准结构只有三个要素:
- 简要描述:一句话说明该 helper 做什么,要求简洁、不冗长;
- 代码示例:可运行的 Go 代码,演示实际用法;
- 预期输出:以注释形式展示运行结果。
Brief description of what this helper does. Be concise and not too long. ```go result := lo.HelperName(example) // expected result一个 helper 可以有多个示例(例如覆盖边界情况);当多个签名被归入同一份文档时,最好逐个描述。以 [docs/data/core-switch.md](https://link.gitcode.com/i/8ba5e830d2d067b7fccfed1bc93081d8) 为例,它为 Switch 及其 Case/CaseF/Default/DefaultF 五个成员各建了一个 `###` 小节,每个小节都遵循"一句话描述 + 代码 + 预期输出注释"的模板: ```go result := lo.Switch(2).Case(1, "1").Case(2, "2").Default("3") // "2"// CaseF 惰性计算结果 result := lo.Switch(2).CaseF(2, func() string { return "2" }).Default("?") // "2"这段文档对应 condition.go 中Switch、Case、CaseF、Default、DefaultF五个函数的真实实现——它们全部以值接收者(switchCase[T, R])链式调用,且每个 case 分支只在!s.done时求值,这正是"惰性计算"语义的源码依据。源码注释也解释了值接收者的性能动机:允许编译器完整内联整条Switch().Case().Default()链,消除函数调用开销。
五、variantHelpers 与 similarHelpers:一字之差,语义完全不同
这是整份规范中最容易混淆、也最影响文档质量的两个字段。核心区别一句话概括:
- variantHelpers:同一个 helper 函数的不同签名/参数版本(同一包内)——本质是"同一个东西的变体";
- similarHelpers:不同但功能相关的 helper(可以跨包)——本质是"相似但不同的东西"。
variantHelpers 示例:Map 家族
Map的所有变体都归在core#slice#map名下(示例来自 docs/CLAUDE.md,对应源码见 slice.go):
variantHelpers: - core#slice#map # func MapT, R R) []R - core#slice#maperr # func MapErrT, R (R, error)) ([]R, error) - core#slice#mapi # func MapIT, R R) []R (with index) - core#slice#mapwithcontext # func MapWithContextT, R R, context.Context) []R注意 docs/data/core-map.md 的签名数组只写了func MapT any, R any R) []R这一个签名——规范明确要求"不要列出其他子包/分类的签名",变体通过variantHelpers引用而非重复罗列。
similarHelpers 的四种典型用法
- 组合型 helper:
FilterMap同时是 Map 和 Filter 的相似对象:
similarHelpers: - core#slice#map # Related transformation helper - core#slice#filter # Related filtering helper- 跨包等价物:
parallel#slice#map(并发版本)、mutable#slice#map(原地修改版本):
similarHelpers: - parallel#slice#map # Parallel version in different package - mutable#slice#map # Mutable version in different package- 功能相近的独立 helper:围绕搜索场景,
Find的相似对象包括单结果搜索、多结果过滤、按键搜索、带回退值的搜索:
similarHelpers: - core#slice#find # Single result search - core#slice#filter # Multiple result filtering - core#slice#findby # Key-based search - core#slice#findorelse # Search with default value- 同模式家族:
Min/Max及其 By/Index 变体:
similarHelpers: - core#slice#min # Find minimum value - core#slice#max # Find maximum value - core#slice#minby # Find minimum by key function - core#slice#maxby # Find maximum by key function - core#slice#minindex # Find minimum value index - core#slice#maxindex # Find maximum value index双向引用与数值后缀规则
- 双向性:当你为某个新 helper 添加
similarHelpers时,必须同时更新被引用的相似 helper 的文档,把新 helper 加进对方的similarHelpers。这条规则由脚本 docs/scripts/check-cross-references.js 强制校验:它会检查A → B的引用是否被B → A的引用所回应,缺失时输出Cross-ref missing错误。 - 存在性:所有
similarHelpers引用必须指向真实存在的 helper。脚本 docs/scripts/check-similar-exists.js 和 check-similar-keys-exist-in-directory.js 都会对category#subCategory#Name全键做存在性检查。 - 数值后缀:不要链接带数字派生的 helper(例如应使用
core#slice#zipx而不是core#slice#zip2)。
此外,渲染组件 HelperCard.tsx 在展示时会对variantHelpers/similarHelpers做去重(过滤与自身同键的项),并按"同包优先、名称排序"排列;链接格式为/docs/{type}/{category}#{name}。
六、相关 helper 的分组合并原则
当多个 helper 作用于同一个结构体或服务于同一目的时,应合并到单个文件,而不是每个 helper 单独建文件:
- Map 家族:
Map()(基础)、MapI()(谓词回调带 index)、MapWithContext()(谓词回调带 context)、MapIWithContext()(同时带 index 和 context); - Switch 家族:所有方法都操作
switchCase[T, R](源码见 condition.go):Switch()是构造器,Case()/CaseF()是添加分支的方法,Default()/DefaultF()是提供默认值的方法。
合并时的四条操作规范:
- 文件名使用主 helper 的名称(如
core-switch.md); signatures数组中包含所有相关签名;variantHelpers数组中列出所有相关 helper;- 每个 helper 用独立的
### HelperName小节分别文档化。
七、命名约定:分类与函数变体后缀
既定 subCategory 列表
| 分类 | 语义 |
|---|---|
condition | 条件逻辑(if/else、switch) |
map | 变换函数 |
find | 搜索与查找函数 |
slice | 数组操作 |
math | 数学运算 |
string | 字符串操作 |
type | 类型工具 |
error-handling | 错误管理 |
retry | 重试机制 |
time | 时间操作 |
function | 函数工具 |
channel | 通道操作 |
tuple | 元组操作 |
intersect | 集合交集 |
函数变体后缀规范
helper 命名遵循 Go 命名约定(导出函数 PascalCase),并用描述性名称清晰表达用途。函数变体使用统一后缀:
F:函数式版本(惰性求值),如CaseF、DefaultF、TernaryF;I:谓词回调中带index int参数的变体,如MapI、FilterI;Err:谓词回调返回 error 的变体,如MapErr、FilterErr、FindErr;WithContext:提供context.Context的变体,如MapWithContext;X:可变参数数量的 helper(如MustX:Must2、Must3、Must4...)。
从源码可以看到这些后缀在实现层面的对应:例如 slice.go 中FilterErr在谓词返回错误时立即终止迭代并返回错误,这与Filter的纯布尔谓词形成Err后缀的语义差;MapErr(slice.go)同样在首个错误处短路。
八、Go Playground 示例:双位置接入与四步流程
每个 helper 必须有一个可运行的 Go Playground 示例,并接入到两个位置:
- 源码:函数 doc block 的最后一行、
func关键字之前,添加// Play: <url>注释。例如 slice.go 中Map的写法:
// Map manipulates a slice and transforms it to a slice of another type. // Play: https://go.dev/play/p/OkPcYAhBo0D func MapT, R any R) []R {- 文档文件:
docs/data/{category}-{slug}.md的 frontmatter 中设置playUrl字段。
当前仓库的slice.go中约有 68 处// Play:注释,可见该约定已在整个核心包中严格落实。
创建 Playground 示例的四步流程
Step 1:编写示例代码。写一个最小、自包含的main.go,遵循以下准则:
- 使用真实但简单的数据;
- 用
fmt.Println打印结果,保证输出可见; - 必要时包含边界情况(空输入、错误场景);
Err变体同时展示成功与失败两种情形;- 时间类 helper 使用
time.Date()保证输出确定性; - 随机类 helper(
SampleBy、SamplesBy)使用rand.New(rand.NewSource(42))保证输出可复现。
Step 2:按包选择正确的导入路径。
// Core helpers import "github.com/samber/lo" // Usage: lo.Map(...) // Iterator helpers (it/ package, requires Go 1.23+) import ( "slices" "github.com/samber/lo/it" ) // Usage: slices.Collect(it.Map(...)) // Convert slices to iterators: slices.Values([]int{1, 2, 3}) // Parallel helpers import lop "github.com/samber/lo/parallel" // Usage: lop.Map(...)Step 3:运行并分享。用go-playgroundMCP 工具执行示例获得可分享 URL:
mcp__go-playground__run_and_share_go_code(code: "<your code>")该工具会在 go.dev/play 上编译并运行代码,返回程序输出(用于校验正确性)和形如https://go.dev/play/p/XXXXXXX的可分享 URL。输出不符合预期就修正代码重新运行,直到结果正确。
Step 4:把 URL 写入源码与文档。先作为// Play:注释加进函数 doc block 末行,再写入对应数据文件的playUrl字段。
常见排错场景
- 首次运行超时:Go Playground 首次执行时若
github.com/samber/lo尚未被缓存可能超时——直接重试即可,模块缓存后后续运行都会成功。 - helper 尚未发布:如果文档与 helper 源码同时创建,Go Playground 因模块版本尚未发布而无法编译。此时应跳过 Playground 示例、让
playUrl留空,待下次发布后再补建。 - SIMD helper:exp/simd/ 下的 helper 需要
goexperiment.simd构建标签(Go 1.27+),Go Playground 不支持该特性,因此这些 helper 不能有 Playground 示例。
批量验证
- 用
mcp__go-playground__execute_go_playground_url重新运行已有 URL 并检查输出,验证所有 Playground URL 可编译; - 用
mcp__go-playground__read_go_playground_url读取已有 playground 的源码。
九、完整文件示例:一份可直接套用的模板
结合 docs/data/core-map.md 的真实写法,一份完整、达标的 helper 文档如下:
--- name: Map slug: map sourceRef: map.go#L123 category: core subCategory: map signatures: - "func MapT any, R any R) []R" playUrl: https://go.dev/play/p/EXAMPLE similarHelpers: [] position: 0 --- Applies a function to each element of a collection and returns a new collection with the results. ```go result := lo.Map([]int{1, 2, 3}, func(item int, index int) string { return fmt.Sprintf("%d", item) }) // []string{"1", "2", "3"}仓库中的 [docs/data/core-map.md](https://link.gitcode.com/i/702c66fb95c6b1736784c6c35b6b3464) 还演示了多示例写法——基础类型转换与结构体变换各给一个示例,后者展示了 `Map` 在真实业务对象上的用法: ```go type Person struct { FirstName string LastName string Age int } people := []Person{ {FirstName: "John", LastName: "Doe", Age: 25}, {FirstName: "Jane", LastName: "Smith", Age: 30}, } fullNames := lo.Map(people, func(p Person, index int) string { return fmt.Sprintf("%s %s", p.FirstName, p.LastName) }) // fullNames: []string{"John Doe", "Jane Smith"}十、质量保障:仓库自带的六类校验脚本
docs/站点的文档质量并非只靠人工 review,docs/package.json中注册了多个可直接运行的校验命令,全部以 Node.js 脚本实现(位于 docs/scripts/ 目录):
| npm 脚本 | 校验内容 | 对应脚本 |
|---|---|---|
check-function-signatures | ①name是否能在源码中找到函数声明;② frontmatter 签名是否存在重复;③ 是否包含源码中不存在的签名([unknown-signature]);④ 是否遗漏源码中存在的签名([missing-signature]);⑤sourceRef是否与源码实际行号一致([sourceRef-outdated]);可加--check让失败时以非零码退出 | check-function-signatures.js |
check-cross-references | 所有similarHelpers引用是否双向互引 | check-cross-references.js |
check-duplicates-in-category | 同一category#name下不允许重复 helper | check-duplicates-in-category.js |
check-filename-matches-frontmatter | 文件名必须等于{category}-{slug}.md | check-filename-matches-frontmatter.js |
check-similar-exists | 所有similarHelpers引用指向的 helper 必须存在 | check-similar-exists.js |
check-similar-keys-exist-in-directory | similarHelpers引用的category#subCategory#Name键必须存在于docs/data/目录 | check-similar-keys-exist-in-directory.js |
这些脚本共享 docs/scripts/utils.js 中的parseFrontmatter()(一个针对本项目文件定制的轻量 YAML 解析器)和loadHelpers()(按category#name、category#subCategory#name、文件名三种维度建立索引)。签名比对采用"空白归一化 + 去除行尾注释 + 去掉末尾{"的规范化处理,以保证脚本比对不受格式差异干扰。另外还有 check-helpers-visible-in-pages.js 负责核对docs/data/中出现的所有category|subCategory组合是否都有对应的站点页面(如core/slice.md、it/map.md)。
十一、提交前检查清单
按照 docs/CLAUDE.md 的 Checklist,提交前逐项确认:
- Frontmatter 完整且格式正确;
- 文件名与 slug 匹配(带
core-、mutable-、parallel-或it-前缀); sourceRef指向正确的源码行号;category与subCategory取值恰当(且属于既定分类集合);- 所有签名均已包含且格式正确;
- Go Playground 示例可运行并演示了用法;
- 预期输出以注释形式展示;
- 如有相似 helper,已在
similarHelpers中列出,且对方也回引了当前 helper; - 相关 helper 已在适当时合并到单一文件;
- 全部校验脚本无报错通过(
npm run check-*系列); - helper 已添加到
llms.txt(站点根目录 static/llms.txt)。
结语
lo 的 helper 文档体系本质上是"一份结构化数据 + 一套自动化渲染 + 六类机器校验"的闭环:frontmatter 承担语义元数据,variantHelpers/similarHelpers编织文档间的交叉引用网络,// Play:注释与playUrl把每个 helper 连接到可运行的验证环境,而校验脚本把"源码行号同步、签名一致、引用双向、文件命名正确"这些容易出错的细节全部变成可自动执行的检查项。新增 helper 时,按照本文梳理的字段语义、命名约定与工作流程操作,即可让新文档与现有 500+ helper 的文档体系无缝衔接。
- 后端
【免费下载链接】lo
💥 A Lodash-style Go library based on Go 1.18+ Generics (map, filter, contains, find...)
相关推荐
TAESDXL终极指南:如何用微型自动编码器加速Stable Diffusion XL图像生成
TAESDXL终极指南:如何用微型自动编码器加速Stable Diffusion XL图像生成 想要体验极速AI绘画吗?TAESDXL正是您需要的解决方案!这个
GitHub Copilot Prompt Files 编写指南:从 Frontmatter 到质量检查的完整规范
GitHub Copilot Prompt Files 编写指南:从 Frontmatter 到质量检查的完整规范 本篇技术指南以 awesome copilo
文档知识库AI 技能/插件Front-End Checklist 规则编写规范:从 Frontmatter 到 MDX 组件的完整创作指南
Front End Checklist 规则编写规范:从 Frontmatter 到 MDX 组件的完整创作指南 本文以仓库根目录下的 SPEC.md http
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考