一、前言

今天的文章聊一下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. python3使用requests登录人人影视网站

    python3使用requests登录人人影视网站 继续练习使用requests登录网站,人人影视有一项功能是签到功能,需要每天登录签到才能升级. 下面的代码python代码实现了使用requests ...

  2. Android Listview & Adapter

    Listview主要有两个职责: 将数据填充到布局 处理用户的选择点击等操作 列表的显示需要三个元素: ListVeiw 用来展示列表的View 适配器(Adapter) 用来把数据映射到ListVi ...

  3. transform:rotate在手机上显示有锯齿的解决方案

    transform:rotate 属于简单好用的效果,但在手机上显示时,会有比较明显锯齿. 解决方案也很简单, 利用外层容器的overflow:hidden 加上图片margin:-1px 就可以解决 ...

  4. ssh连接linux服务器只显示-bash-4.1#不显示路径解决方法

    ssh连接linux服务器只显示-bash-4.1#不显示路径时,我们只需要修改  ~/.bash_profile文件,如果不存在这个文件,那么新建一个,增加内容  export PS1='[\u@\ ...

  5. ThinkPHP整合微信支付之发裂变红包

    1.去商户平台里,给你的商户充钱,没钱是发不了红包哒! 2.微信红包需要证书支持,所以请大家到商户平台下去下载好证书后放到安全文件夹下,并且需要在配置文件中指定好证书路径! 好,接下来带来裂变红包具体 ...

  6. java.outOfMemory

    http://www.kdgregory.com/index.php?page=java.outOfMemory Java Platform, Standard Edition HotSpot Vir ...

  7. 常见C内存管理程序

    本文主要关注的是C内存管理程序,比较著名的几个C内存管理程序,其中包括: l   Doug Lea Malloc:Doug Lea Malloc实际上是完整的一组分配程序,其中包括Doug Lea的原 ...

  8. SSH 创建证书认证

    *******chmod 600 195-tempuser.txt******* useradd tempusersu tempusercdmkdir .sshchmod 700 .sshcd .ss ...

  9. selenium之xpath定位和input文本

    selenium之xpath定位和input文本 xpath简单定位: 打开浏览器的F12 在自己需要定位的元素的那里右键 选择copy->xpath selenium获取input下的文本: ...

  10. eclipse 使用(一)单步调试

    昨天终于将取数据的流程走通了.但是没有成功获得数据.原因是,把服务器中的数据库还原到了本地.而测试数据是写到了本地.把数据给覆盖了.早上来了之后,赶紧在服务器上把数据弄了一下. 之后开始跑代码. 项目 ...