自己写的简单的模板引擎

开发网站需要用到模板引擎,自己写了一个简单的。功能很简单,只做了两件事,一个是替换Model一个是替换List,支持List嵌套。

html模板示例代码如下:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title></title>
    <script type="text/javascript" src="/Scripts/jquery/jquery-1.7.1.min.js"></script>
    <script type="text/javascript" src="/Site/common.js"></script>
    <script type="text/javascript">
        $(function () {
            $("title").html("${siteInfo.siteName}");
        });
    </script>
</head>
<body style="background-color: #fff; font-size: 14px;">
    <span style="color: red;">网站基本信息</span><br />
    <img alt="" src="${siteInfo.logoUrl}" style="width: 100px; height: 100px;" />
    <span>${siteInfo.siteName}</span>
    <br />
    <span style="color: red;">菜单</span>
    <!-- 菜单 开始 ===================================================================================== -->
    <ul id="rootMenu">
        <#foreach list="channel" model="rootMenu" where="siteId=1 and level=1"  >
        <li>
            <img alt="" src="${rootMenu.iconUrl}" style="height: 30px; width: 30px;" />
            <a href="/Home/List?channelId=${rootMenu.id}&page=1">${rootMenu.title}</a>
        </li>
        <ul>
            <#foreach list="channel" model="subMenu" where="parentId=${rootMenu.id}"  >
            <li>
                <img alt="" src="${subMenu.iconUrl}" style="height: 30px; width: 30px;" />
                <a href="/Home/List?channelId=${subMenu.id}&page=1">${subMenu.title},${subMenu.sort}</a>
            </li>
            <#/foreach>
        </ul>
        <#/foreach>
    </ul>
    <!--菜单 结束 ====================================================================================== -->
    <span style="color: red;">新闻活动</span>
    <!-- 内容列表 开始 ===================================================================================== -->
    <ul>
        <#foreach list="content" model="con" where="channel.id=1" page="1" pageSize="5">
        <li>
            <img alt="" src="${con.imgUrl}" style="height: 20px; width: 20px;" />
            <a href="/Home/Content?contentId=${con.id}">${con.title}</a>
            ${con.publishTime}
        </li>
        <#/foreach>
    </ul>
    <!-- 内容列表 结束 ===================================================================================== -->
    <span style="color: red;">社会责任</span>
    <!-- 内容列表 开始 ===================================================================================== -->
    <ul>
        <#foreach list="content" model="con" where="channel.id=1" page="1" pageSize="5" >
        <li>
            <img alt="" src="${con.imgUrl}" style="height: 20px; width: 20px;" />
            <a href="/Home/Content?contentId=${con.id}">${con.title}</a>
            ${con.publishTime}
        </li>
        <#/foreach>
    </ul>
    <!-- 内容列表 结束 ===================================================================================== -->
</body>
</html>

模板引擎代码如下,设计的可能不是太好,不过很简单明了:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using Models;

namespace DAL
{
    /// <summary>
    /// 模板引擎
    /// </summary>
    public class TemplateDal
    {
        #region 载入模板
        /// <summary>
        /// 载入模板
        /// </summary>
        public static string LoadTemplate(HttpServerUtilityBase server, string url)
        {
            string result = string.Empty;
            StreamReader sr = new StreamReader(server.MapPath(url), Encoding.UTF8);
            result = sr.ReadToEnd();
            sr.Close();
            return result;
        }
        #endregion

        #region 替换页面model
        /// <summary>
        /// 替换页面model
        /// </summary>
        public static string ReplaceModels(string pageHtml, Dictionary<string, object> data)
        {
            Regex reg = new Regex(@"\${([^\${}]+)\.([^\${}]+)}", RegexOptions.IgnoreCase);
            MatchCollection mc = reg.Matches(pageHtml);
            foreach (Match m in mc)
            {
                if (data.Keys.Contains<string>(m.Groups[1].Value))
                {
                    object model = data[m.Groups[1].Value];
                    Type type = model.GetType();
                    PropertyInfo propertyInfo = type.GetProperty(m.Groups[2].Value);
                    object obj = propertyInfo.GetValue(model, null);
                    string val = string.Empty;
                    if (obj != null)
                    {
                        if (obj.GetType() == typeof(DateTime))
                        {
                            val = ((DateTime)obj).ToString("yyyy-MM-dd");
                        }
                        else
                        {
                            val = obj.ToString();
                        }
                    }
                    pageHtml = pageHtml.Replace("${" + m.Groups[1].Value + "." + m.Groups[2].Value + "}", val);
                }
            }
            return pageHtml;
        }
        #endregion

