我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第 4 篇文章,点击查看活动详情”
1. 背景说明
企业微信中有个 员工服务 的功能,可以给某些员工套一层马甲,对外提供一些客服的功能。员工服务号支持在聊天时右侧展示一个站点页面。效果如下:
由于这个页面的配置不需要多么高频,为此部署个动态站就有点小题大做了。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. 准备后端数据
后端开发使用 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",
"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 字型人才,既要有知识的广度,更要有知识的深度。博学且有特长,这是技术人未来的样子!