代码改变世界

ajax数据加载经验分享

2007-12-31 14:11  Clingingboy  阅读(4930)  评论(5编辑  收藏  举报
    好久没写篇像样的随笔了,昨天晚上看了老赵的随笔,又由于这段时间这方面技术用的比较多,可能大家已经在用了.这里且与大家分享.


一.纯数据输出

1.早在看ActionScript的时候便接触到了这种写法,直到ajax很火的时候,才知道这种格式叫json.

json数据格式是供前端javascript进行操作的暂时性的数据格式,由于javascript语言的原因,访问json数据非常方便,灵活.本来由后端负责产生Ui的,现在全部由javascript操作dom来产生.避免重复,请大家参考此文JSON简介,同时推荐看看IBM的
掌握 Ajax系列.

为了产生json格式的数据,asp.net ajax框架提供了一个JavaScriptSerializer类用于将数据转换成json.

分析下此做法的优点与缺点

优点:
1.后台传输数据量会比较小,减轻服务器压力
2.前端操作非常灵活,可以做很多事情
3.适用于交互性比较强的应用.
缺点:
1.必须要操作dom才能产生ui,无法与美工配合工作,需要熟悉javascript和dom的人来参与
2.大数据量下,前端javascript对json数据的解析和dom的生成会明显减慢.
豪无疑问的,直接产生html比解析要速度来的快.这是一点,要是在后端直接输出html的话,可能会由于产生的字符串太多,直接拖垮IE,挂掉.
我想唯一的做法就是尽量避免大数据量,这便要看大家的创意了,还有就是输出json,然后对json分批操作解析,这样也会有所缓解.
3.灵活的同时,降低了通用性(我有一个需求,某一天我用ajax用到我不想用了,我想回到传统的网页上去,啊噢,这种开发模式做不到)
4.前端开发人员必须了解json数据格式

二.与Ui一起输出

这是我们一贯的做法,如asp.net ajax中的UpdatePanel,其会去后端请求其Panel中所有的UI,然后返回给前端,前端不需要解析,直接得到数据输出便可.

分析下此做法的优点与缺点

优点:
1.可以与美工配合,避免动态产生dom的错误
2.前端开发人员不再需要了解后端传回什么数据格式,直接输出
3.复用性比较强,可以同时满足ajax和传统网页的浏览模式

缺点:
1.大大降低了数据的交互性,前端有时候无法得到想要的对象,只能依赖于后端产生的html数据.
2.后端数据传输量稍微变大,不过影响不大.(相对而言,后端把对象解析成json格式需要时间,前端解析json数据也是需要时间的,主要的速度瓶颈还是在数据库连接访问这里)

三.选择你想要的开发模式

上面两种模式没有谁好谁不好之说,追求效率的人可能会选第一种,这种模式与语言无端,可以套用在php,jsp,asp,都可以.
第二种开发模式比较适合asp.net,为什么呢?因为asp.net本身便存在着控件这一用法,而且效率并不低.

大家可以先看看老赵这篇技巧:使用User Control做HTML生成

四.我的做法

1.我有一个白老鼠程序,自己写的blog程序,我会在其上面应用一些新技术,然后思考一些问题.
初期阶段,我用ajax去加载UserControl中产生的html,开始感觉很好.后来我又想做成传统的网页形式,去掉ajax的加载方式,所以,我需要这么写(很高兴,终于看到代码了)
<%@ Page ContentType="text/html" Language="C#" EnableViewState="true" MasterPageFile="~/Templete.Master" AutoEventWireup="true" CodeBehind="default.aspx.cs" Inherits="NLBS._default" Title="无标题页" %>
<%@ Register Src="Control/content.ascx" TagName="content" TagPrefix="uc11" %>

<asp:Content ID="Content1" runat="server" contentplaceholderid="ContentPlaceHolder1">

<uc11:content ID="content" runat="server" />
   
</asp:Content>
content用户控件,用于显示文章列表.大家可以看看我的白老鼠程序http://www.clingingboy.cn
然后我做着做着...看到别人的blog有ajax应用,那个酷啊,而且我的虚拟主机便宜,访问数据速度慢.我又想把它换成ajax模式爽一把(我的天),或者说我想随意变换数据的加载模式.这种模式采用json数据格式的话根本无法满足需求,修改太大.怎么办怎么办?

由于本人偷偷的看了下ComponentArt的CallBack控件的源码(其实很早就看了),所以本人也仿照其做了一个自己的CallBack控件.我需要一个容器放content用户控件,然后再异步加载.现在现实的问题,这个页面便是content用户控件的容器.我们通过改变url参数来加载不同数据.(控件呈现是在Render方法中实现)
看下面代码.现在你可以访问诸如http://www.clingingboy.cn/default.aspx?singleArticle_param=true&ID=70  这样的url来获取content数据

        protected override void Render(HtmlTextWriter writer)
        
{
            
//得到一个querystring参数
            string[] requestParams = this.GetCallbackParameters();
            
            
if (requestParams != null && requestParams.Length > 0)
            
{
                
//输出控件数据
                this.HandleCallback(requestParams);
                
return;
            }

            
            
base.Render(writer);
        }

     
private void HandleCallback(string[] arParams)
        
{
            
try
            
{
                
//try render the HtmlTextWriter
                
                StringWriter oStringWriter 
= new StringWriter();
                HtmlTextWriter oWriter 
= new HtmlTextWriter(oStringWriter, string.Empty);

                CallBackEventArgs oArgs 
= new CallBackEventArgs(oWriter);
                oArgs.Parameter 
= arParams[0];
                oArgs.Parameters 
= arParams;

                
this.OnCallback(oArgs);

                oWriter.Close();

                Context.Response.Clear();
                
                Context.Response.Write(oStringWriter.ToString().Trim());

            }

            
catch (Exception ex)
            
{
                
//render ther error
                this.HandleCallbackError(ex);
            }


            
try
            
{
                Context.Response.End();
            }

            
catch { }
        }



小心:如果此时,你在本页面来请求content用户控件,呈现出来的会是2个content控件的ui.所以我们需要另建一个空白页面。使用此模式你不旦可以加载用户控件,同时也可以加载asp.net的内置控件。

2.ajax数据加载与传统url同在

我要访问一篇:博客园的一篇随笔,如http://www.cnblogs.com/JeffreyZhao/archive/2007/12/30/usercontrol_as_an_template.html,那在ajax模式下,我们不是要去掉此url,换成javascript的一个数据请求函数?我们可以用preventDefault()方法把链接给屏蔽掉,或者用stopPropagation()方法把事件给阻截下来.(上面两个方法都是firefox下的方法,ie也有此功能方法,asp.net ajax框架进行了封装)

三.ajax加载用户控件
在MVC中的应用

相信有部分人已经在开始研究asp.net ajax了,请看此文Ajax with the ASP.NET MVC Framework。作者写了一个Script#,所以其前端javascript代码也是通过用其生成了。我们不管前端,我们只来谈加载方式。让我们看一个Control中的Add方法

        [ControllerAction]
        
public void Add(string name) {
            Task task 
= null;
            
if (String.IsNullOrEmpty(name) == false{
                task 
= _taskDB.AddTask(name);
            }


            
if (IsAjaxRequest) {
                
if (task != null{

                    RenderView(
"TaskView", task);

     
                }

            }

            
else {
                
if (task != null{
                    RedirectToAction(
"List");
                }

                
else {
                    Dictionary
<stringobject> viewData = new Dictionary<stringobject>();
                    viewData[
"Tasks"= _taskDB.GetTasks();
                    viewData[
"ShowAddTaskError"= true;

                    RenderView(
"List", viewData);
                }

            }

        }

RenderView会呈现控件的ui,(page,usercontrol都是控件).此代码我们可以看到如果用ajax模式,则会转到TaskView用户控件这里,其本质还是输出UserControl的数据,然后前端得到数据并呈现。但这种做法个人认为不好,每个Action都需要去写if else的语句。我想应该分离出来。

四。控件Id与Name

上次发的随笔篇幅太小,被刷了下来,希望这次没这么倒霉。但我还是希望大家看看修改服务器控件的ID和Name ,解决大家的痛,我们是有办法解决。最后阿门,愿MVC模式与控件同在,呵呵。我可不想 <% %> 横行霸道。

五.让数据绑定控件
代码更漂亮

让我们来看个Repater控件的传统应用

<asp:Repeater ID="normalArticleList" runat="server">
    
<ItemTemplate>
        
<div class="textbox">
            
<div class="textbox-title">
                
<h4><onclick="<%# urlFormats.ArticleConent(Convert.ToInt32(Eval("ArticleID").ToString()))%>" href="#"><span runat=server visible=<%# Eval("IsTop")%>>[置顶]</span><%Eval("Title")%></a>
                
</h4>
                
<div class="textbox-label">
                    [
                    
<%Eval("PostTime")%>
                    | 
<%=lang["author"]%><onclick="<%# urlFormats.UserDetail(int.Parse(Eval("AuthorID").ToString()))%>" href="#"><%Eval("Author"%></a> ]</div>
            
</div>
            
<div class="textbox-content">
                
<%# GetContent(Eval("Content0").ToString(), Eval("Content1").ToString())%><%--<%# Eval("Content1"%>--%></div>
            
<div class="textbox-bottom">
                
<asp:PlaceHolder ID="actionLinkPanel" runat="server">
<href="articleManage.aspx?act=edit&id=<%# Eval("ArticleID") %>" title="<%=lang["edit"]%>">
                    
<img src="/styles/default/images/icon_edit.gif" alt="<%=lang["edit"]%>
" />
                
</a><href="/articleManage.aspx?act=delete&id=<%# Eval("ArticleID") %>" title="<%=lang["delete"]%>
">
                    
<img src="/styles/default/images/icon_del.gif" alt="<%=lang["delete"]%>" /></a>
 | 
                
</asp:PlaceHolder>

<onclick="<%# urlFormats.ArticleCategoryUrl(Convert.ToInt32(Eval("categoryID").ToString()))%>" href="#"><%=lang["category"]%>:<%Eval("CategoryName")%></a> | <href="?id=<%# Eval("ArticleID") %>"><%=lang["permalink"]%></a> | <onclick="<%# urlFormats.ArticleConent(Convert.ToInt32(Eval("ArticleID").ToString()))%>" href="#commentbox" title="评论"><%=lang["comments"]%>:<%Eval("CommentCount"%></a> | <href="#"><%=lang["trackbacks"]%>:<%Eval("TrackbackCount"%></a> | <%=lang["views"]%>:<%Eval("ViewCount"%>
            
</div>
        
</div>
    
</ItemTemplate>
</asp:Repeater>

很好的完成了工作,通过数据绑定语法。可这界面太烦,字段多,金黄色语法就更多,那个心里烦啊,我们还是无法逃避这个问题,而且我们偶尔还会对一些字段进行逻辑判断。我的做法:用Literal控件代替绑定语法,在ItemDataBound事件中完成数据的填充。现在前端
<asp:Repeater ID="normalArticleList" runat="server">
<ItemTemplate>
<div class="textbox">
    
<div class="textbox-title">
        
<asp:Literal ID="articleTitle" runat="server"></asp:Literal>
        
<div class="textbox-label">
            
<asp:Literal ID="articleLabel" runat="server"></asp:Literal>
        
</div>
    
</div>
    
<div class="textbox-content">
        
<asp:Literal ID="articleContent" runat="server"></asp:Literal>
    
</div>
    
<div class="textbox-bottom">
    
<asp:Literal ID="articleBottom" runat="server"></asp:Literal>
    
</div>
</div>
</ItemTemplate>
</asp:Repeater>

后端
        void normalArticleList_ItemDataBound(object sender, RepeaterItemEventArgs e)
        
{
            
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
            
{
                Article item 
= (Article)e.Item.DataItem;
              
                Literal title 
= (Literal)e.Item.FindControl("articleTitle");
                Literal articleLabel 
= (Literal)e.Item.FindControl("articleLabel");
                Literal articleContent 
= (Literal)e.Item.FindControl("articleContent");
                Literal articleBottom 
= (Literal)e.Item.FindControl("articleBottom");
                
                title.Text 
= RenderNormalTitle(item);
                articleLabel.Text 
= RenderNormalLabel(item);
                articleContent.Text 
= RenderNormalContent(item);
                articleBottom.Text 
= RenderNormalBottom(item);
                
            }

        }

这样的写法会漂亮很多,而且数据绑定是通过反射机制的,听过会损失性能。。。

写了5点了,打住。不好意思,赖,没提供什么代码给大家下载,希望对大家有帮助。祝:元旦快乐,工作顺利。