        #region 替换列表的model
        /// <summary>
        /// 替换列表的model
        /// </summary>
        public static string ReplaceModels(string html, string model, object data)
        {
            Regex reg = new Regex(@"\${[^\${}]+\.([^\${}]+)}", RegexOptions.IgnoreCase);
            MatchCollection mc = reg.Matches(html);
            foreach (Match m in mc)
            {
                Type type = data.GetType();
                PropertyInfo propertyInfo = type.GetProperty(m.Groups[1].Value);
                object obj = propertyInfo.GetValue(data, null);
                string val = string.Empty;
                if (obj != null)
                {
                    if (obj.GetType() == typeof(DateTime))
                    {
                        val = ((DateTime)obj).ToString("yyyy-MM-dd");
                    }
                    else
                    {
                        val = obj.ToString();
                    }
                }
                html = html.Replace("${" + model + "." + m.Groups[1].Value + "}", val);
            }
            return html;
        }
        #endregion

        #region 判断页面是否存在 foreach 标签
        /// <summary>
        /// 判断页面是否存在 foreach 标签
        /// </summary>
        public static bool HasForeach(string html)
        {
            Regex reg = new Regex(@"<#foreach[\s]+[^<>]*>", RegexOptions.IgnoreCase | RegexOptions.Singleline);
            return reg.IsMatch(html);
        }
        #endregion

        #region 替换foreach
        /// <summary>
        /// 替换foreach
        /// </summary>
        public static string ReplaceLists(string pageHtml)
        {
            #region 生成标签集合
            Regex reg = new Regex(@"<#foreach[\s]+[^<>]*>", RegexOptions.IgnoreCase | RegexOptions.Singleline);
            Regex reg2 = new Regex(@"<#/foreach>", RegexOptions.IgnoreCase | RegexOptions.Singleline);
            MatchCollection mc = reg.Matches(pageHtml);
            MatchCollection mc2 = reg2.Matches(pageHtml);
            List<Tag> tagList = new List<Tag>();
            foreach (Match m in mc)
            {
                Tag tag = new Tag(m.Index, TagType.TagStart, m.Value);
                tagList.Add(tag);
            }
            foreach (Match m in mc2)
            {
                Tag tag = new Tag(m.Index, TagType.TagEnd, m.Value);
                tagList.Add(tag);
            }
            tagList.Sort((a, b) => a.pos - b.pos);
            #endregion

            for (int i = 0; i < tagList.Count; i++)
            {
                Tag tag = tagList[i];
                int depth = 0;
                if (tag.type == TagType.TagStart)
                {
                    for (int j = i + 1; j < tagList.Count; j++)
                    {
                        if (tagList[j].type == TagType.TagEnd && depth == 0)
                        {
                            string innerHtml = pageHtml.Substring(tag.pos + tag.tagStr.Length, tagList[j].pos - tag.pos - tag.tagStr.Length);
                            ForeachTag foreachTag = new ForeachTag(tag.pos, tagList[j].pos, tag.tagStr, tagList[j].tagStr, innerHtml, pageHtml);
                            return foreachTag.pageHtml;
                        }
                        if (tagList[j].type == TagType.TagStart)
                        {
                            depth++;
                        }
                        if (tagList[j].type == TagType.TagEnd)
                        {
                            depth--;
                        }
                    }
                }
            }
            return pageHtml;
        }
        #endregion

    } //end of class TemplateDal

    #region Foreach标签
    /// <summary>
    /// Foreach标签
    /// </summary>
    public class ForeachTag
    {
        /// <summary>
        /// 开始位置 foreach开始标签的开始位置
        /// </summary>
        public int start { get; set; }
        /// <summary>
        /// 结束位置 foreach结束标签的开始位置
        /// </summary>
        public int end { get; set; }
        /// <summary>
        /// 标签字符串
        /// </summary>
        public string tagStr { get; set; }
        /// <summary>
        /// 结束标签字符串
        /// </summary>
        public string tagEndStr { get; set; }
        /// <summary>
        /// 标签内html
        /// </summary>
        public string innerHtml { get; set; }
        /// <summary>
        /// 页面html
        /// </summary>
        public string pageHtml { get; set; }

        /// <summary>
        /// 列表的标识
        /// </summary>
        public string list { get; set; }
        /// <summary>
        /// 列表的model
        /// </summary>
        public string model { get; set; }
        /// <summary>
        /// where
        /// </summary>
        public string where { get; set; }
        /// <summary>
        /// 当面页数
        /// </summary>
        public int page { get; set; }
        /// <summary>
        /// 每页数据条数
        /// </summary>
        public int pageSize { get; set; }

        /// <summary>
        /// 替换结果html
        /// </summary>
        public string resultHtml { get; set; }

