开发者社区> 范大脚脚> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

【单页应用】一起来单页应用吧,实现简单微博功能!(总结)

简介:
+关注继续查看
前言

本来是说周末研究一下单页应用来着,先是想发出来可以监督自己学习下去,结果谁知道这个水很深,我连着就赔了一个周末进去,到尽头还没搞完。。。

这次这个框架并不是我一个字写出来的,而是参考了同事的框架,我师傅(http://www.cnblogs.com/aaronjs/)说得好:

传承总比再造好,于是便有了这次的demo,其实就是吸收精华啊!!!

到今天有了一个大概的样子,可以拿出来看看了。其中的Model与localstorege相关也加上了,但是暂时没有用得到的场景。

这个代码后面点应该会持续更新,到我认为比较合适的阶段就会放出来了。。。。 于是我们继续吧!

整体思路

我们先来构思下整个框架的构成(这里参照了同事的框架):

依赖库

① requireJS

② jquery

③ underscore.js

PS:本来还想用backbone的,想下还是算了,研究性目的,不要搞的过于复杂吧

程序流程

基本结构我是这么思考的:

① 我们在网页中输入URL后便进入Index,以requireJS加载主框架APP

② APP.js做一些初始化操作,其主要功能便是观察hash变化(URL变化)

若是改变(初始化进入、或者想去其他页面),便根据规则获取URL相关信息,初始化新的VIEW,进行显示,并将URL压人HASH。

③ 实现VIEW类,其具有基本几个事件(onCreate、onLoad、onRender、onHide)

一个VIEW可以对应其HTML模板(可以0个或者多个)

④ 事件操作使用jquery的吧

PS:暂时这个样子吧,我们边写边改,看最后能做到什么样子,这里还是画个图



于是,我们开始辛苦的基础实现吧,毕竟我们现在还神马都米有!!!

基础实现之继承

其实,我们是面向对象的,若是不面向对象应该会被喷吧,于是我们来一起完成一个基础的继承类吧:

复制代码
 1 var b = {};//base
 2 var slice = [].slice;
 3 
 4 b.Class = function (supClass, childAttr) {
 5     //若是传了第一个类,便继承之;否则实现新类
 6     if (typeof supClass === 'object') {
 7         childAttr = supClass;
 8         supClass = function () { };
 9     }
10 
11     //定义我们创建的类12     var newClass = function () {
13         this._propertys_();
14         this.init.apply(this, arguments);
15     };
16     newClass.prototype = new supClass();
17 
18     var supInit = newClass.prototype.init || function () { };
19     var childInit = childAttr.init || function () { };
20     var _supAttr = newClass.prototype._propertys_ || function () { };
21     var _childAttr = childAttr._propertys_ || function () { };
22 
23     for (var k in childAttr) {
24         //_propertys_中作为私有属性
25         childAttr.hasOwnProperty(k) && (newClass.prototype[k] = childAttr[k]);
26     }
27 
28     //继承的属性有可能重写init方法
29     if (arguments.length && arguments[0].prototype && arguments[0].prototype.init === supInit) {
30         //重写新建类,初始化方法,传入其继承类的init方法
31         newClass.prototype.init = function () {
32             var scope = this;
33             var args = [function () {
34                 supInit.apply(scope, arguments);
35             } ];
36             childInit.apply(scope, args.concat(slice.call(arguments)));
37         };
38     }
39 
40     //内部属性赋值
41     newClass.prototype._propertys_ = function () {
42         _supAttr.call(this);
43         _childAttr.call(this);
44     };
45 
46     //成员属性
47     for (var k in supClass) {
48         supClass.hasOwnProperty(k) && (newClass[k] = supClass[k]);
49     }
50     return newClass;
51 };
复制代码
代码还是很好理解的:

PS:我们这个框架建立的所有类,都会经过他!!!

可以传递两个两个参数:需要继承的类,新建类的一些参数,这里我们来稍微走一下这个流程:

一个参数

① 我们先初始化一个新的类newClass,并且实例化他,他就会调用本身的两个方法:

this._propertys_() 用于实例化本身属性 this.init.apply(this, arguments) init为我们定义的,每个类都会调用的初始化方法

② 让我们新建的类(newClass)继承自我们传入的类

③ 初始化四个变量,显然这种情况他们都是空函数

