`
kimmking
  • 浏览: 537242 次
  • 性别: Icon_minigender_1
  • 来自: 中华大丈夫学院
社区版块
存档分类
最新评论

领先技术:HTML 消息模式(Asp.net)

阅读更多
领先技术
HTML 消息模式
Dino Esposito

代码下载位置: CuttingEdge2008_07.exe (514 KB)
在线浏览代码

真正的 AJAX 体系结构以表示层和服务层之间的巧妙分离为特征。正如我上个月所讨论的那样,AJAX 前端和后端之间显式约定的存在为编程可能性开创了全新的世界,但同时也引出了许多体系结构方面的问题(请参阅 msdn.microsoft.com/magazine/cc546561)。我已介绍了客户端数据绑定和模板,并且讨论了浏览器端模板 (BST) 模式的实现。我还简要介绍了 HTML 消息 (HTM) 模式,该模式可作为呈现 Web 客户端用户界面的替代模型。本月,我将介绍 BST 的增强实现,并将它与 HTM 解决方案进行比较。

AJAX 服务层
我将典型 AJAX 表示层的服务器端部分称为 AJAX 服务层,以便与通常表示标准多层体系结构中表示层和中间层之间接触点的服务层相区别。图 1 说明了这一模型。
图 1 具有 AJAX 前端的典型多层系统(单击图像可查看大图)
AJAX 服务层和客户端前端之间通过 HTTP 端点进行通信,该端点由 Windows® Communication Foundation (WCF) 服务提供并由嵌入客户端页面的 JavaScript 代理类调用。尤其是在 ASP.NET AJAX 为参考平台时,设计和使用 AJAX 服务层中的服务便不成问题了。当您创建或更新用户界面时才会出现问题。
您尤其需要功能强大的工具(例如基于 JavaScript 的数据绑定和模板)以便有效地操作客户端上的数据。在这种情况中,ASP.NET 部分呈现仅仅是短期解决方案,它不能代表实际的体系结构改变。但是,ASP.NET 部分呈现让您不必再以编程方式生成用户界面。部分呈现可保留视图状态和服务器页面生命周期,并且允许您使用控件和属性以声明的方式设计用户界面。
这种模型可能是相对简单的网站的最佳选项,但在企业中,如果 AJAX 前端仅是较深层面向服务系统的顶层,它是否有效就很难说了。如图 1 所示,AJAX 服务层代表 JavaScript 前端和一组业务服务之间的中间层。它通过添加安全屏障来保护客户到业务 (C2B) 方案中的核心服务和来往于 JavaScript 对象的消息传送数据,以此消除两层之间的阻抗失谐。
AJAX 服务层在 AJAX 体系结构中发挥着重要作用,这就使基于原始数据(由核心服务返回并由 AJAX 服务层处理的数据)调节 HTML 用户界面成为结构设计师和开发人员几乎不可避免的问题。那么,如何根据原始 JavaScript 数据动态地创建和更新浏览器用户界面呢?

HTML UI 的通用模式
多年与 ASP.NET 服务器控件配合使用可能已经模糊了构建 HTML 用户界面真正需要的概念。如果您接触过自定义控件的开发,则您很可能记得它都是关于在某些缓冲区中积累 HTML 标记,然后将其输出到响应流的内容。除这种通用模式外没有其他办法。增强功能只能使其倾向于更少出错并且更易于管理。因此,积累 HTML 的缓冲区可能是纯内存流,您可以在其中编写 HTML 文字或每个组件都可抽象出大量 HTML 的更为复杂的组件层次结构。
在经典 ASP.NET 中,可通过组合可递归访问的控件树来获得网页的响应。树的每个成员都可接收记载其自身 HTML 标记的流。在 AJAX 模型中,请求的响应可能是序列化为 JavaScript Object Notation (JSON) 的原始数据、XML、整合、您所需的其他项目以及服务器上生成的 HTML。
BST 模式指请求将原始数据返回给客户端的情况。HTM 模式指请求带回直接显示标记的情况。

增强的 BST 实现
在上个月的源代码中,我创建了一个 JavaScript 类,该类将三种 HTML 模板作为输入,在整个数据集合中迭代它们并返回生成的 HTML 文本。图 2 中的 JavaScript 代码显示了该代码的本质。
function pageLoad()
{
    if (builder === null)
    {
        builder = new Samples.MarkupBuilder();
        builder.loadHeader($get("header"));
        builder.loadFooter($get("footer"));
        builder.loadItemTemplate($get("item"));
    }
}
function getLiveQuotes()
{
    Samples.WebServices.LiveQuoteService.Update(onDataAvailable);
}
function onDataAvailable(results)
{
    var temp = builder.bind(results);
    $get("grid").innerHTML = temp;
}
HTML 模板可以定义为 HTML 字符串,或从散布在页面中的 XML 数据岛中加载:
<xml id="item">
    <tr>
        <td align="left">#Symbol</td>
        <td align="right">#Quote</td>
        <td align="right">#Change</td>
     </tr> 
</xml>
HTML 模板中的任意语法都可用于查找数据绑定项的占位符。在此处包含的示例代码中,#Quote 表示绑定数据项对象中属性 Quote 的值。
有几种方法可以改进此代码并使其发挥更大的作用。其中最实质的改进是能够对各项逐一进行样式化。假设下载到客户端的数据表示多种股票的当前报价和价格变化。在这种情况下,您可能希望用绿色呈现上涨的股票,并用红色呈现下跌的股票。为此,您需要为呈现过程注入一些逻辑。图 3 显示了 Samples.MarkupBuilder 类的摘录,它可用于将股票数据集合绑定到一些现有的模板 HTML(完整代码可从本月下载中获得)。
MarkupBuilder 类的核心是 _generate 方法,它负责将模板与数据合并在一起。在图 3 中,_generate 方法携带两个参数:data 和 callback。在我上个月介绍的代码中,同一方法仅携带 data 参数。
function Samples$MarkupBuilder$_generate(data, itemCallback) 
{
    var pattern = /#\w+/g; // Finds all #word occurrences 

    var _builder = new Sys.StringBuilder(this._header);

    for(i=0; i<data.length; i++)
    {
        var dataItem = data[i];
        var template = this._itemTemplate;

        var matches = template.match(pattern); 
        for (j=0; j<matches.length; j++)
        {
            var text = matches[j];
            var memberName = text.slice(1);

            //Invoke a callback to further modify data to be bound
            var memberData = dataItem[memberName];
            var temp = memberData;
            if (itemCallback !== undefined)
            {
                temp = itemCallback(memberName, dataItem);
            }
            template = template.replace(matches[j], temp); 
          }

          _builder.append(template);
    }

    _builder.append(this._footer);

    // Return the markup
    var markup = _builder.toString();
    return markup;
}
作为客户端开发人员,您还需要指定生成器类处理给定项模板时回调的 JavaScript 函数。预期的回调函数的原型如下:
function applyFormatting(memberName, dataItem)
第一个参数是不带初始 # 符号的占位符的名称。在大多数情况下,第一个参数与绑定对象的公共属性名称相匹配。第二个参数是将绑定到当前模板实例的整个数据项对象。
整个数据对象的可用性使得用户能够全面检查运行时条件以确定标记的变化。通过此方法设计的回调函数逻辑上等同于服务器端 ASP.NET 服务器控件的 DataBound 事件。以下代码段显示了根据股票价格涨跌更改报价颜色所需的 JavaScript 代码。
function applyFormatting(memberName, dataItem)
{
    var temp = dataItem[memberName];
    if (memberName == "Change" && x.charAt(0) == "+")
    {
        return "<span style='color:green;'>" + temp + "</span>";
     }
     if (memberName == "Change" && x.charAt(0) == "-")
     {
         return "<span style='color:red;'>" + temp + "</span>";
     }
     return temp;
}
此代码中的逻辑只有在 #Change 占位符调用回调时才可应用。如果针对其他成员调用,则回调仅返回原始成员值。图 4 显示了页面中的最终效果。
图 4 浏览器中的自定义数据绑定(单击图像可查看大图)
您可能想知道如何使图 4 中的某些单元格具有不同的背景颜色。上述的回调是作为数据绑定函数提供的,但实际上带有 #xxx 表达式的项模板中的每个匹配项都可调用该回调。这意味着您实际上可以通过在模板标签中插入 #xxx 占位符来在任何地方调用回调并注入 HTML 代码。下面是一个示例:
<xml id="item">
   <tr>
      <td align="left">#Symbol</td>
      <td #Style1 align="right">#Quote</td>
     <td align="right">#Change</td>
   </tr> 
</xml>
#Style1 表达式解释为处理整个回调的占位符。回调的调用还随附有伪成员的名称(在本例中为 Style1)和当前数据项。根据所提供的信息,如果单元格用于呈现价格上涨的股票,则回调会将宿主标签的背景更改为淡黄色:
function applyFormatting(memberName, dataItem)
{
    if (memberName == "Style1")
    {
        if (dataItem["Change"].charAt(0) == "+")
            return "style='background-color:lightyellow;'";
        else
            return "";
    }
 ...
}
在 ASP.NET 中,大多数基于模板的控件不会对页眉和页脚区域应用数据绑定规则。Samples.MarkupBuilder 组件也不例外。但是,有时您可能想要在页脚中显示从所显示数据派生的信息。快速完成此操作的诀窍是在页脚(或页眉)模板中定义可脚本化的元素:
<xml id="footer">
      <tr>
         <td colspan="3" align="right" 
            style="background-   color:#eeeeee;">
            <small><i>provided by <b id="lblProvider"></b></i></small>
         </td>
      </tr>
   </table> 
</xml>
TD 标记的内容包括具有唯一 ID 的 <b> 标记。这足以使该元素进一步脚本化。应该注意的是,XML 数据岛的内容与从远程 WCF 服务收集的原始数据合并,然后当合并完成时,再通过 HTML 浏览器元素的 innerHTML 属性插入到页面对象模型中。
此时,浏览器将自动解析 HTML 的内容并更新文档对象模型 (DOM)。这样,任何包含唯一 ID 字符串的文字元素都可以脚本化。下面的示例函数是与提供股票报价的远程 WCF 服务的调用相关联的回调函数:
function onDataAvailable(results)
{
    // Bind data and update the UI
    var temp = builder.bind(results, applyFormatting);
    $get("grid").innerHTML = temp;

    $get("lblProvider").innerHTML = results[0].ProviderName;
}
一旦页面元素的 innerHTML 属性更新为包含带有给定 ID 的元素(例如 lblProvider),您就可以开始将该元素脚本化。
BST 模式将促使您使用 JavaScript 生成在浏览器中所需的任何 HTML。通常来说,这是一件好事,因为就像图 1 中所示的那样,它使您能够在一个层中隔离所有表示逻辑。
另外,通过使用模板和 JavaScript 回调,您可以随时了解 HTML 固有的动态性质,并设法适应数据特性和用户预期。模板可以有效地帮助您将代码灵活性和易于维护性结合到一起。像此处介绍的 MarkupBuilder 这样的通用类可完成出价并结束循环。您不能仅依靠单独的 JavaScript 生成 HTML,然后将生成物与少量的表示逻辑混合起来。这样的代码将很快变得非常复杂、难于阅读且不可避免地有许多错误。借助 Microsoft® Client AJAX 库开发的 Helper 类就派上用场了。

InnerHTML 和 DOM
在调用 HTML 消息模式(在 AJAX 应用程序中生成 HTML 标记的另一种方法)之前,我想讨论一下 innerHTML。DOM 是通过浏览器以编程方式公开当前显示页面内容的标准 API。DOM 将该页面表示为元素树。逻辑树中的每个节点都对应于一个具有已知行为和自身标识的活动对象。
您可以在 DOM 节点上完成以下三种基本操作:查找节点、创建节点和操作节点。只要您知道相应元素的 ID,就可以非常轻松地确定特定的节点。API 提供了非常易于使用的函数:
var node = document.getElementById(id);
在 ASP.NET AJAX 中,getElementById 函数由 $get 函数包装。如果多个元素具有相同的 ID,则该函数返回第一个出现在集合中的元素。为了更新 DOM 子树,您应该删除不需要的元素并添加新元素。从设计角度看,这种方法简单而直观,但它会导致性能问题。
几乎所有的浏览器都支持其 DOM 元素的 innerHTML 属性。该属性可设置或检索给定元素开始和结束标记之间的 HTML。虽然该属性随 Internet Explorer® 4.0 的 DHTML 对象模型一同引入,但始终未成为正式的 DOM API。但是与 DOM API 相比,innerHTML 的速度更快,在创建复杂的元素结构时尤为如此。有关 innerHTML 与 DOM 性能的信息,请参阅 go.microsoft.com/fwlink/?LinkId=116828。innerHTML 并非十全十美。要了解可能存在的问题,请访问 go.microsoft.com/fwlink/?LinkId=116827

HTML 消息模式
HTM 模式的目标是使服务器生成将要在浏览器中显示的 HTML 标记块。可能的实现包括调用远程 URL(服务或 HTTP 处理程序)和接收准备好显示的 HTML 代码段。
HTM 的实现完全依赖于服务器上的代码 — 尤其是 AJAX 服务层的代码。这也是支持创建将核心服务与 AJAX 隔离的特定于 AJAX 的中间层和表达需求及关注问题的另一个重要原因(请参阅图 1)。
AJAX 应用程序需要一种服务才可支持 HTML 消息模式,该服务应能完成为其创建的任务,并且能够将其计算结果转化为 HTML 代码段。图 5 说明了这点。
图 5 支持 HTML 消息的服务(单击图像可查看大图)
服务可组合性原则在此处显而易见。它只是可重用性原则的一种变体。通常,该原则适用于面向服务的体系结构 (SOA),在这种结构中您可以通过诸如 Web 服务业务流程执行语言 (WS-BPEL) 之类的复合语言协调业务流程,并获得其他连接生成的父项服务进程。
输出 HTML 的 AJAX 服务可视为获取其数据的核心服务与将其转换为 HTML 的呈现器服务的组合。图 6 显示了可返回股票报价服务的复合体系结构。
图 6 支持 HTML 消息的股票报价服务(单击图像可查看大图)
在此实现中,我正好组合了几个类来获取父服务和包装服务。从设计的角度看,图 6 中的几个类都可视为服务 - 股票报价提供程序、数据查找器、输出呈现器以及您稍候将在本专栏中看到的输入适配器。
实现 HTM 模式
图 7 显示了当前在 HTM 模式的示例实现中所使用的股票报价服务的约定。该服务是围绕脱机和联机数据提供程序构建的。脱机数据提供程序返回报价和更改的原值,而联机提供程序则连接实际的金融服务并返回实时数据。
namespace Samples.Services.FinanceInfo
{
    [ServiceContract(Namespace="Samples.Services",
         Name="FinanceInfoService")]
    public interface IFinanceInfoService
    {
        [OperationContract]
        StockInfo[] GetQuotes(string symbols, bool isOffline);

        [OperationContract(Name="GetQuotesOffline")]
        StockInfo[] GetQuotes(string symbols);

        [OperationContract(Name="GetQuotesFromConfig")]
        StockInfo[] GetQuotes(bool isOffline);

        [OperationContract(Name = "GetQuotesFromConfigOffline")]
        StockInfo[] GetQuotes();

        [OperationContract(Name = "GetQuotesOfflineAsHtml")]
        string GetQuotesAsHtml(string symbols, bool isOffline);

        [OperationContract(Name = "GetQuotesFromConfigAsHtml")]
        string GetQuotesAsHtml(bool isOffline);

        [OperationContract(Name = "GetQuotesFromConfigAsHtmlEx")]
        string GetQuotesAsHtml(string contextKey);
    }
}
两种提供程序都依靠内部查找器组件来实际获取数据。查找器组件以接口为特征,而实际的查找器类是从配置文件中读取的。脱机提供程序的默认查找器组件使用 Microsoft .NET Framework Random 类生成随机数字。联机提供程序的查找器组件可以使用返回金融信息的任何公共 Web 服务。
通过查找器类获得的所有数据随后将使用呈现器类组合到 HTML 代码段中。呈现器组件公开其接口,只需更改配置文件中的设置即可进行替换。默认 HTML 呈现器将以某些硬编码样式构建一个表格。在本月的源代码中,您将发现实际的 HTML 呈现器是从添加了最近更新标签的默认呈现器中派生出的类。以下代码段显示了查找器和呈现类的接口:
namespace Samples.Services.FinanceInfo
  {
      public interface IFinanceInfoFinder
      {
          string ProviderName { get; }
          StockInfo[] FindQuoteInfo            (string symbols);
      }
      public interface IFinanceInfoRenderer
      {
          string GenerateHtml            (StockInfo[] stocks);
      }
  }
该接口保证当通过查找器获得的数据直接流入呈现器的方法时能够实现稳定的互操作。
在此实现中,呈现器的 Generate­Html 方法会根据某些预定义的设置构建表格。通常,它可以使用可能是由客户端传送来任何其他样式信息。但是,股票报价服务旨在选取那些在服务器上配置为正式 HTML 生成器的所有呈现器“服务”。此“服务”的所有任务就是实现上述的 IFinance­InfoRenderer 接口。
使用了通过股票服务生成的标记的示例如下所示:
function getLiveQuotes()
{
    var isOffline = $get("chkOffline").checked;
    Samples.Services.FinanceInfoService.GetQuotesFromConfigAsHtml
      (isOffline,
      onDataAvailable);
}
function onDataAvailable(results)
{
    // Update the UI
    $get("grid").innerHTML = results;
}
getLiveQuotes 函数被附加在客户端事件(如按钮单击或计时器回调事件)上。图 8 显示了运行中的示例页面。HTML 标记使用 JSON 数据包返回到该客户端。
图 8 执行 HTML 消息模式(单击图像可查看大图)

性能和设计方面的注意事项
HTML 消息模式将 UI 生成工作转移到服务器上,具体地讲,转移到那些从客户端调用的服务上。此模型既有优点也有缺点。一方面,它允许您使用托管代码实现任何生成标记所需的复杂逻辑。在服务器上,您可以读取配置文件,连接到远程服务并可访问那些在浏览器中无法获得的具有编程功能的 HTML 模板数据库。
但是,您编写生成该标记的代码时无法从可视化工具(如 Designer)得到更多帮助。任何需要更改的标记都需要使用 C# 代码进行处理,并且布局、数据和代码分离之间并未明确分离。
您可以在服务器到服务器方案中某些查询标记服务的内部 ASP.NET 页面的定义中找到可能的解决方法。这些页面可起到模板的作用;可以使用 Visual Studio® 2008 创建它们,并将其部署在托管 AJAX 服务层的同一 IIS 应用程序中。这些页面将通过编程方式调用,而它们返回的标记将被转发给客户端。
HTML 消息模式往往会比单纯调用返回原始数据的服务产生更多的流量。应该注意的是,尽管 HTML 消息模式产生的流量比部分呈现要少。但添加的样式和 HTML 增强功能越多,则返回的数据包尺寸就变得越大。
鉴于这一点,您可以将 HTML 样式与 HTML 布局分离开来,并且只在标记中嵌入对客户端 CSS 类的引用以提供样式信息。如果将 HTML 标记减少到仅包含布局和数据,则传输原始数据以外的额外负载量百分比将大大降低。在实验过程中,我发现如果您只需要显示少量字段,并且限制为只能引用 CSS 客户端类提供样式信息,则使用 HTML 消息构建的页面所产生的流量有时可能比利用 BST 构建的页面还要低。

DynamicPopulate 扩展器
作为总结,我希望花些时间介绍一下 AJAX 控件工具包中提供的其中一种扩展器 - DynamicPopulate 扩展器,它能够很好地与 HTML 消息服务配合使用。
当绑定到客户端触发器控件(如按钮)时,该扩展器将调用服务方法并将结果附加到 DOM 元素的 innerHTML 属性上。无须赘述,DynamicPopulate 扩展器需要 AJAX 服务层中的 HTML 消息服务:
<act:DynamicPopulateExtender runat="server" 
    ID="DynamicPopulateExtender1" 
    BehaviorID="DynamicPopulateExtender1" 
    ClearContentsDuringUpdate="false"
    TargetControlID="grid" 
    UpdatingCssClass="updating" 
    ServicePath="LiveQuotes.svc"
    ServiceMethod="GetQuotesFromConfigAsHtmlEx"
/>
DynamicPopulate 扩展器还为图 6 中输入适配器服务所突出显示的服务带来另一项要求。通过 ServiceMethod 属性引用的方法需要具备以下原型:
string MethodName(string contextKey);
contextKey 参数可以包含以服务方法处理所使用的所有格式序列化的任何数据。在输入适配器服务类中,您可以接收输入字符串并将其转换为服务中的其他类知道如何处理的特定参数。
在使用扩展器时,你可能会遇到无法阻止用户单击按钮时的默认事件的问题。因此,如果该按钮是一个 ASP.NET 按钮,那么仍然会执行回发从而导致服务调用无效。下面是 DynamicPopulate 扩展器更为常见的使用方法:
<asp:Button runat="server" id="btnRefresh" text="Live Quotes" 
    onclientclick="invoke();return false;" />
随附的 JavaScript 简单函数将执行以下操作:
function invoke()
{
    var extender = $find("DynamicPopulateExtender1"); 
    var isOffline = $get("chkOffline").checked;
    extender.populate(isOffline.toString()); 
}
根据这段代码,UI 会将 HTML 响应与扩展器 TargetControlID 属性所指定的元素合并在一起来进行更新。

即时解决方案
HTML 用于显示由服务器生成的 XML、JSON 和 RSS 格式的数据,这些数据将传送到客户端并在客户端上显示。在 AJAX 环境中,JavaScript 语言的使用阻碍了这个整洁的模型。将数据下载到客户端后,您只能通过 JavaScript 构建 UI。采用自定义数据绑定形式和模板技术的 BST 模式可以帮助您创建所需的 UI。
如果思路以服务器为中心,而您又不愿使用 JavaScript 时怎么办呢?如果 UI 特别复杂,同时您又喜欢使用更为可靠和功能强大的开发及调试工具时怎么办呢?如果在某些大型数据结构的服务器和客户端您都必须重复使用相同的重量级算法时怎么办呢?难道 HTML 响应不能消除客户端的某些顾虑吗?
通常,我相信如果没有一组功能强大且有丰富客户端对象模型的控件,就不能一直使用那些利用 HTML 进行显示、将 JSON 用于数据的模型。目前,在人们开始关注 AJAX 显示前,这种模型可能无法应用于所有的场合。这就是为什么 HTML 消息模式值得关注的原因。虽然它可能不是万能的,但它仍有自己的用武之地。

 

请将您想向 Dino 询问的问题和提出的意见发送至 cutting@microsoft.com

 

Dino Esposito 是《Programming ASP.NET 3.5 Core Reference》的作者。Dino 定居于意大利,经常在世界各地的业内活动中发表演讲。您可加入他的博客,网址为 weblogs.asp.net/despos
分享到:
评论

相关推荐

    W3school.chm

    ASP.NET PHP 5.1 XSLT 1.0 XPath 2.0 XSL-FO WML 1.1 HTML 颜色 字符集 HTML ASCII HTML Latin-1 HTML 符号 实例/案例 HTML 实例 CSS 实例 XML 实例 XML DOM 实例 WAP 实例 JavaScript 实例 ...

    w3school chm

    ASP.NET PHP 5.1 XSLT 1.0 XPath 2.0 XSL-FO WML 1.1 HTML 颜色 字符集 HTML ASCII HTML Latin-1 HTML 符号 实例/案例 HTML 实例 CSS 实例 XML 实例 XML DOM 实例 WAP 实例 ...

    生成html版在线商城购物网站模板下载

    软件代码多重过滤结合强大的SEO优化可以快速被搜索引擎收录,独特的静态生成算法可以大大减轻服务器的负担,无论在生成速度还是安全方面都达到国内领先水平。  免费的生成html静态文件的购物网站网软志成HTML静态版...

    创想电子商务建站系统

    创想电子商务建站系统(授权版),帮你自建网上贸易商城 简单:你想自己做一个大型贸易网站吗,5分钟内我们可以帮助你轻松实现. 领先技术:采用模块功能和静态html生成技术,通过网站后台管理轻松实现各类...

    行业贸易商务门户网站系统正式版

    专为各行业门户贸易网站设计的,ASP.NET编程的行业B2B电子商务系统全功能,利用B2B仿阿里巴巴电子商务网上贸易网站管理系统,可在全球企业间(B2B)电子商务的建立B2B行业网站著名品牌,汇集海量供求信息,全球领先的...

    H5智能内核-基于MVC架构的全新Zoomla!逐浪CMS2 x3.8发布

    ASP.NET 支持三种不同的开发模式: Web Pages(Web 页面)、MVC(Model View Controller 模型-视图-控制器)、Web Forms(Web 窗体) MVC 编程模式 MVC 是三种 ASP.NET 编程模式中的一种。 MVC 是一种使用 MVC...

    韩式服装网店发布系统源码下载

    信息时代,各级男装,女装,童装,时装等服装经营公司,需要一个动态网站,技术领先:集百家之所长,技术领先、功能完备; 快速建店:只需简单设置,10分钟即可以建立一个功能完备的网上服装商城; 操作简便:采用人性化...

    Web2.0社区平台源码

    SpaceBuilder采用世界领先的技术体系架构、优异的缓存技术、基于Lucene的全文检索技术,可以承载千万级的数据,并提供良好的安全性及用户体验,表现层采用asp.net mvc及jQuery开发,是迄今为止基于asp.net技术领域最...

    SK企业网站管理系统 v4.0普及版

    [SK企业网站管理系统]是企业网站系统领域当前功能最强大的开源系统,系统采用“核心skcms+模板”模式,代码全部开源,可极其方便的进行二次开发,功能无限扩展! 伪静态.HTML页面,管理更加方便,有利于搜索优化,...

Global site tag (gtag.js) - Google Analytics