        public ForeachTag(int start, int end, string tagStr, string tagEndStr, string innerHtml, string pageHtml)
        {
            this.start = start;
            this.end = end;
            this.tagStr = tagStr;
            this.tagEndStr = tagEndStr;
            this.innerHtml = innerHtml;
            this.pageHtml = pageHtml;

            Regex reg = new Regex(@"([^\s]+)\s*=\s*""([^""]+)""", RegexOptions.IgnoreCase);
            MatchCollection mc = reg.Matches(this.tagStr);
            foreach (Match m in mc)
            {
                switch (m.Groups[1].Value)
                {
                    case "list":
                        this.list = m.Groups[2].Value;
                        break;
                    case "model":
                        this.model = m.Groups[2].Value;
                        break;
                    case "where":
                        this.where = m.Groups[2].Value;
                        break;
                    case "page":
                        this.page = Convert.ToInt32(m.Groups[2].Value);
                        break;
                    case "pageSize":
                        this.pageSize = Convert.ToInt32(m.Groups[2].Value);
                        break;
                }
            }

            switch (list)
            {
                case "channel":
                    StringBuilder sb = new StringBuilder();
                    ChannelDal m_ChannelDal = new ChannelDal();
                    List<cms_channel_ext> channelList = m_ChannelDal.GetList(this.where);
                    foreach (cms_channel_ext channel in channelList)
                    {
                        sb.Append(TemplateDal.ReplaceModels(this.innerHtml, this.model, channel));
                    }
                    this.pageHtml = this.pageHtml.Substring(0, this.start) + sb.ToString() + this.pageHtml.Substring(this.end + this.tagEndStr.Length);
                    break;
                case "content":
                    sb = new StringBuilder();
                    ContentDal m_ContentDal = new ContentDal();
                    PagerModel pager = new PagerModel();
                    pager.rows = this.pageSize;
                    pager.page = this.page;
                    List<cms_content_ext> contentList = m_ContentDal.GetList(ref pager, this.where);
                    foreach (cms_content_ext content in contentList)
                    {
                        sb.Append(TemplateDal.ReplaceModels(this.innerHtml, this.model, content));
                    }
                    this.pageHtml = this.pageHtml.Substring(0, this.start) + sb.ToString() + "<span id=‘totalRows‘ style=‘display:none;‘>" + pager.totalRows + "</span>" + this.pageHtml.Substring(this.end + this.tagEndStr.Length);
                    break;
                default:
                    this.pageHtml = this.pageHtml.Substring(0, this.start) + this.pageHtml.Substring(this.end + this.tagEndStr.Length);
                    break;
            }
        }
    }
    #endregion

    #region 标签类型
    /// <summary>
    /// 标签类型
    /// </summary>
    public enum TagType
    {
        /// <summary>
        /// 标签开始
        /// </summary>
        TagStart = 1,
        /// <summary>
        /// 标签结束
        /// </summary>
        TagEnd = 2
    }
    #endregion

    #region 标签
    /// <summary>
    /// 标签
    /// </summary>
    public class Tag
    {
        /// <summary>
        /// 标签位置
        /// </summary>
        public int pos { get; set; }
        /// <summary>
        /// 标签类型
        /// </summary>
        public TagType type { get; set; }
        /// <summary>
        /// 标签字符串
        /// </summary>
        public string tagStr { get; set; }

        public Tag(int pos, TagType type, string tagStr)
        {
            this.pos = pos;
            this.type = type;
            this.tagStr = tagStr;
        }
    }
    #endregion

} //end of namespace DAL

在Controller中使用模板引擎:

public ActionResult List(int channelId, int page)
{
    PagerModel pager = new PagerModel();
    pager.page = page;
    cms_siteinfo siteInfo = m_SiteInfoDal.Get();
    cms_channel channel = m_ChannelDal.Get(channelId);
    cms_site site = m_SiteDal.Get(channel.siteId);
    cms_content content = m_ContentDal.GetByChannelId(channelId);
    if (content == null)
    {
        return new RedirectResult(string.Format("/site/{0}/error.html", site.folder));
    }

    string templateHtml = string.Empty;
    switch (channel.listType)
    {
        case (int)Enums.ChannelListType.文字列表:
            templateHtml = TemplateDal.LoadTemplate(Server, string.Format("/site/{0}/list.html", site.folder));
            break;
        case (int)Enums.ChannelListType.图片列表:
            templateHtml = TemplateDal.LoadTemplate(Server, string.Format("/site/{0}/list-image.html", site.folder));
            break;
        case (int)Enums.ChannelListType.单篇文章:
            return new RedirectResult(string.Format("/Home/Content?contentId={0}", content.id));
        case (int)Enums.ChannelListType.页面链接:
            return new RedirectResult(string.Format("{3}?page={1}&channelId={2}", site.folder, page, channelId, channel.pageUrl));
    }

    Dictionary<string, object> dic = new Dictionary<string, object>();
    dic.Add("channel", channel);
    dic.Add("siteInfo", siteInfo);
    dic.Add("content", content);
    dic.Add("pager", pager);
    templateHtml = TemplateDal.ReplaceModels(templateHtml, dic);
    while (TemplateDal.HasForeach(templateHtml))
    {
        templateHtml = TemplateDal.ReplaceLists(templateHtml);
    }
    ViewBag.pageHtml = templateHtml;

    return View();
}

