Freewind @ Thoughtworks scala java javascript dart 工具 编程实践 月结 math python english [comments admin] [feed]

(2012-02-11) base标签解决了模板系统与静态页面生成的难题

广告: 云梯:翻墙vpn (省10元) 土行孙:科研用户翻墙http proxy (有优惠)

网站群项目中,有一个重要的功能点:

  1. 用户通过我们提供的一些jsp标签,自己制作页面模板
  2. 系统根据用户制作的模板,生成静态化网站,可上传到任一http服务器供浏览

在这里有一个难题:页面中引用的图片、css/js等资源文件的路径难以处理

具体来说有以下几点:

一、用户制作模板页面时,使用dreamweaver。为了能够实时看到效果,页面在引用图片等资源文件时,是以相对路径来取值。这里假定目录结构为:

index.html

category.html

article.html

/images/

/js/

/css/

/others/

可以看到,各html模板页面,都处于根目录下,之间为平级结构。各资源文件根据种类不同,放在不同的目录下。所以在html中引用它们的时候,一般都是写成:

从中可见,不论是在html标签中,css定义中,甚至是js中,都是直接使用如"images/??“这样直接以"images"开头的相对路径方式来引用的。这种方式可以让dreamweaver找到正确的资源文件,正常显示。

二、系统在生成静态网站时,为了提供友好的url格式,各模板页面对应的url层次可能不再是平级,某些页面就不能正确的找到资源文件了

假设该站点中的某些url如下所示:

首页: http://xxx.com/jsj/index.html

栏目页: http://xxx.com/jsj/categories/news/index.html

文章页: http://xxx.com/jsj/categories/news/11111.html

从中可以看出,代表栏目与文章的url,它们比首页多了两层。如果现在它们已经是静态化的网站,则其目录结构应该为:

index.html

/categories/news/index.html

/categories/news/11111.html

/images/

/js/

/css/

/others/

如果我们在生成静态页面时,没有手动对其引用的图片地址进行处理,那么”/categories/news/index.html"与”/categories/news/11111.html"都无法找到正确的图片或其它资源了。因为资源文件都在根目录下,而它们只会在自己所在的目录中去寻找。

解决方法:

一、让用户写模板时,在资源文件路径前增加一些可供后台处理的标记,例如:

在生成静态网站时,在后台根据url的不同,生成不同的urlPrefix来填充,以生成正确的url。

这种方法在解决问题的时候,带来了一个更大的问题:用户在使用dreamweaver做页面时,无法实时看到效果!因为不论是图片,还是css/js文件,加了${urlPrefix}的路径dreamweaver都无法识别,所以没法看到效果。这是无法接受的。所以这种方法不可行。

二、重新定义模板目录结构,让它满足生成的url之间的层次关系。对于此处的例子来说,应该是这样:

index.html

/level1/level2/category.html

/level1/level2/article.html

/images/

/js/

/css/

/others/

可以看到"category.html"与"article.html"两个模板文件,现在都处于第三层,它们与资源文件之间的关系,跟最终生成的url的关系是一样的。这样,在做模板的过程中,如果想引用一个图片,就会写成:

这样生成的静态页面,也可以找到正确的图片。看起来不错,但是有一个小问题和一个大问题。

小问题是,如果有一天,url的层次结构变了,比如,现在是http://xxx.com/categories/news/index.html,以后变成了http://xxx.com/news/index.html,怎么办?我们必须手动把所有的模板文件中的”../../“改为”../“,这个工作量相当大,而且很容易出错。

大问题是:我们还提供了一个"import"标签,让模板可以包含另一个模板,以达到重用。

假设我们现在把导航栏的代码,放在一个叫_navigator.html的文件中,供其它页面引用。这个文件本身,也用到了一些图片:

它放在根目录下,其它模板都引用了它。

index.html:

catetgory.html:

index.html:

