使用SiteMesh和EasyUI创建Web网站

在开发网站时,我们经常遇到一些公共资源,比如相同的页头和页脚,相同的导航菜单,一些共用的脚本和样式,这些东西我们希望一改统改,而不是一个个页面去改,因为需求经常在变,即使需求不变,开发过程中使用的资源也在不断的变化,这时候就需要对这些公共资源进行统一管理,我们还希望能灵活的配置,哪些地方使用这些公共的资源,而其他的地方则不使用。本文就介绍一下如何进行处理。
首先,来看一个常见的CMS系统布局:
经典CMS系统

这是一个很常见的二列布局,有公共的头部和尾部,头部一般放LOGO,标题,还有可变的登录状态信息,或一些搜索框,底部是一些版权信息或友链,中间左侧是统一的导航菜单,一般采用折叠窗或树形控件展示。
这个布局里可能用到一些公共的资源,如每个页面都会引用jQuery,还有搜索相关的脚本,大多页面都会引用jQuery EasyUI,我们会怎么去做呢。

第一种方案:使用iFrame

我们很早就接触过iFrame,尽管它有各种各样的问题,但仍不失为一种轻松易用的整合方案,我们可以写一个公共的页面,它有除了content page之外的所有内容和一个iframe,然后我们可以使用iframe来嵌入content page,每当点击左侧的菜单时,只用更换右边iframe里的内容就可以了。
so easy,但我不得不说这是一种很糟糕的方案,因为H5标准已经不推荐使用iframe了,虽然你仍然可以在H5的页面里使用它,但是使用一个面临淘汰的技术,不是一个好的选择,毕竟我们还有其他办法。
另外,iframe本身的问题,要把content page刚好撑满右边的区域,我们不得不计算那块区域的大小,并且设置iframe的大小,并且设置它引用的页面的大小,为了防止出现双滚动条,我们还需要设置content page里的内容溢出如何处理。
最重要的一条是,如果在content page里也需要使用到jQuery等资源时,你只好在内容页里再引用一遍。
接下来让我们看一看有没有别的办法。

方案二:使用JSP/PHP

动态页面有很多好处,它可以使用include命令相互包含,为此,我们独立地提供header.jsp,nav.jsp,footer.jsp这三个公共资源,然后在每个content page里引用这几个页面,这样,内容页里就不用再次引用公共的jQuery资源了。而且,由于每个页面是独立引用的,我们可以在任何一个内容页里选择不引用这些公共页面。
动态化的另一个好处是,顶部的登录信息可以直接在header.jsp里判断,在JSP里你可以自由地访问公共的信息,如session。
但,这仍不是最好的方式,因为:
首先,动态化后,后端的代码和前端的代码合在了一起,代码显示比较乱,前端与后端没有很好的分离。
其次,动态化降低了性能,服务器不得不在用户访问时进行编译处理,而实际上除了登录判断,我们没必要使用动态的页面。
第三点,每个页面独立地去引用公共资源,虽然灵活,但较麻烦。如果header.jsp重命名为banner.jsp,那么,你需要在一个个页面中去独立修改。

方案三:使用filter

在java里,我们可以写一个filter,在访问每个内容页面时,先经过filter,这个filter可以在返回前把公共的资源合并到content page页面里,一起返回给客户端。
这个方案要优于方案二,它解决了方案二中1和3条缺点,你只需要关注于content page页面的具体业务,而不需要再关注公共资源,如果公共资源有变动,也只需要修改filter。

SiteMesh

SiteMesh就是方案三的一种实现,不同的是,访问内容业里,filter不是把公共资源装配到content page里,而是创建了一个公共的模板,访问内容页时,把内容页的内容填到公共模板里去。

SiteMesh也是设计模式里装饰模式的实践。

我们看下它是怎么做的(template.jsp):

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
    //这里判断登录
%>
<html>
<head>
    <meta charset="UTF-8">
    <title>esprms: <sitemesh:write property='title'>Title goes here</sitemesh:write></title>
    <link rel="stylesheet" href="/bootstrap/css/bootstrap.min.css">
    <sitemesh:write property='head'/>
</head>
<body>
    <div class="header"></div>
    <div class="nav"></div>
    <sitemesh:write property='body'>Body goes here. Blah blah blah.</sitemesh:write>
    <div class="footer"></div>
