Hugo 动态数据这么玩,嘿嘿

2,418 阅读3分钟

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第 4 篇文章,点击查看活动详情

1. 背景说明

企业微信中有个 员工服务 的功能,可以给某些员工套一层马甲,对外提供一些客服的功能。员工服务号支持在聊天时右侧展示一个站点页面。效果如下:

image.png

由于这个页面的配置不需要多么高频,为此部署个动态站就有点小题大做了。Hugo 作为优秀的静态站点生成工具,正是拿来练手的好时机。每个员工服务号,对应一个 Hugo 静态页面。

Hugo 使用 Gitlab Runer 持续构建为容器镜像,结合 Argocd 实现持续发布。一切都是那么完美,而且是 Gitops 最佳实践,可真正交付的时候却发现: 业务部门的妹子们,不会提交 Git

不是所有的妹子都是程序媛,😌😌😌

如何让业务人员在后台配置,动态修改 Hugo 页面内容呢?,这就是本文要解决的业务问题 ~ ~

2. Hugo 动态数据新玩法

Hugo 支持从本地或远程 API 加载动态数据,格式为 JSON 或 CSV 类型。

{{ $dataJ := getJSON "url" }}
{{ $dataC := getCSV "separator" "url" }}

如果 URL 有前缀或后缀,也支持传多个参数,Hugo 会自动把所有参数拼接为请求 URL 路径。

{{ $dataJ := getJSON "url prefix" "arg1" "arg2" "arg n" }}
{{ $dataC := getCSV "separator" "url prefix" "arg1" "arg2" "arg n" }}

这大概你能从 官方文档中看到的所有资料,但实际使用却没那么简单。

2. 准备后端数据

image.png

后端开发使用 Django 框架,admin 界面选择 Grappelli,Markdown 编辑器选用 django-mdeditor,API 开发选择 django rest framework。接口数据样例如下:

{
    "total": 4,
    "current": 1,
    "pageSize": 15,
    "data": [
        {
            "id": 1,
            "created": "2022-09-15T16:36:07.205586+08:00",
            "modified": "2022-09-15T17:53:54.000265+08:00",
            "page": "fuwuhao-test",
            "content": "# md\r\n\r\n> aaaaa\r\n\r\n```shell\r\nabc\r\n```\r\n\r\n![](https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2F1113%2F052420110515%2F200524110515-1-1200.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1665827605&t=3343b61be72d5cbe83d8fd9ea894a574)",
            "owner": "5b41105a-ae18-471a-a01a-a566557f4021"
        },
    ]
}

3. 魔改 Hugo 主题

为简单起见,主题采用官方推荐的 ananke。预期目标是,每个员工服务号对应的静态页面中,新增一个配置 page: xxxx,该配置与后端 API 返回的 page 字段一致;页面内容通过 API 数据渲染,所以页面的正文内容,可以为空;当然即使不为空,也不会展示。

# 这就是一个新页面的所有配置
---
title: ""
date: 2022-06-29T11:08:52+08:00
draft: false
slug: "test"
page: "fuwuhao-test"
---

接下来修改主题的模版页面,找到 themes/annake/layouts/signale.html, 用以下内容代替:

{{ define "header" }}{{ partial "page-header.html" . }}{{ end }}
{{ define "main" }}
<!-- 引入 marked 和 jquery,marked 用来渲染 markdown 格式,jquery 用来操作 dom -->
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js" integrity="sha512-aVKKRRi/Q/YV+4mjoKBsE4x3H+BkegoM/em46NNlCqNTmUYADjBbeNefNxYV7giUp0VxICtqdrbqU7iVaeZNXA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <div class="flex-l mt2 mw8 center">
    <article class="center cf ph2 ph4-ns mw7">
      <header>
        <h1 class="f4 {{ $.Param "text_color" | default "mid-gray" }}">
          {{ .Title }}
        </h1>
      </header>
      <div class="nested-copy-line-height lh-copy f4 nested-links {{ $.Param "text_color" | default "mid-gray" }}">
      <!-- 核心逻辑 -->
      <!-- 经过各种百爪挠心的测试,这里必须用 with 声明局部变量,否则就报错 -->
      {{ with $dataurl := $.Site.Params.dataurl }}
        {{ with $page := $.Params.page }}
          <!-- 看这里拼接 URL 路径 -->
          {{ $dataRemote := getJSON $dataurl "v1/hugo/page/?page=" $page }}
          {{ range $dataRemote.data }}
            <div class="markdown">{{ .content }}</div>
          {{ end }}
        {{ end }}
      {{ end }}
      </div>
    </article>
    <script type="text/javascript">
      <!-- dom 加载完成后,通过 marked 解析内容,并动态修改 DOM 结构 (对前端来说小儿科) -->
      $(document).ready(function () {
        let text = marked.parse($(".markdown").text());
        $(".markdown").html(text);
      })
    </script>
  </div>
{{ end }}

最后,还要在 themes/ananke/config.yaml 中增加 dataurl 配置,也就是远程 API 的基地址。

# config.yaml
module:
  hugoVersion:
    min: "0.84.0"

params:
  dataurl: "http://0.0.0.0:8000/"

4. 总结

重点和难点在于 Hugo getJSON 参数的构建,需要对 Hugo 变量相对熟悉。花费时间更多的是,golang template 里 with 声明局部变量的部分,经过测试,目前仅有这种方法才可以实现,其他尝试均以报错终结。 Hugo 作为静态站点生成工具,对动态数据的支持是一种新的尝试,也是大众需求之所在。一个好的站点,未必非要纯静态或纯动态,你说呢!

源码未公开,如有需要,请加个人微信交流: hsdtsyl (黑色的碳酸饮料)

T 字型人才,既要有知识的广度,更要有知识的深度。博学且有特长,这是技术人未来的样子!