④ 将传入的子属性的值给予新建类(newClass),这里只有一个参数便忽略吧

⑤ 29行,由于我们的类继承自supClass,所以这里是满足条件的,我们看看他里面干了些什么:

他重新定义了,我们创建类的init函数(我们说过他在自身属性初始化完毕后会执行)。

这里我们为他的参数新增了一个方法,也就是父类的init方法,只不过其this指向为子类。

复制代码
newClass.prototype.init = function () {
    var scope = this;
    var args = [function () {
        supInit.apply(scope, arguments);
    } ];
    childInit.apply(scope, args.concat(slice.call(arguments)));
};
复制代码
⑥ 41行,大家注意_propertys_这个函数,他是用于我们为自身属性赋值的,等下我们举个例子

⑦ 最后将父类的成员对象赋给子类(因为我们知道成员是不会被继承的)

⑧ 返回新建类,流程结束

情况我这里不多做介绍,我们再说下两个参数的情况就好。

两个参数

我们这种场景用的也很多,第一个参数为要继承的类,第二个参数为新建类的一些初始化信息,我们来走一次流程吧:

① 传入两个参数,要继承的类;新建类的一些属性和原型方法

② 12行同样初始化我们的新类,我们实例化时,首先会执行_propertys_方法为自身赋值,然后执行初始化方法(可以传递参数哦,参数是new时候传递的):

var newClass = function () {
        this._propertys_();
        this.init.apply(this, arguments);//此处的参数请注意
 };
③ 让新类继承自传入的类,此时我们的newClass.prototype其实就拥有很多方法了,包括init与_propertys_

④ 18-21行,初始化四个后面会用到的函数,各位自己注意其意图

⑤ 23-26行,将将传入的子属性对象,赋予newClass,_propertys_也作为原型对象了

⑥ 29-38行,将父类的init作为参数传递给子类的init,由子类决定是不是要执行

⑦ 最后的就不多说了

好了,到此各位应该了解他的作用了,我们来一个简单的例子吧:

简单例子

实现一个父类Bird(鸟类),及其子类Chicken(鸡)Duck(鸭)

首先,我们来看鸟类都能呼吸,都有名字与年龄,所以我们这么干:

复制代码
 1 var Bird = new b.Class({
 2     //作为自身属性将被调用,里面必须采用this.XX的方式书写
 3     _propertys_: function () {
 4         this.name = '鸟类';
 5         this.age = 0;
 6     },
 7     //一定会被执行的初始化方法
 8     init: function () {
 9         alert('一定会执行');
10     },
11     //原型方法
12     breathe: function () {
13         alert('我能呼吸');
14     }
15 });
16 
17 var b = new Bird({des: '测试init方法是否能捕获参数'});
18 
19 var s = '';
复制代码


这里我们的init自动执行了,并且,其会用到传入的参数!!!



这是我们得到的鸟类实例。

好了,我们来实例化一个鸡的类,并且他拥有嚎叫打鸣的本领,与性别的特征

复制代码
 1 var Chicken = new b.Class(Bird, {
 2     _propertys_: function () {
 3         this.sex = '公';
 4     },
 5     //一定会被执行的初始化方法
 6     init: function () {
 7         alert('我是一只鸡');
 8     },
 9     //原型方法
10     howl: function () {
11         alert('我能打鸣');
12     }
13 });
复制代码


这里,父类的init会执行,子类的init会执行,而且,子类的init函数会默认带上父类的init方法



init: function (superInit) {
    alert('我是一只鸡');
},
PS:虽说父类一定会执行,但是若是在此调用父类的superInit方法的话,this指向是子类哦!!!

好了,这个家伙结束,我们进入下一个核心VIEW

基础实现之视图

视图(View)是我们框架的第二个核心,我们来看看到底可能会有哪些view呢?

① 核心之,页面VIEW

这是最重要的view,他应该包含整个页面的逻辑,从初始化到最后的展示,事件绑定等应该一应俱全

② 各个级别组件,弹出层,提示层,遮盖层......

这些都应该是view,但是,先不管那么多,我们来实现我们的核心吧!!!

思考过程

此处使用backbone的视图到变得简单了,我们来简单思考下我们页面视图形成的流程吧:

① 用户第一次进入界面,访问index,由main导向app,app获得url,获取其相关参数(list)

② app根据list实例化view,view调用自身init方法,慢慢开始构建页面,并会触发各个流程

PS:在此我认为每个页面view应该统一继承一个父类,于是我们来尝试性试试吧

第一版

复制代码
  1 b.AbstractView = b.Class({
  2     //基本view应该具有的属性
  3     _propertys_: function () {
  4         this.id = (new Date()).getTime(); //唯一pageID
  5         this.rootBox = $('body'); //视图容器
  6         this.root = $('<div/>'); //视图的根元素,可进行设置
  7         this.header = null;
  8         this.footer = null;
  9         this.template = '';//可能的模板
 10         this.isCreated = false;//是否创建完毕
 11         this.status = b.AbstractView.STATE_NOTCREATE;//当前状态
 12     },
 13     init: function () {
 14     },
 15     //定义将要用到的事件,其中元素选取都会以root为标准,所以使用内部提供函数吧
 16     events: {
 17         'selector,eventType': 'func'
 18     },
 19     //默认属性
 20     attrs: {
 21     },
 22     //获取视图元素
 23     find: function (selector) {
 24         return this.root.find(selector);
 25     }, 
 26     //创建dom
 27     create: function (opts) {
 28         if(!this.isCreated && this.status != b.AbstractView.STATE_ONCREATE) {
 29             var attr = opts && opts.attr;
 30             var html = this.createHtml();
 31             this.initRoot(attr);//初始化root
 32             this.hide();
 33             this.rootBox.append(this.root);
 34             this.root.html(html);
 35             this.trigger('onCreate');//触发正在创建事件,其实这里都创建完了
 36             this.status = b.AbstractView.STATE_ONCREATE;
 37             this.isCreated = true;
 38             this.bindEvent();
 39         }
 40     },
 41     //呈现/渲染视图
 42     show: function (callback) {
 43         if(this.status == b.AbstractView.STATE_ONSHOW) {
 44             return;
 45         }
 46         this.create();
 47         this.root.show();
 48         this.trigger('onShow');
 49         this.status = b.AbstractView.STATE_ONSHOW
 50         callback && (typeof callback == 'function') && callback.call(this);
 51         this.trigger('onLoad');
 52     },
 53     //隐藏dom
 54     hide: function (callback) {
 55         if(!this.root || this.status == b.AbstractView.STATE_ONHIDE) {
 56             return;
 57         }
 58         this.root.hide();
 59         this.trigger('onHide');
 60         this.status = b.AbstractView.STATE_ONHIDE;
 61         callback && (typeof callback == 'function') && callback();
 62     },
 63     //事件绑定
 64     bindEvent: function () {
 65         var events = this.events;
 66         for(var k in events) {
 67             var sec_type = k.replace(/\s/i, '').split(',');
 68             var func = events[k];
 69             if(sec_type &&sec_type.length == 2 && typeof func == 'function') {
 70                 var selector = sec_type[0];
 71                 var type = sec_type[1];
 72                 var scope = this;
 73                 this.find(selector).on(type, function () {
 74                     func.call(scope, $(this));
 75                 })
 76             }
 77         }
 78     },
 79     //此处可以配合模板与相关参数组成html
 80     //解析模板也放到此处
 81     createHtml: function () {
 82         throw new Error('请重新定义createHtml方法');
 83     },
 84     initRoot: function () {
 85         var attr = this.attrs;
 86         if(!attr) {
 87             return;
 88         }
 89         for(var k in attr) {
 90             if(k == 'className') {
 91                 this.root.attr('class', attr[k]);
 92             }else {
 93                 this.root.attr(k, attr[k]);
 94             }
 95         }
 96        this.root.attr('id', this.id);
 97     },
 98     //触发事件
 99     trigger: function (k, args) {
100         var event = this[k];
101         args = args || [];
102         if(event && typeof event == 'function') {
103             event.apply(this, args)
104         }
105     },
106     setRootBox: function (dom) {
107         this.rootBox = dom;
108     },
109     setAttr: function (k, v) {
110         this.root.attr(k, v);
111     },
112     getAttr: function (k) {
113         return this.root.attr(k);
114     },
115     setCss: function (k, v) {
116         this.root.css(k, v);
117     },
118     getCss: function (k) {
119         return this.root.css(k);
120     },
121     //dom创建后执行
122     onCreate: function () {
123 
124     },
125     //dom创建后数据加载时执行,用于加载后执行我们的逻辑
126     onLoad: function () {
127 
128     },
129     //dom创建后,未显示
130     onShow: function () {
131 
132     },
133     //dom隐藏前
134     onHide: function () {
135 
136     }
137 });
138 
139 //组件状态,未创建
140 b.AbstractView.STATE_NOTCREATE = 'notCreate';
141 //组件状态,已创建但未显示
142 b.AbstractView.STATE_ONCREATE = 'onCreate';
143 //组件状态,已显示
144 b.AbstractView.STATE_ONSHOW = 'onShow';
145 //组件状态,已隐藏
146 b.AbstractView.STATE_ONHIDE = 'onHide';
复制代码
代码注释写的很详细了,我这里就不多说了,我们来用一个例子试一试:

复制代码
 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 
11 </body>
12 <script type="text/javascript">
13     var PageView = b.Class(b.AbstractView, {
14         _propertys_: function () {
15             this.template = '我是叶小钗';
16         },
17         init: function (superInit) {
18             console.log(superInit);
19             console.log('init');
20         },
21         createHtml: function () {
22             var htm = [
23             '<header>标题</header>',
24             '<div class="main">',
25                 '<input type="text" id="txt" />',
26                 '<input type="button" id="bt" value="点击我" />',
27                 this.template,
28             '</div>',
29             '<footer>页尾</footer>'
30             ].join('');
31             return htm;
32         },
33         attrs: {
34             'data-id': 'test',
35             className: 'yexiaoc'
36         },
37         events: {
38             '#bt,click': function (el) {
39                 var txt = this.find('#txt');
40                 alert(txt.val())
41             }
42         },
43         onCreate: function () {
44             console.log('onCreate');
45         },
46         //dom创建后数据加载时执行,用于加载后执行我们的逻辑
47         onLoad: function () {
48             console.log('onLoad');
49         },
50         //dom创建后,未显示
51         onShow: function () {
52             console.log('onShow');
53         },
54         //dom隐藏前
55         onHide: function () {
56             console.log('onHide');
57         }
58     });
59     var view = new PageView();
60     view.show();
61     var s = '';
62 </script>
63 </html>
复制代码


初步实现我们的期望

继承之实现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代码:

 base核心
app代码:

 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了,然后将我们的整个逻辑慢慢铺开,首先我将我们的文件目录分开。



 html页
 main
 app
 c
 c.base
 c.view
 index
至此我们的代码便完全分离开了,接下来我们来细化我们的app文件。

细化/清晰结构

现在来看看我们最后形成的结构:



我们现在来一层层剥离他。

唯一的一个页面demo

复制代码
 1 <!DOCTYPE html>
 2 <html xmlns="http://www.w3.org/1999/xhtml">
 3 <head>
 4     <meta charset="utf-8" />
 5     <title></title>
 6     <link rel="Stylesheet" type="text/css" href="res/style/main2.css" />
 7     <link rel="Stylesheet" type="text/css" href="res/style/tuan.css" />
 8 </head>
 9 <body>
10 </body>
11 <script src="app/libs/require.js" data-main="main" type="text/javascript"></script>
12 </html>
复制代码
入口函数main

复制代码
 1 window.BASEURL = '';
 2 window.VIEWS_PATH = 'views/';
 3 
 4 function getViewPath(path) {
 5     return 'text!' + VIEWS_PATH + path + '.html';
 6 }
 7 
 8 require.config({
 9     baseUrl: BASEURL,
10     shim: {
11         $: {
12             exports: 'jQuery'
13         },
14         _: {
15             exports: '_'
16         }
17     },
18     paths: {
19         '$': 'app/libs/jquery',
20         '_': 'app/libs/underscore',
21         'text': 'app/libs/require.text',
22 
23         'c': 'app/common/c',
24         'cBase': 'app/common/c.base',
25         'cStorage': 'app/common/c.storage',
26         'cStore': 'app/common/c.store',
27 
28         'cView': 'app/common/c.view',
29         'app': 'app/app',
30 
31 
32         's': 's'
33     }
34 });
35 
36 require(['app'], function (APP) {
37     new APP();
38 });
复制代码
核心base/view

 View Code
 View Code
