Hugo Pipes 资源处理
Hugo Pipes 是 Hugo 的资产处理函数集,在 Hugo 的核心设计中扮演着关键角色,旨在提供速度和灵活性,使得 Hugo 能够在几秒钟内渲染完整的网站。它是 Hugo 快速资产管道的重要组成部分。
核心概念
资产处理功能集
Hugo Pipes 是 Hugo 内置的一套用于处理各种资产(如 CSS、JavaScript、图像等)的函数。这些功能通过 resources
命名空间下的方法提供,实现:
- 资源转换:Sass 编译、JavaScript 构建、图像处理
- 优化:压缩、指纹生成、缓存控制
- 集成:Node.js 依赖、PostCSS 插件、外部工具
资源类型
Hugo Pipes 可以操作不同范围的资源:
全局资源 (Global Resources)
位于 assets
目录中,或通过模块挂载到 assets
目录的文件:
assets/
├── scss/
│ ├── main.scss
│ └── _variables.scss
├── js/
│ ├── main.js
│ └── components/
├── images/
│ └── hero.jpg
└── data/
└── config.json
远程资源 (Remote Resources)
通过 HTTP 或 HTTPS 访问的远程服务器文件:
{{ $css := resources.GetRemote "https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" }}
{{ with $css }}
<link rel="stylesheet" href="{{ .Permalink }}">
{{ end }}
页面范围资源 (Page-scoped Resources)
页面捆绑包内的资源,通过 Page.Resources
方法访问:
{{ range .Resources.ByType "image" }}
<img src="{{ .Permalink }}" alt="{{ .Title }}">
{{ end }}
主要功能详解
CSS 处理
Sass/SCSS 编译
<!-- 基本 Sass 编译 -->
{{ $sass := resources.Get "scss/main.scss" }}
{{ $css := $sass | resources.ToCSS }}
<link rel="stylesheet" href="{{ $css.Permalink }}">
<!-- 带选项的 Sass 编译 -->
{{ $options := dict "targetPath" "css/style.css" "outputStyle" "compressed" }}
{{ $css := $sass | resources.ToCSS $options }}
<link rel="stylesheet" href="{{ $css.Permalink }}">
<!-- 开发和生产环境不同配置 -->
{{ $options := dict }}
{{ if hugo.IsProduction }}
{{ $options = dict "outputStyle" "compressed" }}
{{ else }}
{{ $options = dict "outputStyle" "expanded" "enableSourceMap" true }}
{{ end }}
{{ $css := $sass | resources.ToCSS $options }}
PostCSS 处理
<!-- 基本 PostCSS 处理 -->
{{ $css := resources.Get "css/main.css" }}
{{ $css = $css | resources.PostCSS }}
<link rel="stylesheet" href="{{ $css.Permalink }}">
<!-- 自定义 PostCSS 配置 -->
{{ $options := dict "config" "postcss.config.js" }}
{{ $css = $css | resources.PostCSS $options }}
PostCSS 配置文件示例:
// postcss.config.js
module.exports = {
plugins: [
require('autoprefixer'),
require('cssnano')({
preset: 'default',
}),
],
}
Tailwind CSS 集成
{{ $css := resources.Get "css/main.css" }}
{{ $css = $css | resources.PostCSS (dict "config" "tailwind.config.js") }}
{{ if hugo.IsProduction }}
{{ $css = $css | resources.Minify }}
{{ end }}
<link rel="stylesheet" href="{{ $css.Permalink }}">
JavaScript 处理
基本 JavaScript 构建
<!-- 简单 JavaScript 处理 -->
{{ $js := resources.Get "js/main.js" }}
{{ $js = $js | resources.Minify }}
<script src="{{ $js.Permalink }}"></script>
<!-- ESBuild 构建 -->
{{ $js := resources.Get "js/main.js" }}
{{ $built := $js | js.Build (dict "minify" true) }}
<script src="{{ $built.Permalink }}"></script>
高级 JavaScript 构建
{{ $options := dict
"minify" hugo.IsProduction
"target" "es2018"
"format" "esm"
"sourcemap" (not hugo.IsProduction)
}}
{{ $js := resources.Get "js/app.js" | js.Build $options }}
<script type="module" src="{{ $js.Permalink }}"></script>
Babel 转译
{{ $js := resources.Get "js/modern.js" }}
{{ $options := dict "config" "babel.config.json" }}
{{ $js = $js | js.Babel $options }}
<script src="{{ $js.Permalink }}"></script>
文件操作
文件连接 (Concat)
<!-- 连接多个 CSS 文件 -->
{{ $css1 := resources.Get "css/normalize.css" }}
{{ $css2 := resources.Get "css/main.css" }}
{{ $css3 := resources.Get "css/custom.css" }}
{{ $combined := slice $css1 $css2 $css3 | resources.Concat "css/all.css" }}
<link rel="stylesheet" href="{{ $combined.Permalink }}">
<!-- 连接多个 JavaScript 文件 -->
{{ $libs := slice
(resources.Get "js/jquery.js")
(resources.Get "js/bootstrap.js")
(resources.Get "js/app.js")
}}
{{ $js := $libs | resources.Concat "js/bundle.js" | resources.Minify }}
<script src="{{ $js.Permalink }}"></script>
从字符串创建资源
<!-- 动态生成 CSS -->
{{ $colors := .Site.Params.colors }}
{{ $css := printf ":root { --primary: %s; --secondary: %s; }" $colors.primary $colors.secondary }}
{{ $resource := $css | resources.FromString "css/variables.css" }}
<link rel="stylesheet" href="{{ $resource.Permalink }}">
<!-- 生成 JavaScript 配置 -->
{{ $config := dict "apiUrl" .Site.Params.apiUrl "version" .Site.Params.version }}
{{ $js := printf "window.config = %s;" ($config | jsonify) }}
{{ $resource := $js | resources.FromString "js/config.js" }}
<script src="{{ $resource.Permalink }}"></script>
模板执行
<!-- 将资源作为模板执行 -->
{{ $template := resources.Get "js/config.js.tpl" }}
{{ $js := $template | resources.ExecuteAsTemplate "js/config.js" . }}
<script src="{{ $js.Permalink }}"></script>
模板文件示例:
// assets/js/config.js.tpl
window.siteConfig = {
baseURL: '{{ .Site.BaseURL }}',
title: '{{ .Site.Title }}',
language: '{{ .Site.Language.Lang }}',
params: {{ .Site.Params | jsonify }}
};
资源优化
压缩 (Minify)
<!-- CSS 压缩 -->
{{ $css := resources.Get "css/main.css" | resources.Minify }}
<!-- JavaScript 压缩 -->
{{ $js := resources.Get "js/main.js" | resources.Minify }}
<!-- HTML 压缩(在生产环境) -->
{{ if hugo.IsProduction }}
{{ $html := resources.Get "templates/email.html" | resources.Minify }}
{{ end }}
指纹生成 (Fingerprint)
<!-- SHA256 指纹 -->
{{ $css := resources.Get "css/main.css" | resources.Minify }}
{{ $css = $css | resources.Fingerprint }}
<link rel="stylesheet" href="{{ $css.Permalink }}" integrity="{{ $css.Data.Integrity }}">
<!-- 自定义算法 -->
{{ $css = $css | resources.Fingerprint "md5" }}
<link rel="stylesheet" href="{{ $css.Permalink }}">
后期处理 (PostProcess)
<!-- 延迟处理直到构建完成 -->
{{ $css := resources.Get "css/main.css" }}
{{ $css = $css | resources.PostProcess }}
<link rel="stylesheet" href="{{ $css.Permalink }}">
图像处理
<!-- 基本图像处理 -->
{{ $image := resources.Get "images/hero.jpg" }}
{{ $resized := $image.Resize "800x600" }}
<img src="{{ $resized.Permalink }}" alt="Hero Image">
<!-- 高级图像处理 -->
{{ $options := "800x600 q85 Lanczos" }}
{{ $webp := $image.Resize $options | images.Filter (images.Format "webp") }}
<img src="{{ $webp.Permalink }}" alt="Hero Image">
<!-- 响应式图像 -->
{{ $sizes := slice "320" "640" "1024" "1200" }}
{{ range $sizes }}
{{ $resized := $image.Resize (printf "%sx webp q85" .) }}
<!-- 使用 $resized 生成 srcset -->
{{ end }}
缓存与性能优化
管道链缓存
Hugo Pipes 的调用基于整个"管道链"进行缓存:
<!-- 这个管道链只会执行一次,后续直接使用缓存 -->
{{ $result := resources.Get "scss/main.scss" |
resources.ToCSS |
resources.Minify |
resources.Fingerprint }}
resources 目录缓存
处理后的资源缓存到 resources
目录:
# 推荐将 resources 目录纳入版本控制
git add resources/
# 清理未使用的缓存
hugo --gc
缓存配置
# hugo.toml
[caches]
[caches.images]
dir = ":resourceDir/_gen"
maxAge = -1 # 永不过期
[caches.assets]
dir = ":resourceDir/_gen"
maxAge = -1
实际应用示例
完整的前端构建管道
<!-- layouts/partials/head.html -->
{{ $sass := resources.Get "scss/main.scss" }}
{{ $css := $sass | resources.ToCSS }}
{{ if hugo.IsProduction }}
{{ $css = $css | resources.PostCSS | resources.Minify | resources.Fingerprint }}
{{ end }}
<link rel="stylesheet" href="{{ $css.Permalink }}"
{{ if hugo.IsProduction }}integrity="{{ $css.Data.Integrity }}"{{ end }}>
{{ $js := resources.Get "js/main.js" }}
{{ if hugo.IsProduction }}
{{ $js = $js | js.Build (dict "minify" true) | resources.Fingerprint }}
{{ else }}
{{ $js = $js | js.Build (dict "sourcemap" "inline") }}
{{ end }}
<script src="{{ $js.Permalink }}"
{{ if hugo.IsProduction }}integrity="{{ $js.Data.Integrity }}"{{ end }}></script>
条件资源加载
<!-- 根据页面参数加载不同资源 -->
{{ if .Params.math }}
{{ $mathCSS := resources.Get "css/katex.css" | resources.Minify }}
<link rel="stylesheet" href="{{ $mathCSS.Permalink }}">
{{ end }}
{{ if .Params.chart }}
{{ $chartJS := resources.Get "js/chart.js" | resources.Minify }}
<script src="{{ $chartJS.Permalink }}"></script>
{{ end }}
主题资源覆盖
<!-- 允许用户覆盖主题资源 -->
{{ $css := "" }}
{{ if resources.Get "css/custom.css" }}
{{ $css = resources.Get "css/custom.css" }}
{{ else }}
{{ $css = resources.Get "css/default.css" }}
{{ end }}
{{ $css = $css | resources.ToCSS | resources.Minify }}
<link rel="stylesheet" href="{{ $css.Permalink }}">
与外部工具集成
Node.js 依赖集成
<!-- 使用 npm 包 -->
{{ $js := resources.Get "js/app.js" }}
{{ $built := $js | js.Build (dict
"external" (slice "react" "react-dom")
"format" "esm"
"minify" hugo.IsProduction
) }}
<script type="module" src="{{ $built.Permalink }}"></script>
自定义构建脚本
#!/bin/bash
# scripts/build-assets.sh
echo "构建前端资源..."
# 安装依赖
npm ci
# 运行自定义构建
npm run build
# Hugo 构建
hugo --minify
echo "构建完成!"
CI/CD 集成
# .github/workflows/build.yml
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build with Hugo
run: hugo --minify
调试和故障排除
启用详细日志
# 显示资源处理详情
hugo server --debug --verboseLog
# 显示模板指标
hugo server --templateMetrics
检查资源状态
<!-- 在模板中调试资源 -->
{{ $resource := resources.Get "css/main.css" }}
{{ if $resource }}
<!-- 资源存在 -->
<p>Resource found: {{ $resource.RelPermalink }}</p>
<p>Size: {{ $resource.Data.Size }} bytes</p>
{{ else }}
<!-- 资源不存在 -->
<p>Resource not found</p>
{{ end }}
常见问题解决
- 资源找不到:检查文件路径和 assets 目录配置
- 构建失败:验证外部依赖和工具版本
- 缓存问题:使用
hugo --gc
清理缓存 - 性能问题:检查管道链复杂度和缓存配置
最佳实践
1. 组织资源文件
assets/
├── scss/
│ ├── main.scss # 主样式文件
│ ├── _variables.scss # 变量
│ ├── _mixins.scss # 混合
│ └── components/ # 组件样式
├── js/
│ ├── main.js # 主脚本
│ ├── modules/ # 模块
│ └── vendor/ # 第三方库
├── images/
│ ├── src/ # 源图像
│ └── processed/ # 处理后图像
└── data/
└── config.json # 配置数据
2. 环境特定处理
{{ $options := dict }}
{{ if hugo.IsProduction }}
{{ $options = dict "minify" true "sourcemap" false }}
{{ else }}
{{ $options = dict "minify" false "sourcemap" "inline" }}
{{ end }}
{{ $js := resources.Get "js/main.js" | js.Build $options }}
3. 性能优化策略
- 使用条件加载避免不必要的资源
- 合理使用缓存机制
- 在生产环境启用压缩和指纹
- 将
resources
目录纳入版本控制
4. 安全考虑
<!-- 使用 SRI (Subresource Integrity) -->
{{ $css := resources.Get "css/main.css" | resources.Minify | resources.Fingerprint }}
<link rel="stylesheet" href="{{ $css.Permalink }}"
integrity="{{ $css.Data.Integrity }}"
crossorigin="anonymous">
通过 Hugo Pipes 的强大功能,您可以构建高效、优化的前端资源管道,提升网站的性能和用户体验。