前言
上周的时候,做了一点单页应用的研究,但是给断了,本来说最近继续的,但是最近有点其它事情给耽搁了,就给忘了。
PS:其实是师傅(http://www.cnblogs.com/aaronjs/)叫我读jquery源码,我给读跪了。。。
最近两天又朋友问怎么还没写,所以有了今天的东西。
这个单页应用的框架问题很多,就是个简单的demo,喜欢的朋友就看看吧,需要源码的可以下载,但是里面的图片资源文件我给删除了,需要的就留言吧。
这次代码增加了本地存储的应用,将model搞出来了,但是不太完善,过段时间再更新吧。
关于博客园
我想说博客园的接口很难用吗???而且全部是xml的,我这里还写了一个后端程序作为转换呢:
1 <%@ WebHandler Language="C#" Class="Handler" %> 2 3 using System; 4 using System.Web; 5 using System.Net; 6 using System.IO; 7 using System.Text; 8 using System.Collections; 9 using System.Xml; 10 11 12 13 public class Handler : IHttpHandler { 14 15 public void ProcessRequest (HttpContext context) { 16 context.Response.ContentType = "text/plain"; 17 string url = context.Request["url"] != null ? context.Request["url"].ToString() : ""; 18 string sException = null; 19 string sRslt = null; 20 WebResponse oWebRps = null; 21 WebRequest oWebRqst = WebRequest.Create(url); 22 oWebRqst.Timeout = 50000; 23 try 24 { 25 oWebRps = oWebRqst.GetResponse(); 26 } 27 catch (WebException ex) 28 { 29 sException = ex.Message.ToString(); 30 context.Response.Write(sException); 31 } 32 finally 33 { 34 if (oWebRps != null) 35 { 36 StreamReader oStreamRd = new StreamReader(oWebRps.GetResponseStream(), Encoding.GetEncoding("utf-8")); 37 sRslt = oStreamRd.ReadToEnd(); 38 oStreamRd.Close(); 39 oWebRps.Close(); 40 } 41 } 42 43 XmlDocument doc = new XmlDocument(); 44 45 doc.InnerXml = sRslt; 46 // Convert XML to a JSON string 47 string JSON = XmlToJSON(doc); 48 49 // Replace \ with \\ because string is being decoded twice 50 //JSON = JSON.Replace(@"\", @"\\"); 51 52 context.Response.Write(JSON); 53 54 } 55 private static string XmlToJSON(XmlDocument xmlDoc) 56 { 57 StringBuilder sbJSON = new StringBuilder(); 58 sbJSON.Append("{ "); 59 XmlToJSONnode(sbJSON, xmlDoc.DocumentElement, true); 60 sbJSON.Append("}"); 61 return sbJSON.ToString(); 62 } 63 64 // XmlToJSONnode: Output an XmlElement, possibly as part of a higher array 65 private static void XmlToJSONnode(StringBuilder sbJSON, XmlElement node, bool showNodeName) 66 { 67 if (showNodeName) 68 sbJSON.Append("\"" + SafeJSON(node.Name) + "\": "); 69 sbJSON.Append("{"); 70 // Build a sorted list of key-value pairs 71 // where key is case-sensitive nodeName 72 // value is an ArrayList of string or XmlElement 73 // so that we know whether the nodeName is an array or not. 74 SortedList childNodeNames = new SortedList(); 75 76 // Add in all node attributes 77 if (node.Attributes != null) 78 foreach (XmlAttribute attr in node.Attributes) 79 StoreChildNode(childNodeNames, attr.Name, attr.InnerText); 80 81 // Add in all nodes 82 foreach (XmlNode cnode in node.ChildNodes) 83 { 84 if (cnode is XmlText) 85 StoreChildNode(childNodeNames, "value", cnode.InnerText); 86 else if (cnode is XmlElement) 87 StoreChildNode(childNodeNames, cnode.Name, cnode); 88 } 89 90 // Now output all stored info 91 foreach (string childname in childNodeNames.Keys) 92 { 93 ArrayList alChild = (ArrayList)childNodeNames[childname]; 94 if (alChild.Count == 1) 95 OutputNode(childname, alChild[0], sbJSON, true); 96 else 97 { 98 sbJSON.Append(" \"" + SafeJSON(childname) + "\": [ "); 99 foreach (object Child in alChild) 100 OutputNode(childname, Child, sbJSON, false); 101 sbJSON.Remove(sbJSON.Length - 2, 2); 102 sbJSON.Append(" ], "); 103 } 104 } 105 sbJSON.Remove(sbJSON.Length - 2, 2); 106 sbJSON.Append(" }"); 107 } 108 109 // StoreChildNode: Store data associated with each nodeName 110 // so that we know whether the nodeName is an array or not. 111 private static void StoreChildNode(SortedList childNodeNames, string nodeName, object nodeValue) 112 { 113 // Pre-process contraction of XmlElement-s 114 if (nodeValue is XmlElement) 115 { 116 // Convert <aa></aa> into "aa":null 117 // <aa>xx</aa> into "aa":"xx" 118 XmlNode cnode = (XmlNode)nodeValue; 119 if (cnode.Attributes.Count == 0) 120 { 121 XmlNodeList children = cnode.ChildNodes; 122 if (children.Count == 0) 123 nodeValue = null; 124 else if (children.Count == 1 && (children[0] is XmlText)) 125 nodeValue = ((XmlText)(children[0])).InnerText; 126 } 127 } 128 // Add nodeValue to ArrayList associated with each nodeName 129 // If nodeName doesn't exist then add it 130 object oValuesAL = childNodeNames[nodeName]; 131 ArrayList ValuesAL; 132 if (oValuesAL == null) 133 { 134 ValuesAL = new ArrayList(); 135 childNodeNames[nodeName] = ValuesAL; 136 } 137 else 138 ValuesAL = (ArrayList)oValuesAL; 139 ValuesAL.Add(nodeValue); 140 } 141 142 private static void OutputNode(string childname, object alChild, StringBuilder sbJSON, bool showNodeName) 143 { 144 if (alChild == null) 145 { 146 if (showNodeName) 147 sbJSON.Append("\"" + SafeJSON(childname) + "\": "); 148 sbJSON.Append("null"); 149 } 150 else if (alChild is string) 151 { 152 if (showNodeName) 153 sbJSON.Append("\"" + SafeJSON(childname) + "\": "); 154 string sChild = (string)alChild; 155 sChild = sChild.Trim(); 156 sbJSON.Append("\"" + SafeJSON(sChild) + "\""); 157 } 158 else 159 XmlToJSONnode(sbJSON, (XmlElement)alChild, showNodeName); 160 sbJSON.Append(", "); 161 } 162 163 // Make a string safe for JSON 164 private static string SafeJSON(string sIn) 165 { 166 StringBuilder sbOut = new StringBuilder(sIn.Length); 167 foreach (char ch in sIn) 168 { 169 if (Char.IsControl(ch) || ch == '\'') 170 { 171 int ich = (int)ch; 172 sbOut.Append(@"\u" + ich.ToString("x4")); 173 continue; 174 } 175 else if (ch == '\"' || ch == '\\' || ch == '/') 176 { 177 sbOut.Append('\\'); 178 } 179 sbOut.Append(ch); 180 } 181 return sbOut.ToString(); 182 } 183 public bool IsReusable { 184 get { 185 return false; 186 } 187 } 188 189 }
因为博客园没有手机端,所以简单做了一个,真的非常简单啊。。。
多上一张图:
功能一览
其实说白了就只有一个博客页与博客详情页了
博客页
其中滚动会分页:我们来看看我们的数据吧:
核心代码:
1 define(['$', '_', 'cBase', 'cView', getViewPath('index'), getViewPath('index_item'), 'blogModel', 'blogStore'], function ($, _, b, v, html, itemTpt, model, store) { 2 var getPageScrollPos = function () { 3 var left = Math.max(document.documentElement.scrollLeft, document.body.scrollLeft), 4 top = Math.max(document.documentElement.scrollTop, document.body.scrollTop), 5 height = Math.min(document.documentElement.clientHeight, document.body.clientHeight), 6 width = Math.min(document.documentElement.clientWidth, document.body.clientWidth), 7 pageWidth = Math.max(document.documentElement.scrollWidth, document.body.scrollWidth), 8 pageHeight = Math.max(document.documentElement.scrollHeight, document.body.scrollHeight); 9 return { 10 top: top, 11 left: left, 12 height: height, 13 width: width, 14 pageWidth: pageWidth, 15 pageHeight: pageHeight 16 }; 17 }; 18 var model = model.blogList.getInstance(); 19 var blogStore = store.blog.getInstance(); 20 21 var pageSize = 10; 22 var curpage = 1; 23 var isLoading = false; //正在加载 24 var blogs = {}; 25 26 27 var View = b.Class(v.PageView, { 28 _propertys_: function () { 29 }, 30 init: function (superInit, request, interface) { 31 superInit(request, interface); 32 console.log('init'); 33 }, 34 createHtml: function () { 35 return html; 36 }, 37 attrs: { 38 'data-id': 'test', 39 className: 'yexiaoc' 40 }, 41 events: { 42 '.orderItem,click': function (el) { 43 var id = el.attr('data-id'); 44 var blog = blogs[id]; 45 blogStore.set(blog); 46 this.forward('detail'); 47 this._unbindScroll(); 48 var s = ''; 49 } 50 }, 51 onCreate: function () { 52 console.log('onCreate'); 53 this.tpl = _.template(itemTpt); 54 this._loadData(); 55 }, 56 //dom创建后数据加载时执行,用于加载后执行我们的逻辑 57 onLoad: function () { 58 console.log('onLoad'); 59 60 var scope = this; 61 $(window).bind('scroll', function () { 62 scope._onWindowScroll.call(scope) 63 }); 64 65 }, 66 _loadData: function (onSuc, onError) { 67 var tpl = this.tpl; 68 var list = this.find('#lstbox'); 69 var param = {}; 70 param.url = 'http://wcf.open.cnblogs.com/blog/sitehome/paged/' + curpage + '/' + pageSize; 71 model.setParam(param); 72 var scope = this; 73 isLoading = true; 74 model.execute(function (data) { 75 var listData = data.feed && (data.feed.entry || []); 76 if (listData) { 77 if (listData.length == 0) { 78 list.html('<li>暂无数据</li>'); 79 } else { 80 $.each(listData, function (i, item) { 81 blogs[item.id] = item; //将博客存储 82 var htm = tpl(item) || ''; 83 list.append(htm); 84 }); 85 } 86 (typeof onSuc === 'function') && (onSuc.call(scope, data)); 87 } 88 isLoading = false; 89 }); 90 }, 91 _onWindowScroll: function () { 92 var pos = getPageScrollPos(); 93 if (pos.pageHeight - (pos.top + pos.height) < 500 && !isLoading) { 94 curpage++; 95 this._loadData(); 96 } 97 }, 98 _unbindScroll: function () { 99 $(window).unbind('scroll'); 100 }, 101 //dom创建后,未显示 102 onShow: function () { 103 104 console.log('onShow'); 105 }, 106 //dom隐藏前 107 onHide: function () { 108 console.log('onHide'); 109 } 110 }); 111 112 return View; 113 });
对应两个模板页
1 <input class="login" type="button" value="点击登录" /> 2 <header> 3 <b class="icon_home i_bef" id="js_home"></b> 4 <h1> 5 博客园</h1> 6 <i id="js_return" class="returnico"></i> 7 </header> 8 <section class="cont_wrap" style="margin: 17px 0 40px;"> 9 <ul class="pro_list" id="lstbox"> 10 </ul> 11 </section> 12 <ul class="tab_search fix_bottom"> 13 <li class="tabcrt">博客</li> 14 <li class="tab_hotel ">新闻</li> 15 <li class="tab_hotel ">48小时</li> 16 <li class="tab_hotel ">推荐</li> 17 </ul>
1 <li class="arr_r orderItem" data-id="<%=id %>"> 2 <article class="blog_item"> 3 <h3> 4 <a href="<%=link.href %>" target="_blank"><%=title.value || '无题' %></a> 5 </h3> 6 7 <div class="author pro_list_rank"> 8 <%if(author.avatar){ %> 9 <a href="<%=author.uri %>" target="_blank"> 10 <img src="<%=author.avatar %>"> 11 </a> 12 <%} %> 13 JavaScript 是根据 "ECMAScript"标准制定的网页脚本语言。这个标准由 ECMA 组织发展和维护。ECMA-262 是正式的 JavaScript标准。JavaScript是目前Web客户端开发的主要编程语言,也是Ajax的核心技术之一。 14 </div> 15 <div class="item_footer"> 16 <a href="<%=author.uri %>" class="lightblue">Scut</a> <%=published %> <a href="<%=link.href %>" title="2013-08-21 15:21" class="gray">评论(<%=comments %>)</a> 17 <a href="<%=link.href %>" class="gray">阅读(<%=views %>)</a> <span class="price1">推荐(<%=diggs %>)</span></div> 18 </article> 19 </li>
然后就是详情页了
详情页
1 define(['$', '_', 'cBase', 'cView', getViewPath('detail'), getViewPath('detail_article'), 'blogModel', 'blogStore'], function ($, _, b, v, html, itemTpt, model, store) { 2 3 var model = model.blog.getInstance(); 4 var blogStore = store.blog.getInstance(); 5 var tpl = _.template(itemTpt); 6 7 var View = b.Class(v.PageView, { 8 _propertys_: function () { 9 }, 10 init: function (superInit, request, interface) { 11 superInit(request, interface); 12 console.log('init'); 13 }, 14 createHtml: function () { 15 return html; 16 }, 17 attrs: { 18 }, 19 events: { 20 '#js_return,click': function () { 21 this.forward('index'); 22 } 23 }, 24 onCreate: function () { 25 console.log('onCreate'); 26 27 }, 28 _loadData: function () { 29 var box = this.find('.cont_wrap'); 30 box.html(''); 31 var param = { url: 'http://wcf.open.cnblogs.com/blog/post/body/' + blogStore.get().id } 32 $.get('Handler.ashx', param, function (data) { 33 (typeof data === 'string') && (data = $.parseJSON(data)); 34 data && data.string && (blogStore.setAttr('value', data.string.value)); 35 var d = blogStore.get(); 36 box.append(tpl(d)); 37 var ss = ''; 38 }); 39 }, 40 //dom创建后数据加载时执行,用于加载后执行我们的逻辑 41 onLoad: function () { 42 console.log('onLoad'); 43 }, 44 //dom创建后,未显示 45 onShow: function () { 46 console.log('onShow'); 47 this._loadData(); 48 49 }, 50 //dom隐藏前 51 onHide: function () { 52 console.log('onHide'); 53 } 54 }); 55 56 return View; 57 });
其中模板各位自己下载看吧。
结语
之前已经详细描述了整个代码逻辑这次就不说了,各位觉得不错可以下载下来看看,后面点还会更新。
源码
http://files.cnblogs.com/yexiaochai/page.zip(page.zip)