一、前言

今天的文章聊一下freemarker的一些特性:宏,我们将使用它写出一些模块化,可扩展的页面代码,这样的复用并且可扩展的代码风格正是我一直所追求的优雅。

二、需求案例

干巴巴的代码没意思,我们拿一个实际应用的例子。

Deprecated:由于我的博客改版了,以下线上例子不再适用,大家理解下面的代码就好了。
先看一下我们具体的需求,以我的博客网站为例,比较[首页] 及[markdown编辑器页]
可以发现他们的公共点即头部导航栏。

再比对下[首页]及[文章全文页],

可以发现公共点除了头部导航行,还包括博客大标题及右侧导航栏,用面向对象中的继承关系我们可以将它们表示如下:

正如同类可以通过基类定义通用功能实现复用,通过继承扩展一样,freemarker的页面是不是也可以定义基础模板,并经过类似继承的手段来实现复用和扩展呢,答案自然是有的,这个就是我们今天的所谈到的。

三、语法实现

首先是baseMarco.ftl 基本模板宏:

1.baseMarco.ftl

<#compress>//在基本宏里定义#compress 压缩页面指令, 扩展页就不需要定义了
<#macro base base_title base_keywords="" base_js=[] base_css=[]>
//base: 模板名 base_title base_keywords 可由扩展页传入的标题和关键字
//base_js css 由扩展页传入其自己的css js 我这里定义的是一个数据,方便传入多个
<html lang="zh-CN">
<head>
<title>初的博客-${base_title}</title>
//标题 后缀为扩展页所传入
//公共css
<link rel="stylesheet"
href="//cdn.bootcss.com/bootstrap/3.3.6/css/bootstrap.min.css">
</head>
//遍历扩展页css
<#list base_css as c>
<link rel="stylesheet" href="${staticsPath}${c}">
</#list>
<body id="main-body" >
<div class="container">
<ul class="nav nav-pills">
<li ><a href="/">首页</a></li>
<li ><a href="/articles.html">文章</a></li>
...
</ul>
</div>
//以上是公共导航栏
//#nested 指令表示扩展页内容将嵌套在此处
<#nested>
//以下是公共页脚
<footer class="blog-footer">
<p>? 2015-2016 初</p>
</footer>
</body>
//公共js
<script src="http://apps.bdimg.com/libs/jquery/1.11.3/jquery.min.js"></script>
//遍历公共js
<#list base_js as j>
<script src="${staticsPath}${j}"></script>
</#list>
</html>
</#macro>
</#compress>

ok,然后是扩展的markdown页。
有了基本宏之后,扩展页就只需要填写自己的内容了,代码非常干净。

2.markdown.ftl:

//引入基本宏
<#include "WEB-INF/views/baseMacro.ftl">
//@base 基本宏的名字 base_title 本页标题,与base中的前缀拼接就成为了该页完整标题 Chu Lung's Blog-markdown编辑器
//base_js css 本页自有的js和css
<@base base_title="markdown编辑器" base_js=["/markdown/editormd.js","/markdown/main.js"] base_css=["/markdown/css/editormd.css"] base_keywords="在线markdown编辑器,editormd">
//@base 中间的内容将嵌套至 base 宏中的#nested处
<div id="layout">
<div id="editor-div"></div>
</div> </@base>

以上就完成了markdown页面的扩展。

freemarker的宏嵌套不仅可以嵌套内容,同样也可以嵌套宏,就如同子类继承父类,”孙”类还可以继承子类一样。

前面说到了,首页文章全文页的公共点除了导航栏,还有大标题和侧边栏。因此我们需要扩展宏,让它包含这两页面的公共内容。

3.pageMarco.ftl

//宏名字 page
<#macro page title js=[] css=[] keywords="">
//引入base宏
<#include "WEB-INF/views/baseMacro.ftl">
//标题 js css等让下一级扩展页传入 keywords本页赋值
<@base base_title=title base_js=js base_css=css base_keywords="个人博客,java,初">
//以下内容将被嵌套至base宏中#nested指令处, 注意内容中又包含一个#nested指令
<div class="container">
//公共大标题
<div class="blog-header">
<h1 class="blog-title">初的博客</h1>
<p class="lead blog-description">唯爱、技术和美食三者不可辜负.</p>
</div>
<div class="row">
<div class="col-sm-8 blog-main">
//该指令表明下一级扩展页内容将被嵌套至此
<#nested>
</div>
//公共侧边栏
<div class="col-sm-3 col-sm-offset-1 blog-sidebar">
<div class="sidebar-module sidebar-module-inset">
<h4>Hi</h4>
<p>欢迎来到我的博客</p>
</div>
<#-- <div class="sidebar-module">
<h4>标签</h4>
<ol class="list-unstyled">
<li><a href="#">March 2014</a></li>
</ol>
</div>
-->
<div class="sidebar-module">
<h4>档案</h4>
<ol id="articleFilings" class="list-unstyled">
</ol>
</div>
</div>
</div>
</div>
</@base>
</#macro>

然后首页就可以嵌套至page中了,文章页也一样,这里就不再累述了。

4.index.ftl

<#include "WEB-INF/views/pageMacro.ftl">
<@page title="首页" js=["/blog/js/common.js"]>
<div>
...
</div>
</@page>

四、结语