在最终生成的页面中,只有index.html上的导航栏显示正常,category与article都不正常。因为_navigator.html被包含进去后,其引用的图片路径最终都是,对于处于第三层的category与article来说,都不正确。

这个问题,导致该方案完全不可行。除非不让用import标签。

三、不对用户模板进行任何限制,而在生成静态页面时,由后台修改各引用路径

即在生成静态页面时,将category.html所引用的所有资源文件,都修改为”../../images/1.jpg"这样,不管它之间是怎么引用的。

初看时,觉得虽然麻烦点,但是应该还可能实现,因为我们有强大的Jsoup来处理html数据。但真做起来,很快发现行不通,因为实在太麻烦了,还有很多地方无法考虑。

用户可以在很多地方引用到资源文件,比如像img标签里,style属性里,页面内的css定义中,单独的css文件中,引用的js文件中,甚至js中还可以用拼字符串的形式去引用某文件。这样算下来,我们根本没有办法去正确修改所有的路径,难度实在太大,完全不可行。

四、使用base标签

html中有一个base标签,可以让当前页面所有以相对路径方式引用的资源,都以base的href属性中指定的uri作为基准(如果没有base,则以当前页面的uri作为基准)。其使用方法如下:

注意,由于IE的bug,光写在IE下是可能出错的,必须在后面加一个,或者写成这样的形式。

有了这个base标签,我们可以按照三的思路,但不用修改每一个资源路径,直接修改其base标签的href值即可。比如,首先规定,用户的html模板必须放在根目录下,这样它们引用的资源文件路径实际上都是相对于根目录。然后,在生成的html页面中,把每一页的base,都改为根目录路径(或者首页的访问地址)即可。

比如,当首页路径为http://xxx.com/jsj/index.html时,即可在所有的html页面中都使用以下base:

不论最终它们的url有多少层,全都一视同仁,问题解决。

但马上发现新的问题:页面中的base值写死了,如果我们这些静态页面放在其它服务器上url改变了,或者拷到本地以文件形式查看,怎么办?肯定路径有误。

好在经过测试,发现base的href中,还支持这样写法:

这样的话,我们只需要在生成页面时,根据url的层级来确定它与首页之间相差几层。如果是首页,将其base改为:

如果是第三级,则:

这样,不论是把生成的静态网站放到别的服务器,或者别的url下,或者本地硬盘,都可以正常显示!

正在高兴,又发现了一个大问题:只有firefox和chrome支持这种相对路径,IE全部都不支持!

根据w3c的标准,base的href必须是一个“绝对路径的uri”。看来这次IE倒是坚定的支持了标准,可惜这标准用起来太不方便了。

这下怎么办?IE是必须要支持的浏览器,难道要再换一种解决方案吗?我实在找不到更好的做法了。

最终,在stackoverflow上提问,有网友提出我们可使用javascript来取得当前的绝对路径,并替换掉base中的相对路径。经测试可行。

http://stackoverflow.com/questions/9237654/how-to-let-ie-support-html-base-tag-with-relative-href-like-base-href/9237783#9237783

网友Cheery给出一些代码,但是看起来有点太复杂了,特别是那些短变量名,让人看得郁闷。于是我写了一个简单些的代码,经过简单测试没有问题:











可以看到,base中的href还是相对路径,但是在IE下时,将根据当前的url对它进行处理。比如当前为http://xxx.com/jsj/categories/news/index.html,处理后的base就变为:

直接以本地文件方式访问该页面,也可以正常显示!现在不论是哪个浏览器,在哪里访问,都正常了。至此问题解决。

注:据说IE6在其它某些时候,对于base的处理还有些问题。但现在先不考虑这么多,发现问题时再改进。毕竟虽然麻烦,但是我们可以使用js来修复。

参看:

  1. http://notes.minty.org/cgi-bin/wiki.pl?Fixing_Base_Href_Javascript_Document.Location_For_Internet_Explorer
comments powered by Disqus