</body>
</html>

我们看到模板里有三个sitemesh:write property=,分别有title,head,body三个属性,实际上就是把内容页里的head,title,body里的内容放进模板里相应的位置,这样访问内容页就可以看到完成的展示效果了,而我们只需要关注内容页里本身的代码就可以里,不需要重复引用公共的资源。

SiteMesh如何把脚本放到底部

不过,我们还经常需要做些性能优化的工作,比如把脚本放在尾部,而不是头部,这个有些麻烦,因为sitemesh只内置了head,title,body三个属性,如果要把脚本放到下面,需要自定义一个属性,方法是:
1、首先建一个过滤器:

package com.esprms.filter;

import org.sitemesh.SiteMeshContext;
import org.sitemesh.content.ContentProperty;
import org.sitemesh.content.tagrules.TagRuleBundle;
import org.sitemesh.content.tagrules.html.ExportTagToContentRule;
import org.sitemesh.tagprocessor.State;

public class HtmlScriptsTagRuleBundle implements TagRuleBundle {

    @Override
    public void cleanUp(State arg0, ContentProperty arg1, SiteMeshContext arg2) {
        // TODO Auto-generated method stub

    }

    @Override
    public void install(State defaultState, ContentProperty contentProperty,
            SiteMeshContext siteMeshContext) {
        defaultState.addRule("myscripts", new ExportTagToContentRule(contentProperty.getChild("myscripts"), false));

    }

}

配置自定义标签
这个filter新定义了一个标签:myscripts,然后怎么用呢?我们要把它加入到sitemesh.xml配置里去:

<!-- 增加自定义标签,目前只提供了一个 scripts -->
    <content-processor>
       <tag-rule-bundle class="com.esprms.filter.HtmlScriptsTagRuleBundle" />
    </content-processor>

使用自定义标签
在内容页里就可以使用了:

<html>
<head lang="zh-cn">
    <meta charset="UTF-8">
    <title>这里是内容页的标题</title>
    <!--这里是内容页的CSS-->
    <link rel="stylesheet" href="my.min.css">
</head>
<body>
    <!--这里是内容页的内容-->
</body>
<myscripts>
    <script type="text/javascript" src="js/test.min.js"></script>
</myscripts>
</html>

我们的模板变成了

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
    //这里判断登录
%>
<html>
<head>
    <meta charset="UTF-8">
    <title>esprms: <sitemesh:write property='title'>Title goes here</sitemesh:write></title>
    <link rel="stylesheet" href="/bootstrap/css/bootstrap.min.css">
    <sitemesh:write property='head'/>
</head>
<body>
    <div class="header"></div>
    <div class="nav"></div>
    <sitemesh:write property='body'>Body goes here. Blah blah blah.</sitemesh:write>
    <div class="footer"></div>
    <sitemesh:write property='body'>content scripts. Blah blah blah.</sitemesh:write>
</body>
</html>

SiteMesh和EasyUI

文章开始处的布局是EASYUI的常用布局,它提供了north,easy,south,center四种方式,可以自动帮我们计算,结合sitemesh,只用提供类似如下的模板就行了:

<body class="easyui-layout">
    <div region="north" style="height:80px;"></div>
    <div region="center" fit="true" class="easyui-layout">
        <div region="west" style="width:300px;"></div>
        <div region="center">
            <sitemesh:write property='body'>Body goes here. Blah blah blah.</sitemesh:write>
        </div>
    </div>
    <div region="south" style="height:40px;"></div>
</body>

其他内容自行脑补。

这样的布局,即实现了公共资源统一管理,一改统改,而不用重复引用,而且,最重要的是,我们没有用于iframe,它的适应能力很好,兼容浏览器性也好。

问题:
如果左侧导航菜单中有状态,根据右侧内容页的不同选中对应的菜单怎么办呢?
因为我们不是把模板中的内容放到内容页里,而是把内容页里的内容放到模板中再返回给客户端的,内容页里没有菜单的HTML,如果改模板里的HTML会影响到所有内容页,那么这个问题如何处理呢?
注:这个问题其实不难解决,不过这里我就不给出答案了,留做思考。

根据上面的介绍,下面这种门户型的页面就更容易用SiteMesh实现了:
门户页面

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值