一起来单页应用吧,实现简单微博功能!(下)
前言
开始之前先扯点其它的,我上午去理发,居然才7元,但是那个阿妈给我搞成和尚的造型了,我想说点什么,但转念一想节约点洗头水也好!!!
PS:其实主要原因是我掉头发。。。。。。
好了,接昨天的内容:【单页应用】一起来单页应用吧,实现简单微博功能!(上)
昨天我们一起准备了两大核心,继承与view,虽说有问题,而且必定有问题,但是我们暂时不管他。
我们先继续先往下面写,到最后整体流程拉通后再一起优化就好,现在去纠结也不一定有结果,于是我们开始吧。
继承之实现APP
我们这里APP要干的事情,与其说担任MVC中控制器的角色,不如说他就是充当了一下路由选择的角色,根据不同的URL导向不同的view,并且会管理hash。
由于我们会处理request请求,压入hash以达到后退功能有效,所以这里先来实现一个hash类吧
实现Hash对象
先来一个辅助函数,用于计算某个字符在数组的位置:
1 var indexOf = function (k, arr) { 2 if (!arr) { 3 return -1; 4 } 5 //若是对象本身便居然indexof,便使用自身的,比如字符串 6 if (arr.indexOf) { 7 return arr.indexOf(k); 8 } 9 for (var i = 0, len = arr.length; i < len; i++) { 10 if (arr[i] == k) { 11 return i; 12 } 13 } 14 return -1; 15 };
PS:这个hash的实现不算太好,后面也许会改动
1 b.Hash = b.Class({ 2 _propertys_: function () { 3 this.keys = []; 4 this.values = []; 5 }, 6 init: function (obj) { 7 (typeof obj == 'object') || (obj = {}); //??? 8 for (var k in obj) { 9 if (obj.hasOwnProperty(k)) { 10 this.keys.push(k); 11 this.values.push(obj[k]); 12 } 13 } 14 }, 15 length: function () { 16 return this.keys.length; 17 }, 18 getItem: function (k) { 19 var index = indexOf(k, this.keys); 20 if (index < 0) { 21 return null; 22 } 23 return this.keys[index]; 24 }, 25 getKey: function (i) { 26 return this.keys[i]; 27 }, 28 getValue: function (i) { 29 return this.values[i]; 30 }, 31 add: function (k, v) { 32 return this.push(k, v); 33 }, 34 del: function (k) { 35 var index = indexOf(k, this.keys); 36 return this.delByIndex(index); 37 }, 38 delByIndex: function (index) { 39 if (index < 0) return this; 40 this.keys.splice(index, 1); 41 this.vaules.splice(index, 1); 42 return this; 43 }, 44 //移除栈顶hash,并返回 45 pop: function () { 46 if (!this.keys.length) return null; 47 this.keys.pop(); 48 return this.values.pop(); 49 }, 50 push: function (k, v, order) { 51 if (typeof k == 'object' && !v) { 52 for (var i in k) { 53 if (k.hasOwnProperty(i)) { 54 this.push(i, k[i], order); 55 } 56 } 57 } else { 58 var index = indexOf(k, this.keys); 59 if (index < 0 || order) { 60 if (order) this.del(k); 61 this.keys.push[k]; 62 this.values.push[v]; 63 } else { 64 this.values[index] = v; 65 } 66 } 67 }, 68 //查找hash表,返回key 69 indexOf: function (v) { 70 var index = indexOf(v, this.vaules); 71 if (index >= 0) { 72 return this.keys[index]; 73 } 74 return -1; 75 }, 76 each: function (handler) { 77 if (typeof handler == 'function') { 78 for (var i = 0, len = this.length(); i < len; i++) { 79 handler.call(this, this.keys[i], this.vaules[i]); 80 } 81 } 82 }, 83 getObj: function () { 84 var obj = {}; 85 for (var i = 0, len = this.length(); i < len; i++) { 86 obj[this.keys[i]] = this.values[i]; 87 } 88 return obj; 89 } 90 });
此hash对象基本就是数组的写照,各位可以对照着看,于是我们继续我们的app
app雏形
1 var Application = new b.Class({ 2 _propertys_: function () { 3 var scope = this; 4 this.webRoot = ''; //应用跟目录 5 this.head = $('head'); 6 this.body = $('body'); 7 this.viewRoot = 'views/'; //视图所在目录 8 this.defaultView = 'index'; //默认加载视图 9 10 this.request; //请求对象 11 this.viewPath; //当前请求视图路径,解析request得出 12 this.mainFrame; //主框架 13 this.viewPort; //视图框架 14 this.stateDom; //状态栏 15 16 this.views = new b.Hash(); //views保存浏览器存储的hash 17 this.curView; //当前视图 18 this.interface = {}; //提供给视图访问的接口,暂时不管 19 this.history = []; //历史记录 20 21 // this.stopListening = false;//是否开启监听 22 23 this.onHashChange = function () { 24 scope.history.push(window.location.href); 25 var url = decodeURIComponent(window.location.hash.replace(/^#+/i, '')).toLowerCase(); 26 scope._onHashChange(url); 27 }; 28 29 this.lastHash = ''; 30 this.lastFullHash = ''; 31 this.isChangeHash = false; //hash是否发生变化 32 }, 33 init: function (opts) { 34 //为属性赋值 35 opts = opts || {}; 36 for (var k in opts) { 37 this[k] = opts[k]; 38 } 39 this.createViewPort(); 40 this.bindEvent(); //事件绑定 41 }, 42 43 //创建app页面基本框架,此处不能使用id,因为。。。 44 createViewPort: function () { 45 var htm = [ 46 '<div class="main-frame">', 47 '<div class="main-viewport"></div>', 48 '<div class="main-state"></div>', 49 '</div>' 50 ].join(''); 51 this.mainframe = $(htm); 52 this.viewport = this.mainframe.find('.main-viewport'); 53 this.statedom = this.mainframe.find('.main-state'); 54 var body = $('body'); 55 body.html(''); 56 body.append(this.mainframe); 57 }, 58 //!!!!!!非常重要哦!!!!!! 59 bindEvent: function () { 60 var scope = this; 61 //暂时不使用requireJS 62 // requirejs.onError = function () {}; 63 $(window).bind('hashchange', this.onHashChange); 64 }, 65 _onHashChange: function (url) { 66 url = url.replace(/^#+/i, ''); 67 var req = this.parseHash(url); 68 69 this.request = req; 70 this.viewPath = this.viewPath || this.defaultView; 71 this.loadView(this.viewPath); //!!!重要的视图加载 72 }, 73 //该方法慢慢看吧。。。 74 parseHash: function (hash) { 75 var fullhash = hash, 76 hash = hash.replace(/([^|]*)(?:|.*)?$/img, '$1'), 77 h = /^([^?&|]*)(.*)?$/i.exec(hash), 78 vp = h[1] ? h[1].split('!') : [], 79 viewpath = (vp.shift() || '').replace(/(^/+|/+$)/i, ''), 80 path = vp.length ? vp.join('!').replace(/(^/+|/+$)/i, '').split('/') : [], 81 q = (h[2] || '').replace(/^?*/i, '').split('&'), 82 query = {}, y; 83 this.isChangeHash = !!(!this.lastHash && fullhash === this.lashFullHash) || !!(this.lastHash && this.lastHash !== hash); 84 if (q) { 85 for (var i = 0; i < q.length; i++) { 86 if (q[i]) { 87 y = q[i].split('='); 88 y[1] ? (query[y[0]] = y[1]) : (query[y[0]] = true); 89 } 90 } 91 } 92 93 this.lastHash = hash; 94 this.lashFullHash = fullhash; 95 return { 96 viewpath: viewpath, 97 path: path, 98 query: query, 99 root: location.pathname + location.search 100 }; 101 }, 102 //!!!非常重要 103 loadView: function (viewPath) { 104 var id = viewPath; 105 var scope = this; 106 //此处本来应该判断是否已经有该视图,但是我们暂时不管,我们只要加载了相关视图就算成功 107 /* 108 一些操作 109 */ 110 111 //此处应该加载我们的js文件 112 $.getScript(this.buildUrl(viewPath), function () { 113 var view = new PageView(); 114 view.show(); 115 scope.viewport.append(curView.$el); 116 var s = ''; 117 }); 118 //!!!暂时不使用requireJS 119 // var self = this; 120 // requirejs([this.buildUrl(path)], function (View) { 121 // callback && callback.call(self, View); 122 // }); 123 }, 124 buildUrl: function (path) { 125 return this.viewRoot = path; 126 } 127 });
好了,至此,我们粗制滥造版app结束,我们来试试先,再一并讲解其主要流程。
简单测试
html代码:
1 <!DOCTYPE html> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <meta charset="utf-8" /> 5 <title></title> 6 <script src="res/libs/jquery.js" type="text/javascript"></script> 7 <script src="res/test/c.base.js" type="text/javascript"></script> 8 </head> 9 <body> 10 </body> 11 <script src="res/test/app.js" type="text/javascript"></script> 12 <script type="text/javascript"> 13 var app = new Application(); 14 15 </script> 16 </html>
base代码:
app代码:
文件结构:
测试结果
我可耻的觉得自己成功了一半了。。。
基本流程讲解
app是我们整个框架的核心,我们来简单讲解一下:
① 在整个_propertys_函数中,定义了我们app会用到的一些实例属性、方法
② init方法(一定会执行,因为我们使用了c.base的方法创建类),他主要干了两件事:
创建基本dom结构;
绑定事件,这个绑定事件就非常重要了
③ 59行开始,便是该文件的核心之一,这里为window绑定了hashchange事件,于是hash一旦改变(以#号方式),那么就会触发onhashchange事件
④ 触发hashchange事件后,会获得请求url#后面的参数,根据约定,他就是我们请求的view的路径,所以开始执行loadView方法,开始加载视图
⑤ 102行,开始加载视图,这里本应该使用requireJS,但是这里便于讲解,便于各位理解,我们暂时没有使用,于是动态加载我们的index文件,在成功后实例化view并且将view装入我们的视口中。
⑥ 整体流程结束
于是我们的粗制滥造版本也结束了。
阶段总结
好了,我们现在回过头来看看我们整个框架现在变成什么样了。
① 我们拥有实现类的通用b.class
② 我们有了我们的抽象视图类
③ 我们有了我们的control application
假使以上流程都流程问题不大,那么我们整个功能其实就七七八八了,因为还差的model可以轻松补上。
但是明显我们现在的APP是有缺陷的,而且缺陷比较大,比如我们的hash相关其实根本没有用上,所以现在我们来完善他们吧!
RequireJS组织代码
在这里,我们便开始使用我们的RequireJS了,然后将我们的整个逻辑慢慢铺开,首先我将我们的文件目录分开。
至此我们的代码便完全分离开了,接下来我们来细化我们的app文件。
细化app
待续......
结语
写了几个小时了,有点累,休息下,我们晚上在继续