可能是java的面向对象接触得多了,用其他语言,包括freemarker这种标记语言我都会下意识的考虑如何似组合和继承写出优雅的代码,为此需要去查询许多资料,这样其实是会花不少时间琢磨的,可能花费数天。或许页面之间copy会来的更快,几分钟就ok。但正因为可复用和扩展的代码是需要精心设计的,这可以让我保持思考,不成为一个纯粹的码农,反而可以享受编程的乐趣,保持自信。

作者:初龙

原文链接:https://chulung.com/article/extensible-modular-programming-using-the-freemarker-macro

本文由MetaCLBlog于2017-07-17 09:04:10自动同步至cnblogs

本文基于 知识共享-署名-非商业性使用-禁止演绎 4.0 国际许可协议发布,转载必须保留署名及链接。

使用Freemarker宏进行可扩展式模块化编程的更多相关文章

  1. freemarker 宏嵌套nested 的使用

    转载来源:http://blog.sina.com.cn/s/blog_7e5699790100z59g.html 模板页: <#assign basePath = request.contex ...

  2. Javascript模块化编程(一)模块的写法最佳实践六、输入全局变量 独立性是模块的重要特点,模块内部最好不与程序的其他部分直接交互。 为了在模块内部调用全局变量,必须显式地将其他变量输入模块。

    Javascript模块化编程,已经成为一个迫切的需求.理想情况下,开发者只需要实现核心的业务逻辑,其他都可以加载别人已经写好的模块但是,Javascript不是一种模块化编程语言,它不支持类clas ...

  3. 深入了解Javascript模块化编程

    本文译自Ben Cherry的<JavaScript Module Pattern: In-Depth>.虽然个人不太认同js中私有变量存在的必要性,但是本文非常全面地介绍了Javascr ...

  4. javascript 学习笔记之模块化编程

    题外: 进行web开发3年多了,javascript(后称js)用的也比较多,但是大部分都局限于函数的层次,有些公共的js函数可重用性不好,造成了程序的大量冗余,可读性差(虽然一直保留着注释的习惯,但 ...

  5. javascript模块化编程 从入门到实战

    <!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8& ...

  6. 从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 十六 ║Vue基础:ES6初体验 & 模块化编程

    缘起 昨天说到了<从壹开始前后端分离 [ Vue2.0+.NET Core2.1] 十五 ║ Vue前篇:JS对象&字面量&this>,通过总体来看,好像大家对这一块不是很 ...

  7. 深入JavaScript模块化编程

    今天看requirejs官网的manual,发现了下面这篇好文章,于是花点时间翻译了一下,翻译不好的地方请指正,谢谢!   推荐阅读原文:) http://www.adequatelygood.com ...

  8. Javascript模块化编程详解

    在这篇文章中,我将会回顾一下js模块化编程的基础,并且将会讲到一些真的非常值得一提的进阶话题,包括一个我认为是我自创的模式. 模块化编程是一种非常常见Javascript编程模式.它一般来说可以使得代 ...

  9. javascript模块化编程思想、实现与规范

    随着BS架构的发展,网站逐渐变成了互联网应用程序,嵌入网络的JavaScript代码越来越庞大,越来越复杂(业务逻辑处理或用户交互很多写在前端).网页越来越像桌面程序,需要一个团队分工协作.进度管理. ...

随机推荐

  1. Python time模块学习

    Python time模块提供了一些用于管理时间和日期的C库函数,由于它绑定到底层C实现,因此一些细节会基于具体的平台. 一.壁挂钟时间 1.time() time模块的核心函数time(),它返回纪 ...

  2. 前后数据交互(ajax) -- 初始化页面表格

    // 初始化员工信息列表 function loadpage ( pageNum ) { var keywords = $("#keywords").val(); $(" ...

  3. struts2 log4j:WARN Please initialize the log4j system properly. 解决方法

    在tomcat启动的时候,出现这个警告: log4j:WARN No appenders could be found for logger (org.apache.commons.digester. ...

  4. OD调试篇8

    那么今天需要破解的呢,是这样一款软件. 程序刚刚进去会发现一个nag弹窗   说没有注册,要花20美金才能注册.只有5天的限制期限可以用了 进去之后 点击help里的关于这款软件   也显示了这是一个 ...

  5. Unity Sprite切割导出

    这次需要将美术提供的Sprite图集切割导出,整体思路依然和上次的Sprite转prefab一致,只是在转prefab的逻辑修改为了创建Texture的逻辑. 过程很简单,直接看最终代码结果: usi ...

  6. java中set的交集、差集、并集的简单实现

    实现思路很简单,直接上代码: package test; import java.util.HashSet; import java.util.Set; public class Test { pub ...

  7. codeforces298c

    link:http://codeforces.com/problemset/problem/298/C 这道题目可以看出来我智商确实拙计 #include <iostream> #incl ...

  8. codeforces 381 D Alyona and a tree(倍增)(前缀数组)

    Alyona and a tree time limit per test 2 seconds memory limit per test 256 megabytes input standard i ...

  9. crontabs Permission denied

    问题 jack@somemachine /data/jack $ crontab -e crontabs/jack: Permission denied 解决办法 sudo chown root:cr ...

  10. Sporadic IOException: Failed to persist config

    问题 在调用Jenkins API来更新Job的时候报错‘Sporadic IOException: Failed to persist config’. 原因 https://issues.jenk ...