时间: 2024-08-24 23:37:33

自己写的简单的模板引擎的相关文章

写一个简单的模板引擎

写一个简单的模板引擎 ES6 开始支持模板字符串(Template literals),支持如下的写法: `string text ${expression} string text`; 其实在很多模板引擎中,常常会有这样需求,比如常用的 doT,使用类似的语法 <div>{{=1+2}}</div> // 或者支持循环或者判断 {{for(var i in it){}}} <span>{{=i}}</span> {{}}} 简单插值的实现 我们先来看看一个

写个简单运动模板

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" con

一个简单的PHP模板引擎

PHP早期开发中通常是PHP代码和HTML代码混写,这也使代码中充斥着数据库操作,逻辑处理等.当项目不大时,这样的代码还可以接受,但是随着项目不断扩大,我们就会发现同一个文件中同时存在前端逻辑和后端处理,当逻辑越来越复杂时,代码的可读性和可维护性都会变得非常差,以至于后来不得不进行大规模的代码重构.所以后来就出现了代码分层的思想,尽量拆分开前端代码和后端代码. PHP模板引擎能解决这种混乱吗?当然可以.但是呢,即使你不用专门的模板引擎也可以写出逻辑清晰的代码,重点是要有分层的思想,有专门的脚本去

JavaScript模板引擎

JavaScript模板引擎实例应用 在之前的一篇名为<移动端基于HTML模板和JSON数据的JavaScript交互>的文章中,我向大家说明了为什么要使用JavaScript模板以及如何使用,文末还提到了laytpl.artTemplate.doT.baiduTemplate.kissyTemplate等模板引擎. 本文将举实例向大家讲解几个常用模板引擎的简单使用. 演示地址:模板引擎示例http://demo.52fhy.com/jstemp/ 准备工作 演示数据:blog.json结构:

JavaScript模板引擎artTemplate.js——结语

再次首先感谢模板的作者大神,再次放出github的地址:artTemplate性能卓越的js模板引擎 然后感谢博客园的一位前辈,他写的handlebars.js模板引擎教程,对我提供了很大的帮助,也是由此自己写了一份简单的artTemplate.js的总结. 由于这次是工作期间匆忙中写的,所以有些不完善的地方,后续会继续补充的.

掌握js模板引擎

最近要做一个小项目,不管是使用angularjs还是reactjs,都觉得大材小用了.其实我可能只需要引入一个jquery,但想到jquery对dom的操作,对于早已习惯了双向绑定模式的我,何尝不是一种痛苦. 听过这样一句话:"技术没有缺席,只有姗姗来迟",很多技术自己不知道,并非没有.今天我想介绍的就是一个简单的js模板引擎artTemplate,让我们扬帆起航吧- 一.概述 artTemplate 是新一代 javascript 模板引擎,它采用预编译方式让性能有了质的飞跃,并且充

JavaScript模板引擎实例应用(转)

本文将举实例向大家讲解几个常用模板引擎的简单使用. 演示地址:模板引擎示例http://demo.52fhy.com/jstemp/ 准备工作 演示数据:blog.json结构: { "list": [ { "title": "这是title", "url": "http://www.cnblogs.com/52fhy/p/5271447.html", "desc": "摘要&

用 php 实现一个视图组件和模板引擎——基础

只要不是做后端接口开发和一些作为守护进程之类的服务器脚本,大多数时候都是在和浏览器打交道,因此合理组织并展现 html 标签是最为常见的工作.一般大家使用框架时,都会自带有一套视图组件和模板引擎. 我们不讨论这些组件和引擎的好坏.因为这些东西已经经过考验,可以在生产环境下使用.我们现在只是为了学习一些东西,这时候了解一些原理上的可能对以后的帮助更大,如果一些人真的很有时间,利用这些基础知识完全可以写一个自己的组件,即可当做练习,也可以拿去自用. 好了,说这么多,我还是希望很多人明白,视图和模板引

nodejs+Express中使用mustache模板引擎

由于公司一个seo项目,需要我协助.此项目他人已经开发大半,由于seo需要,使用了服务器端模板引擎.我项目的后端同事说项目是go语音写的,跑项目麻烦,只给了我template和css等静态文件. 为了方便自己调试模板花了点时间用nodejs跑了一套. 装node就不说了,网上很多 mkdir appName cd appName/ npm init npm install express --save npm install mustache --save npm install mustach