CMS系统模版中标签Label基类的设计
作者:希维• 更新时间:2023-08-28 23:00:35 •阅读 0
上节讲了页面的整个生产流程,大家都期待第三篇,也就是生产的核心内容——Label的替换。说实话,我很有鸭梨啊:)一个人一个实现思路,所以...可能你不能接受。我的标签分为2种,一种是配置变量标签(就是站点和系统的Config),用 %变量名%表示,在初始化Labels之前是要执行替换的。另外一种就是数据调用的Label咯。看下风格://简单的循环列表{Article:List Top="10" CategoryId="5"}[field:Title/]{/Article:List}//引用用户控件模版,CategoryId是需要传递的参数{System:Include TemplateId="5" CategoryId="7"/}//详情页模版{Article:Model ArticleId="Url(articleid)"}
[field:Title/]
{/Article:Model}{Artcile:Model name="PostTime" dateformat="yyyy年MM月dd日"/}大家可以看出点端倪了吧,格式都是统一的。我来说下:Article:List:是Article模块下的List标签Top :调用条数CategoryId:分类ID当然还支持其他的属性比如Skip,Class,Cache等等,这些属性关键是看List标签的支持度。下面的...当然是循环部分,而[field:FieldName/]则是具体的字段,接着是关闭标签。但例如System模块的Include标签却没有内容部分。而详情页的字段展示和列表不同,他的字段可以任意位置摆放。所以可以下面的那个Model虽没有ID也可以输出:) 这些七七八八的细节比较多。我们如何解释这些标签代码呢?其实这些都是文本,依靠文本执行代码就得靠反射了。所以得反射!是的,Article是程序集(或者是命名空间),而List其实就是个类。List又包含了好多参数,还包含了循环体,所以参数其实也是类(Parameter),而循环体里有[field]其实也是类(Field)。呵呵,一切皆是类。那么,各种的标签都是类,我们需要抽象出他们的公共部分作为基类,或许还要设计些接口?根据我们提到的所有信息里,目前能想到的就是Id,Parameters,Fields,Cache,Html和GetHtml()方法。从上面的标签里我们有看到include会给子模版里的标签传参,所以Parameters应该是可变的,Fields也最好可变的,所以数组都不合适。另外循环的时候要替换Field,所以Fields最好是键值对集合(k/v)。Parameters也存成K/V合适吗?暂时也这么存吧。每个标签在网页里出现的目的是什么?转换成Html,哪怕他是空(或许是在某些条件下输出的是空),那么我们设计成为virtual函数还是抽象成接口呢? 首先说虚函数的意义,就是子类可以去覆盖,但也可以直接使用,而接口则是必须实现。如果设计成接口,就算不输出的标签也要多去实现,那不是很烦。所以暂时我们设计成虚函数,或许我们的决定是错的。 另外GetHtml感觉名称不够准确,因为每个Label都有原始的Html代码,所以改名为 GetRenderHtml()。///
/// Label基类/// public class Label{///
/// ID,一般用于缓存的Key/// public string ID { get; set; }///
/// 原始的HTML代码/// public string Html { get; set; }///
/// 标签的参数/// public IDictionary
Parameters { get; set; }/// /// 标签的字段/// public IDictionary Fields { get; set; }/// /// 缓存/// public Cache Cache { get; set; }/// /// 获取需要呈现的HTML/// /// public virtual string GetRenderHtml(){return string.Empty;}}大家是否觉得Parameters和Fields很难看呢?因为关于他们的操作(获取某个parameter,删除,增加,枚举等)还很多,所以应该单独封装,而且万一哪天发现IDictionary不合适,所以封装是合适的。所以改成了,public ParameterCollection Parameters { get; set; }public FieldCollection Fields { get; set; }那么怎么在页面里发现这些Label,并实例化他们呢? 当然是强大的正则了。{((?\w+):(?\w+))(?[^}]*)((/})|(}(?(?>(?{\1[^}]*})|(?<-o>{/\1})|(?:(?!{/?\1)[\s\S]))*)(?(o)(?!)){/\1}))懂正则的朋友我想说:你懂的:)。字符串被分为了4个组分别是assembly,class,parameters,template。而Label的ParameterCollection和FiledCollection则需要从组和组再次使用正则获取。Parameter的正则:(?\w+)=(?("([^"]+)")|('[^']+')|([^\s\}]+))Field的正则:\[field:(?[\w\.]+)(?[^]]+)?/\]我说下嵌套的实现思路:1、递归Template找到所有的Label,被嵌套的必须有ID号2、当替换外层Label每行数据时,需要把当前行的数据DataItem传递给里层的Label,里层的Label实例可以通过FindLabel(id)来找到。是不是觉得有点像Repeater啊?哈哈。3、外层Label的Template是需要Replace掉内层Label的Html的。不然Field就乱了。说了这么多不如看代码明白,那就创建个LabelFactory类,负责Label的生产。public class LabelFactory{/// /// 匹配Label的正则/// private static readonly Regex LabelRegex = new Regex(@"{((?\w+):(?\w+))(?[^}]*)((/})|(}(?(?>(?{\1[^}]*})|(?<-o>{/\1})|(?:(?!{/?\1)[\s\S]))*)(?(o)(?!)){/\1}))");/// /// 根据模版获取其包含的所有Label/// /// 模版/// Label初始化前需要的工作/// public static IList