使用 Java 和 Maven (JBake) 生成静态网站
使用 JBake(“mvn generate-resources”)构建您的静态网站或博客。使用布局、宏和数据文件。
我们迁移了整个www.optaplanner.org网站(1399 个文件)以使用 Java 和 Maven,而不是 Ruby 和 Rake 进行构建。从表面上看,什么都没有改变。但在源代码中,对于我们的 Java 开发人员团队来说,它是一个游戏规则改变者。
我们的 Java 团队现在可以轻松地为网站做出贡献。在完成迁移后的几个小时内,我们的一位开发人员已经提交了一份不愿意用十英尺长的杆子接触以前的源代码的提交。
我们建立了这个网站。
我们在 Java 和 Maven 上构建了这个站点。
我们建立了这个网站。
我们在 JBake 和 Freemarker 上建立了这个网站。
为什么使用静态网站生成器?
静态网站生成器将模板和内容文件转换为静态 HTML/JS/CSS 网站。对于我们这样的项目,这比内容管理系统 (CMS) 有很多优势:
托管很便宜。GitHub 页面甚至免费托管静态网站。
源文件进入 Git 进行备份和历史记录。
源文件为纯文本格式:
更改以拉取请求的形式出现,以进行适当的审查和 CI 验证。
源代码在我们的 IDE 中是开放的,这鼓励将它们与代码一起重构。这会减少陈旧的内容。
多年来,Awestruct 一直为我们服务。但由于缺乏活动,是时候升级了。
为什么是 JBake?
因为我们是 Java 程序员。
有几个很好的静态网站生成器,比如 Jekyll (Ruby) 和 Hugo (Go)。我们选择JBake (Java),因为:
我们的网站现在使用 Maven ( mvn generate-resources)构建。
无需安装任何东西。甚至不是 JBake。每个人都使用相同版本的 JBake 构建,如pom.xml.
而且速度很快:即使mvn clean在我的机器上构建 150 个输出页面也只需要 20 秒。
.下面全是Java。
编写条件表达式很简单。API ( String.substring(), ...) 很熟悉。日期格式 ( d MMMM yyyy) 和正则表达式的行为符合预期。
最重要的是,错误消息很清楚。
8 年来,我用 Awestruct (Ruby) 编写了这个网站。但我从来没有花时间好好学习 Ruby,所以每次改变都需要数小时的反复试验。我不能只是阅读错误消息并修复它。这不是鲁比的错。那是因为我从来没有花几天时间来真正学习 Ruby。使用 JBake,我可以在很短的时间内修复错误:不再需要反复试验。
什么是 JBake?
JBake 是一个静态网站生成器,有很多选项:
使用 Maven 或 Gradle 构建。
我们选择Maven,因为我们所有的 repos 都是用 Maven 构建的(尽管两个OptaPlanner Quickstarts也用 Gradle 构建,因为 OptaPlanner 也支持 Gradle)。
用 Asciidoc、Markdown 或 HTML 编写内容。
我们选择Asciidoc是因为它比 Markdown更丰富、更可靠。此外,我们所有的文档都是用 Asciidoc 编写的。
使用 Freemarker、Thymeleaf 或 Groovy 创建模板。
我们选择Freemarker是因为它是一个强大的、经过实战考验的模板引擎。
技巧和窍门
这些是构建高级静态网站的常见任务以及如何在 JBake-Freemarker 中实现每个任务。您甚至可以将这些JBake 设计模式称为:
使用宏渲染共享内容
我们几乎所有的模板都显示相同的最新版本面板:
最新发布
Freemarker 模板非常适合避免重复自己 (DRY):
templates/macros.ftl使用输出 HTML 的宏创建:
<#macro latestReleases> <div class="panel panel-default"> <div class="panel-heading">Latest release</div> ... </div> </#macro>
然后在*.ftl模板中使用它:
<#import "macros.ftl" as macros> ... <div class="row"> <div class="col-md-9"> ... </div> <div class="col-md-3"> <@macros.latestReleases/> </div> </div>
使用数据文件添加视频、事件或其他易失性数据
某些数据更改过于频繁,无法在内容或模板文件中进行维护:
一个数据文件,例如一个简单的*.yml文件,可以很好地保存这样的易失性数据:
创建data/videos.yml:
- youtubeId: blK7gxqu2B0
title: "Unit testing constraints"
...
- youtubeId: gIaHtATz6n8
title: "Maintenance scheduling"
...
- youtubeId: LTkoaBk-P6U
title: "Vaccination appointment scheduling"
...
然后在ftl模板中使用它:
<#assign videos = data.get('videos.yml').data> <div class="panel panel-default"> <div class="panel-heading">Latest videos</div> <div class="panel-body"> <ul> <#list videos[0..6] as video> <li> <a href="https://youtu.be/${video.youtubeId}">${video.title}</a> </li> </#list> </ul> </div> </div>
布局继承
所有 HTML 页面通常共享相同的 HTML 头(元数据)、页眉(导航)和页脚。这些非常适合base.ftl布局,由所有其他模板扩展:
尽管大多数内容使用normalBase.ftl,但useCaseBase.ftl所有用例页面都有单独的模板,例如车辆路线问题 (VRP)、维护计划和轮班排班。
使用带有 的宏<\#nested>来构建布局继承:
创建templates/base.ftl:
<#macro layout> <html> <head> ... </head> <body> <div> ... <#-- header --> </div> <#nested> <div> ... <#-- footer --> </div> </body> </html> </#macro>
扩展它templates/useCaseBase.ftl并引入自定义属性related_tag:
<#import "base.ftl" as parent> <@layout>${content.body}</@layout> <#macro layout> <@parent.layout> <h1>${content.title}</h1> <#nested> <h2>Related videos</h2> <#assign videos = data.get('videos.yml').data> <#assign relatedVideos = videos?filter(video -> video.tags.contains(content.related_tag))> <ul> <#list relatedVideos as video> <li><a href="https://youtu.be/${video.youtubeId}">${video.title}</a></li> </#list> </ul> </@parent.layout> </#macro>
创建content/vehicleRoutingProblem.adoc使用该模板并设置该related_tag属性的用例页面:
= Vehicle Routing Problem
:jbake-type: useCaseBase
:jbake-related_tag: vehicle routing
The Vehicle Routing Problem (VRP) optimizes the routes of delivery trucks,
cargo lorries, public transportation (buses, taxi's and airplanes)
or technicians on the road, by improving the order of the visits.
This routing optimization heavily reduces driving time and fuel consumption compared to manual planning:
...
开始
自己试试吧。要构建www.optaplanner.org网站,请运行以下命令:
$ git clone https://github.com/kiegroup/o... ... $ cd optaplanner-website $ mvn clean generate-resources ... $ firefox target/website/index.html
或者看看源代码。