前情回顾
根据之前的学习,我们形成了一个view与一个messageCenter
view这块来说又内建了一套mvc的东西,我们这里来理一下
首先View一层由三部分组成:
① view
② dataAdpter
③ viewController
view一块两个重要数据是模板以及对应data,一个状态机status
这里view只负责根据状态取出对应的模板,而后根据传入的数据返回组装好的html
这里一个view多种状态是什么意思呢?
比如我有一个组件,但是里面有一圈数据是需要Ajax请求的,所以我的view可能就分为两个状态了
init->ajaxSuccess 这样的话首次加载默认的dom结构,数据加载结束后便再次渲染
PS:这里再次渲染的时候暂时图方便是采用将整个DOM结构换掉的手法,虽然简单粗暴却不合适,这块后期优化
这里数据的变化不由view负责,负责他的是dataAdapter
dataAdpter属于一个独立的模块,可用与多个viewController,dataAdpter内部首先维护着一个观察者数组,
然后是两个关键的datamodel以及viewmodel
datamodel用于操作,viewmodel会根据datamodel生成最终,然后使用viewmodel进行页面render,这个就是传入的data
若是我某一个datamodel对象发生变化便会通知观察者们,然后对应的view就会得到更新,该过程的发生点控制于viewController
viewController是连接view与dataAdpter的枢纽
viewController必须具有view,却可以没有dataAdpter,因为不是所有view都需要data才能渲染
我们实际工作中的大量业务逻辑会在viewController中定义完成,然后viewController也分了几个事件点
① create 触发onViewBeforeCreate、onViewAfterCreate事件
② show会实际将dom结构转入并且显示出来 触发onViewBeforeShow、onViewAfterShow事件
show的时候会绑定相关事件,事件借鉴于Backbone事件机制,每次注册前会先移除
③ 而后便是hide事件,他会隐藏我们的dom却不会移除,对应会有onViewBeforeHide、onViewAfterHide
④ destroy事件,会移除dom结构,并且删除实例、释放自身资源
以上是主流功能,还有一些功能不一定常用,比如我们任务view隐藏后,其所有状态事件全部应该移除,在show时重新绑定
messageCenter
现在没有什么大问题,却有一个小隐忧,这个消息中心会全局分发,一旦注册后,在触发时皆会触发,这个就有一个问题
我有一个alert组件,我自己内部在初始化时候注册了一个onShow的事件,我在show的时候真正的执行之
这个看上去没有什么问题,但是以下场景会有不一样的感受
我一个页面上有两个alert实例的话,我调用其中一个的时候,另一个alert的onShow也会被触发,这个是我们不愿意看见的
换个例子,我们一个页面上有两个IScroll,我们如使用messageCenter的话,一个滑动结束触发对应键值事件,很有可能两边会同时被触发
所以,这些都是我们需要关注的问题
下面让我们来详细整理
View相关梳理
现在View相关的功能点还不完全成熟,主要纠结点在于modelView改变后,view应该作何反应
若是一小点数据的改变却会引起整个dom结构的重组,这一点也是致命的,
其次一个view不同的状态会组成不同的view,但是一个view组成的html应该有一个容器,此“容器”现阶段我们概念感不是很强
所谓容器,不过是有模板嵌套的场景,后加载出来的html需要放入之前的某一个位置
若是子模板改变只会改变对应部分的dom、若是主模板改变就只能全部dom重组了!!!
于是我们简单整理后的代码如下:
首先来看看我们的view
1 Dalmatian.View = _.inherit({
2
3 // @description 设置默认属性
4 _initialize: function () {
5
6 var DEFAULT_CONTAINER_TEMPLATE = '<div class="view"></div>';
7 var VIEW_ID = 'dalmatian-view-';
8
9 // @override
10 // @description template集合,根据status做template的map
11 // @example
12 /*
13 {
14 init: '<ul><%_.each(list, function(item){%><li><%=item.name%></li><%});%></ul>'//若是字符串表明全局性
15 ajaxLoading: 'loading',
16 ajaxSuc: 'success'
17 }
18 */
19 this.templateSet = {};
20
21 // @override
22 /*
23 ***这块我没有考虑清楚,一般情况下view是不需要在改变的,若是需要改变其实该设置到datamodel中***
24 这个可以考虑默认由viewController注入给dataModel,然后后面就可操作了......
25 这里的包裹器可能存在一定时序关系,这块后续再整理
26
27 与模板做映射关系,每个状态的模板对象可能对应一个容器,默认为根容器,后期可能会被修改
28 ajaxLoading: el,
29 ajaxSuc: selector
30 */
31 this.wrapperSet = {};
32
33 this.viewid = _.uniqueId(VIEW_ID);
34 this.currentStatus = null;
35 this.defaultContainer = DEFAULT_CONTAINER_TEMPLATE;
36 this.isNoWrapper = false;
37
38 //全局根元素
39 this.root = null;
40 //当前包裹器
41 this.curWrapper = null;
42 //当前模板对应解析后的html结构
43
44 },
45
46 _initRoot: function () {
47 //根据html生成的dom包装对象
48 //有一种场景是用户的view本身就是一个只有一个包裹器的结构,他不想要多余的包裹器
49 if (!this.isNoWrapper) {
50 this.root = $(this.defaultContainer);
51 this.root.attr('id', this.viewid);
52 }
53 },
54
55 // @description 构造函数入口
56 initialize: function (options) {
57 this._initialize();
58 this.handleOptions(options);
59 this._initRoot();
60
61 },
62
63 // @override
64 // @description 操作构造函数传入操作
65 handleOptions: function (options) {
66 // @description 从形参中获取key和value绑定在this上
67 // l_wang options可能不是纯净的对象,而是函数什么的,这样需要注意
68 if (_.isObject(options)) _.extend(this, options);
69
70 },
71
72 //处理包裹器,暂时不予理睬
73 _handleNoWrapper: function (html) {
74 //...不予理睬
75 },
76
77 //根据状态值获取当前包裹器
78 _getCurWrapper: function (status, data) {
79 //处理root不存在的情况
80 this._handleNoWrapper();
81
82 //若是以下逻辑无用,那么这个便是根元素
83 if (!data.wrapperSet || !data.wrapperSet[status]) { return this.root; }
84 if (_.isString(data.wrapperSet[status])) { return this.root.find(data.wrapperSet[status]); }
85
86 },
87
88 // @description 通过模板和数据渲染具体的View
89 // @param status {enum} View的状态参数
90 // @param data {object} 匹配View的数据格式的具体数据
91 // @param callback {functiion} 执行完成之后的回调
92 render: function (status, data, callback) {
93
94 var templateFn, wrapper;
95 var template = this.templateSet[status];
96
97 //默认将view中设置的默认wrapper注入值datamodel,datamodel会带入viewModel
98 wrapper = this._getCurWrapper(status, data);
99
100 if (!wrapper[0]) throw '包裹器参数错误';
101 if (!template) return false;
102
103 //解析当前状态模板,编译成函数
104 templateFn = Dalmatian.template(template);
105 wrapper.html(templateFn(data));
106 this.html = wrapper;
107
108 this.currentStatus = status;
109
110 _.callmethod(callback, this);
111 return true;
112
113 },
114
115 // @override
116 // @description 可以被复写,当status和data分别发生变化时候
117 // @param status {enum} view的状态值
118 // @param data {object} viewmodel的数据
119 update: function (status, data) {
120
121 if (!this.currentStatus || this.currentStatus !== status) {
122 return this.render(status, data);
123 }
124
125 // @override
126 // @description 可复写部分,当数据发生变化但是状态没有发生变化时,页面仅仅变化的可以是局部显示
127 // 可以通过获取this.html进行修改
128 _.callmethod(this.onUpdate, this);
129 }
130 });
view基本只负责根据模板和数据生成html字符串,有一个不同的点是他需要记录自己的根元素,这个对我们后续操作有帮助
其中比较关键的是templateSet以及wrapperSet,这里的wrapperSet会被注入给dataAdpter的datamodel,后期便于调整
然后是我们的Adapter
1 Dalmatian.Adapter = _.inherit({
2
3 // @description 构造函数入口
4 initialize: function (options) {
5 this._initialize();
6 this.handleOptions(options);
7 },
8
9 // @description 设置默认属性
10 _initialize: function () {
11 this.observers = [];
12 // this.viewmodel = {};
13 this.datamodel = {};
14 },
15
16 // @description 操作构造函数传入操作
17 handleOptions: function (options) {
18 // @description 从形参中获取key和value绑定在this上
19 if (_.isObject(options)) _.extend(this, options);
20 },
21
22 // @override
23 // @description 操作datamodel返回一个data对象形成viewmodel
24 format: function (datamodel) {
25 return datamodel;
26 },
27
28 getViewModel: function () {
29 return this.format(this.datamodel);
30 },
31
32 registerObserver: function (viewcontroller) {
33 // @description 检查队列中如果没有viewcontroller,从队列尾部推入
34 if (!_.contains(this.observers, viewcontroller)) {
35 this.observers.push(viewcontroller);
36 }
37 },
38
39 setStatus: function (status) {
40 _.each(this.observers, function (viewcontroller) {
41 if (_.isObject(viewcontroller))
42 viewcontroller.setViewStatus(status);
43 });
44 },
45
46 unregisterObserver: function (viewcontroller) {
47 // @description 从observers的队列中剔除viewcontroller
48 this.observers = _.without(this.observers, viewcontroller);
49 },
50
51 notifyDataChanged: function () {
52 // @description 通知所有注册的观察者被观察者的数据发生变化
53 // this.viewmodel = this.format(this.datamodel);
54 var data = this.getViewModel();
55 _.each(this.observers, function (viewcontroller) {
56 if (_.isObject(viewcontroller))
57 _.callmethod(viewcontroller.update, viewcontroller, [data]);
58 });
59 }
60 });
他只负责更新数据,并在数据变化时候通知ViewController处理变化,接下来就是我们的viewController了
View Code
这个控制器是连接view以及Adapter的桥梁,三者合一便可以处理一些问题,接下来看一个简单的demo
Ajax例子
View Code
这段代码的核心在此
1 //模拟Ajax请求
2 function getAjaxData(callback, data) {
3 setTimeout(function () {
4 if (!data) {
5 data = [];
6 for (var i = 0; i < 5; i++) {
7 data.push({ title: '我是标题_' + i });
8 }
9 }
10 callback(data);
11 }, 1000);
12 }
13
14 var AjaxView = _.inherit(Dalmatian.View, {
15 _initialize: function ($super) {
16 //设置默认属性
17 $super();
18
19 this.templateSet = {
20 init: $('#template-ajax-init').html(),
21 loading: $('#template-ajax-loading').html(),
22 ajaxSuc: $('#template-ajax-suc').html()
23 };
24
25 this.wrapperSet = {
26 loading: '.cui-error-tips',
27 ajaxSuc: '.cui-error-tips'
28 };
29 }
30 });
31
32 var AjaxAdapter = _.inherit(Dalmatian.Adapter, {
33 _initialize: function ($super) {
34 $super();
35 this.datamodel = {
36 title: '标题',
37 confirm: '刷新数据'
38 };
39 this.datamodel.ajaxData = {};
40 },
41
42 format: function (datamodel) {
43 //处理datamodel生成viewModel的逻辑
44 return datamodel;
45 },
46
47 ajaxLoading: function () {
48 this.setStatus('loading');
49 this.notifyDataChanged();
50 },
51
52 ajaxSuc: function (data) {
53 this.datamodel.ajaxData = data;
54 this.setStatus('ajaxSuc');
55 this.notifyDataChanged();
56 }
57 });
58
59 var AjaxViewController = _.inherit(Dalmatian.ViewController, {
60 _initialize: function ($super) {
61 $super();
62 //设置基本的属性
63 this.view = new AjaxView();
64 this.adapter = new AjaxAdapter();
65 this.viewstatus = 'init';
66 this.container = '#container';
67 },
68
69 //显示后Ajax请求数据
70 onViewAfterShow: function () {
71 this._handleAjax();
72 },
73
74 _handleAjax: function (data) {
75 this.adapter.ajaxLoading();
76 getAjaxData($.proxy(function (data) {
77 this.adapter.ajaxSuc(data);
78 }, this), data);
79 },
80
81 events: {
82 'click .cui-btns-sure': function () {
83 var data = this.$el.find('#ajax_data').val();
84 data = eval('(' + data + ')');
85 this._handleAjax(data);
86 }
87 }
88 });
89
90 var a = new AjaxViewController();
91 a.show();
首先定义view
其次定义数据处理层
最后将两者合一
重点放到了数据处理中,实际上的逻辑由Controller处理,真正的html又view生成,整个代码如上......
结语
今天对之前的学习进行了一些整理,由于过程中多数时间在编码,所以描述少了一点,整个这块还是有一些问题,我们留待后期解决吧
本文转自叶小钗博客园博客,原文链接:http://www.cnblogs.com/yexiaochai/p/3735139.html,如需转载请自行联系原作者