一、前言
今天的文章聊一下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 國際許可協議發布,轉載必須保留署名及鏈接。
