
Hugo PAGE.Scratch 方法详解页面级持久键值存储与 PAGE.Store 迁移指南【免费下载链接】hugoThe world’s fastest framework for building websites.项目地址: https://gitcode.com/gh_mirrors/hu/hugoPAGE.Scratch是 Hugo 在Page对象上提供的一个方法用于创建并返回一个作用域限定于当前页面的持久化键值数据结构可在模板、短代码shortcode、局部模板partial与渲染钩子render hook之间安全地暂存和传递状态。本文以 docs/content/en/methods/page/Scratch.md 为骨架结合仓库源码与官方 Store 系列文档完整讲解其方法语义、作用域规则、软弃用状态以及迁移到PAGE.Store的路径。读完本文你将能在模板开发中熟练使用这套页面级状态存储 API并安全地完成从Scratch到Store的代码迁移。一、PAGE.Scratch 是什么根据官方文档的定义Scratch方法返回一个持久化数据结构maps.Scratch用于存储和操作按键key组织的值且作用域限定于当前页面。其方法签名为PAGE.Scratch返回类型为maps.Scratch即仓库中common/hstore/scratch.go定义的*hstore.Scratch类型。典型的使用场景包括在短代码内部累积统计值、在渲染钩子中传递中间计算结果、在父模板与子模板之间共享临时状态等。由于它随页面生命周期持久存在同一页面内多次访问Scratch都会操作同一个底层数据实例。二、软弃用说明v0.138.0 起别名为 PAGE.StoreScratch方法自 v0.138.0 起被标记为软弃用soft deprecation官方建议改用PAGE.Store方法详见 docs/content/en/methods/page/Store.md。需要明确的弃用细节别名关系自 v0.138.0 起PAGE.Scratch方法被直接别名为PAGE.Store二者在功能上完全等价。软弃用含义该方法会在未来某个版本被移除但移除日期尚未确定在你继续使用Scratch期间Hugo不会发出任何警告。迁移建议尽管目前仍可正常使用官方明确建议尽快改用PAGE.Store。从源码可以印证这一别名关系。在 hugolib/page__common.go 中func (p *pageCommon) Store() *hstore.Scratch { return p.store() } // See issue 13016. func (p *pageCommon) Scratch() *hstore.Scratch { return p.Store() }Scratch()直接调用Store()两者返回同一类型的实例而底层实例的创建采用了sync.OnceValue保证惰性初始化与并发安全见 hugolib/page__new.gostore: sync.OnceValue(func() *hstore.Scratch { return hstore.NewScratch() }), // Rarely used.这意味着无论模板中调用多少次Scratch或Store每个页面都只会在首次访问时创建一次存储实例后续访问复用同一对象天然适合在服务器重建rebuild期间跨模板保持状态。三、数据结构提供的方法官方通过 docs/content/en/_common/store-methods.md 完整记录了该数据结构支持的方法Scratch与Store共用同一套方法。以下示例均以.Scratch编写迁移时仅需将.Scratch替换为.Store。Set设置键值Set用于将指定键的值设为给定值{{ .Scratch.Set greeting Hello }}Get获取键值Get返回指定键的值类型为any{{ .Scratch.Set greeting Hello }} {{ .Scratch.Get greeting }} → HelloAdd在既有值上累加Add将给定值添加到指定键的既有值之上。规则如下对于单值Add支持 Go 的运算符即支持数值相加与字符串拼接如果某个键的首次Add传入的是数组或切片则后续的Add会把新值追加到该列表中。字符串拼接示例{{ .Scratch.Set greeting Hello }} {{ .Scratch.Add greeting Welcome }} {{ .Scratch.Get greeting }} → HelloWelcome数值累加示例{{ .Scratch.Set total 3 }} {{ .Scratch.Add total 7 }} {{ .Scratch.Get total }} → 10切片追加示例{{ .Scratch.Set greetings (slice Hello) }} {{ .Scratch.Add greetings (slice Welcome Cheers) }} {{ .Scratch.Get greetings }} → [Hello Welcome Cheers]从源码实现看common/hstore/scratch.go 中Add会先检查既有值的类型若是 Slice/Array 则通过collections.Append追加否则通过math.DoArithmetic(existingAddend, newAddend, )执行运算。同时该方法内部全程使用读写锁sync.RWMutex保护valuesmap保证并发环境下模板渲染的安全。SetInMap向键下的 map 中写入条目SetInMap接收key、mapKey和value三个参数将mapKey与value组成的键值对写入key指向的 map 中{{ .Scratch.SetInMap greetings english Hello }} {{ .Scratch.SetInMap greetings french Bonjour }} {{ .Scratch.Get greetings }} → map[english:Hello french:Bonjour]实现上SetInMap在键首次使用时自动创建map[string]any作为底层容器。DeleteInMap删除 map 中的条目DeleteInMap接收key与mapKey从key指向的 map 中移除对应条目{{ .Scratch.SetInMap greetings english Hello }} {{ .Scratch.SetInMap greetings french Bonjour }} {{ .Scratch.DeleteInMap greetings english }} {{ .Scratch.Get greetings }} → map[french:Bonjour]GetSortedMapValues按 mapKey 排序取值GetSortedMapValues返回[]any即从key指向的 map 中取出所有值并按mapKey排序后的数组。由于 map 的迭代顺序在 Go 中是不确定的需要稳定输出时应当使用此方法而非直接遍历{{ .Scratch.SetInMap greetings english Hello }} {{ .Scratch.SetInMap greetings french Bonjour }} {{ .Scratch.GetSortedMapValues greetings }} → [Hello Bonjour]对应的源码在 common/hstore/scratch.go先收集 map 的所有键经sort.Strings排序后按序组装成切片返回。Delete删除键Delete从数据结构中移除指定键{{ .Scratch.Set greeting Hello }} {{ .Scratch.Delete greeting }}四、作用域Scope规则创建数据结构的方法或函数决定了其作用域官方在 docs/content/en/_common/store-scope.md 中给出了完整对照表作用域方法或函数page页面PAGE.Storesite站点SITE.Storeglobal全局hugo.Storelocal局部collections.NewScratchshortcode短代码SHORTCODE.StorePAGE.Scratch以及同等的PAGE.Store创建的即是页面级作用域同一页面内的模板、短代码、渲染钩子共享该实例而不同页面之间互不影响。若需要其他作用域可选用上表中的对应方法或函数。例如站点级SITE.Store在 hugolib/site.go 中定义短代码级SHORTCODE.Store/SHORTCODE.Scratch在 hugolib/shortcode.go 中定义它们返回的都是同一个*hstore.Scratch类型。五、存储值的不确定性Determinate Values使用Scratch/Store时有一个重要的时序陷阱。官方在 Store 文档 中专门说明了这一点Store含Scratch常被用于在短代码模板、由短代码调用的局部模板或渲染钩子模板中写入值在这三种场景下存储的值在 Hugo渲染页面内容之前是无法确定的indeterminate。如果你需要在父模板中读取这些值而父模板此时尚未渲染页面内容可以通过将页面内容渲染结果赋给一个 noop空操作变量来强制触发内容渲染{{ $noop : .Content }} {{ .Scratch.Get mykey }}除了.Content以下方法同样可以触发内容渲染ContentWithoutSummary、FuzzyWordCount、Len、Plain、PlainWords、ReadingTime、Summary、Truncated、WordCount。例如{{ $noop : .WordCount }} {{ .Scratch.Get mykey }}六、最佳实践与迁移清单结合本文内容给出如下实操建议新代码一律使用PAGE.StoreScratch是软弃用 API虽然暂时无警告但应避免在新模板中引入技术债。机械替换即可完成迁移由于Scratch与Store底层是同一实例见 hugolib/page__common.go迁移时只需将模板中的.Scratch.全部替换为.Store.方法签名与返回类型完全一致。跨页面不要共享状态页面级作用域意味着状态隔离在单页面内需要站点级共享时请改用SITE.Store需要全局共享时使用hugo.Store。读取父模板值时先触发内容渲染若在父模板中读取短代码写入的值务必先用 noop 变量触发.Content或等效方法渲染否则可能读到空值。需要稳定顺序时使用GetSortedMapValuesGo 的 map 遍历顺序不确定涉及多语言、多分类等需要固定输出的场景务必使用排序方法而非直接遍历。综上PAGE.Scratch是 Hugo 模板体系中实现页面级状态管理的经典 API其功能完整、实现健壮读写锁保护 惰性单例创建随着 v0.138.0 将其软弃用并别名为PAGE.Store开发者应逐步迁移到新命名以跟随 Hugo 的演进方向。【免费下载链接】hugoThe world’s fastest framework for building websites.项目地址: https://gitcode.com/gh_mirrors/hu/hugo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考