tumblr自定义风格系统的实现

叁叁肆2018-09-19 13:41

本文来自网易云社区

作者:司徒诚颢

要实现类似tumblr的自定义风格系统,首先需要一个模板引擎。

该模板引擎需要满足以下需求:

  • 语法足够简单,最好只支持variable、block元素,不要支持循环。由于要对所有用户开放,支持循环的模板存在安全问题,可能遭到攻击,因为用户可以构造一个死循环的模板。
  • 数据不通过.访{Title},而不是${post.title}

这样的话,freemarker、velocity虽然功能强大,却不符合上面两点,就不适合在这里使用了。

不过还是有很多轻量级的模板引擎可供选择的,比如MiniTemplator、Mustache,这两个引擎都有多种语言的实现。 

其中MiniTemplator的语法更接近tumblr,稍加修改就能改成和tumblr一样的语法,正好符合我们的要求。MiniTemplatorde的解析部分基本可以直接使用,无需做太大修改。 

由于tumblr模板的数据是单层的,因此同一个变量在不同的块级元素下可能需要填充不同的数据,比如${Title}在post的block中就是日志的标题,而在顶级的block中就是博客的标题。

MiniTemplator只提供了最基本的数据填充方法,因此需要自己实现一套处理上下文相关的数据填充框架。对这种数据的填充逻辑必然是和具体的数据定义耦合在一起的:

/**  
* 数据填充接口
*
*/
public interface Data {
/**
* 该block是否需要处理
* @param blockName
* @return
*/
public boolean needProcessBlock(String blockName);
/**
* 填充该block数据
* @param xpath
* @param blockName
* @param blockNo
* @param miniTemplator
*/
public void process(String xpath, String blockName, int blockNo, MiniTemplator miniTemplator);
/**
* 获取该变量的值
* @param key
* @return
*/
public Object getValue(String key);
}
process是个递归的过程,调用process("","",0,templator)就完成对模板的数据填充:
public void process(String xpath, String curBlockName, int curBlockNo, MiniTemplator miniTemplator) {  
List blockNos = miniTemplator.getSubBlocks(curBlockNo);
for (int blockNo : blockNos) {
String blockName = miniTemplator.getBlockName(blockNo);
if (needProcessBlock(blockName)) {
process(xpath, blockName, blockNo, miniTemplator);
}
}
List variables = miniTemplator.getSubVariables(curBlockNo);
for (String variable : variables) {
miniTemplator.setVariable(variable, String.valueOf(getValue(variable)));
}
miniTemplator.addBlockByNo(curBlockNo);
}


getValue是个类似回溯的过程,根据数据填充逻辑的复杂度,可以实现多个Data接口,比如GlobalData表示全局数据,PostData表示日志数据,PostData会保存对GlobalData的引用,调用PostData的getValue时,如果postData无法处理该值,就调用GlobalData的getValue。

Appearance Options是tumblr模板的一个特色,可以给模板的使用者提供一些选项,模板根据用户的设置会显示成不同的效果,比如就是一个条件值。

虽然minitemplator本身提供条件判断,但是如果把这些条件值在初始化minitemplator的时候传给TemplateSpecification,就会导致模板的结构和用户的数据交织在一起,这样每个用户都会有一个minitemplator的实例,给缓存模板带来麻烦。

因此还是使用和tumblt一样的语法,把这些参数都作为数据,和templator的解析区分开:
如果if:Show Likes的选项会真,就设置数据IfShowLikes为true,IfNotShowLikes为false,{block:IfShowLikes}{/block:IfShowLikes}中内容会被渲染,{block:IfNotShowLikes}{/block:IfNotShowLikes}中内容不会被渲染。

Appearance Options的定义也单独解析: 

只要再对MiniTemplator做一些扩展(主要是添加一些接口),这样就基本上实现tumblr的自定义风格模板系统,只要针对自己的需求添加一些处理各种块数据的逻辑就可以了。

使用这样的自定义风格系统,所有的数据都是在服务器端填充的,因此数据的加载速度是很重要的。
需要用一些方法来加速页面的加载:

  • 服务器端对于相互间没有依赖的数据的获取,应该使用并行的方式来查询数据,这样可以成倍的减少加载时间。
  • 对某些特殊的数据块,服务器不直接填充数据,而是填充js代码,当页面加载完成后,再异步加载这些数据并显示。
  • 对某些用户数据,可是适当的使用本地缓存。

由于tumblr允许用户上传自己的html模板,因此防范恶意代码也是相当重要的。tumblr的cookie是放在www.tumblr.com下的,因此可以防止个人主页窃取cookie。



网易云免费体验馆0成本体验20+款云产品!

更多网易研发、产品、运营经验分享请访问网易云社区