路由/Control APP

 View Code
基本应用,视图与视图模板

复制代码
 1 define(['$', '_', 'cBase', 'cView', getViewPath('index')], function ($, _, b, v, html) {
 2     var View = b.Class(v.PageView, {
 3         _propertys_: function () {
 4             this.template = html;
 5             this.url = 'http://wcf.open.cnblogs.com/blog/sitehome/recent/10'; //获取首页文章
 6         },
 7         init: function (superInit, request, interface) {
 8             superInit(request, interface);
 9             console.log('init');
10         },
11         createHtml: function () {
12             return this.template;
13         },
14         attrs: {
15             'data-id': 'test',
16             className: 'yexiaoc'
17         },
18         events: {
19         },
20         onCreate: function () {
21             console.log('onCreate');
22         },
23         //dom创建后数据加载时执行,用于加载后执行我们的逻辑
24         onLoad: function () {
25             console.log('onLoad');
26             $.get(this.url, function (data) {
27 
28                 var s = '';
29             });
30         },
31         //dom创建后,未显示
32         onShow: function () {
33             console.log('onShow');
34         },
35         //dom隐藏前
36         onHide: function () {
37             console.log('onHide');
38         }
39     });
40 
41     return View;
42 });
复制代码
 View Code
整体代码如上,现在我们来利用他做一个简单的应用

小试牛刀

我们来重新定义下我们的index,用它看看能不能实现微博功能。

我们知道我们该操作inde.js的onload事件,以加载微博数据:

PS:微博接口好像有所变化,我原来的方法不能用啦。。。

 View Code
复制代码
 1 onLoad: function () {
 2     console.log('onLoad');
 3     $.getScript(this.url, function (data) {
 4         T.api("/statuses/home_timeline", null, "json", "GET")
 5         .success(function (addResponse, hometimelineResponse) {
 6             // 获取主页时间线数据
 7             var data = addResponse.data.info;
 8             var $timeline = $("#timeline");
 9             $.each(data, function (i, item) {
10                 $timeline.append("<li>" + item.origtext + "</li>");
11             });
12         }).error(function (code, message) {
13                 alert("操作失败," + message);
14         });
15     });
16 },
复制代码
先不管其他的,我们来看看我们获得了什么:

PS:我这里直接用死数据,懒得去研究了。。。。。。确实熬不住了



最后的实例view模板代码:(相信我,你不会想点开的!!!)

 View Code
结语

刚开始写感觉还不错,几个小时过去了,我感觉有点来不起了,暂时到这里吧,换个时间我们搞个更好的例子

 



本文转自叶小钗博客园博客,原文链接http://www.cnblogs.com/yexiaochai/p/3254111.html,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
HTML5 学习1.网页基本信息
HTML5 学习1.网页基本信息
11 0
台湾大学林轩田机器学习基石课程学习笔记11 -- Linear Models for Classification
台湾大学林轩田机器学习基石课程学习笔记11 -- Linear Models for Classification
33 0
CV之NS之VGG16:基于TF Slim(VGG16)利用七个不同的预训练模型实现快速NS风格
CV之NS之VGG16:基于TF Slim(VGG16)利用七个不同的预训练模型实现快速NS风格
131 0
STF简单修改实现安卓多机同屏控制
背景:STF是一个非常优秀的安卓真机管理平台,近期团队同学在看安卓真机适配方面的事情,想到如果采用STF来进行多机同屏控制那将是一件非常nice的事情。动手:初步看了一下STF的代码,前端主要是采用WebSocket与后端进行通信,那我们只要找到通信类,在他原来的send上面加上foreach,那么就完美了。简单尝试,周末用Ubuntu装了个开发环境,初步实现了多台安卓机的同屏控制,但有些细节方面
7683 0
HtmlUnit、httpclient、jsoup爬取网页信息并解析
1.爬取页面效果图 点击"百度一下"按钮前页面 点击"百度一下"按钮后页面 天涯社区登录页面 登录进去之后个人主页 二、具体实现代码 HtmlUnit(底层也是采用httpclient)和jsoup  API package com.yuanhai.test; import java.io.IOException; import java.net.Malformed
5427 0
+关注
文章
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载