【单页应用】view与model相关梳理

前情回顾


根据之前的学习,我们形成了一个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了

  1 Dalmatian.ViewController = _.inherit({
2
3 // @description 构造函数入口
4 initialize: function (options) {
5 this._initialize();
6 this.handleOptions(options);
7
8 //处理datamodel
9 this._handleDataModel();
10 this.create();
11 },
12
13 // @description 默认属性设置点,根据该函数,我可以知道该类具有哪些this属性
14 _initialize: function () {
15
16 //用户设置的容器选择器,或者dom结构
17 this.containe;
18 //根元素
19 this.$el;
20 //默认容器
21 this.root = $(‘body‘);
22
23 //一定会出现
24 this.view;
25 //可能会出现
26 this.adapter;
27 //初始化的时候便需要设置view的状态,否则会渲染失败,这里给一个默认值
28 this.viewstatus = ‘init‘;
29
30 },

31
32 setViewStatus: function (status) {
33 this.viewstatus = status;
34 },
35
36 // @description 操作构造函数传入操作
37 handleOptions: function (options) {
38 if (!options) return;
39
40 this._verify(options);
41
42 // @description 从形参中获取key和value绑定在this上
43 if (_.isObject(options)) _.extend(this, options);
44 },
45
46 //处理dataAdpter中的datamodel,为其注入view的默认容器数据
47 _handleDataModel: function () {
48 //不存在就不予理睬
49 if (!this.adapter) return;
50 this.adapter.datamodel.wrapperSet = this.view.wrapperSet;
51 this.adapter.registerObserver(this);
52 },
53
54 // @description 验证参数
55 _verify: function (options) {
56 //这个underscore方法新框架在报错
57 // if (!_.property(‘view‘)(options) && (!this.view)) throw Error(‘view必须在实例化的时候传入ViewController‘);
58 if (options.view && (!this.view)) throw Error(‘view必须在实例化的时候传入ViewController‘);
59 },
60
61 // @description 当数据发生变化时调用onViewUpdate,如果onViewUpdate方法不存在的话,直接调用render方法重绘
62 update: function (data) {
63
64 //这样虽然减少回流,但会隐藏页面跳动
65 // _.callmethod(this.hide, this);
66
67 if (!_.callmethod(this.onViewUpdate, this, [data])) {
68 this.render();
69 }
70
71 // _.callmethod(this.show, this);
72 },
73
74 /**
75 * @override
76 *
77 */
78 render: function () {
79 // @notation 这个方法需要被复写
80 // var data = this.adapter.format(this.origindata);
81 this.view.render(this.viewstatus, this.adapter && this.adapter.getViewModel());
82 },
83
84 // @description 返回基于当前view下的某节点
85 find: function (selector) {
86 if (!this.$el) return null;
87 return this.$el.find(selector);
88 },
89
90 _create: function () {
91 this.render();
92
93 //render 结束后构建好根元素dom结构
94 this.$el = $(this.view.html);
95 },
96
97 create: function () {
98
99 //l_wang 这段代码没有看懂************
100 // var $element = this.find(this.view.viewid);
101 // if ($element) return _.callmethod(this.recreate, this);
102 //l_wang 这段代码没有看懂************
103
104 // @notation 在create方法调用前后设置onViewBeforeCreate和onViewAfterCreate两个回调
105 _.wrapmethod(this._create, ‘onViewBeforeCreate‘, ‘onViewAfterCreate‘, this);
106
107 },
108
109 /**
110 * @description 如果进入create判断是否需要update一下页面,sync view和viewcontroller的数据
111 */
112 _recreate: function () {
113 this.update();
114 },
115
116 recreate: function () {
117 _.wrapmethod(this._recreate, ‘onViewBeforeRecreate‘, ‘onViewAfterRecreate‘, this);
118 },
119
120 //事件注册点
121 bindEvents: function (events) {
122 if (!(events || (events = _.result(this, ‘events‘)))) return this;
123 this.unBindEvents();
124
125 // @description 解析event参数的正则
126 var delegateEventSplitter = /^(\S+)\s*(.*)$/;
127 var key, method, match, eventName, selector;
128
129 //注意,此处做简单的字符串数据解析即可,不做实际业务
130 for (key in events) {
131 method = events[key];
132 if (!_.isFunction(method)) method = this[events[key]];
133 if (!method) continue;
134
135 match = key.match(delegateEventSplitter);
136 eventName = match[1], selector = match[2];
137 method = _.bind(method, this);
138 eventName += ‘.delegateEvents‘ + this.view.viewid;
139
140 if (selector === ‘‘) {
141 this.$el.on(eventName, method);
142 } else {
143 this.$el.on(eventName, selector, method);
144 }
145 }
146
147 return this;
148 },
149
150 //取消所有事件
151 unBindEvents: function () {
152 this.$el.off(‘.delegateEvents‘ + this.view.viewid);
153 return this;
154 },
155
156 _show: function () {
157 this.bindEvents();
158 this.root = $(this.container);
159 this.root.append(this.$el);
160 this.$el.show();
161 },
162
163 show: function () {
164 _.wrapmethod(this._show, ‘onViewBeforeShow‘, ‘onViewAfterShow‘, this);
165 },
166
167 _hide: function () {
168 this.forze();
169 this.$el.hide();
170 },
171
172 hide: function () {
173 _.wrapmethod(this._hide, ‘onViewBeforeHide‘, ‘onViewAfterHide‘, this);
174 },
175
176 _forze: function () {
177 this.unBindEvents();
178 },
179
180 forze: function () {
181 _.wrapmethod(this._forze, ‘onViewBeforeForzen‘, ‘onViewAfterForzen‘, this);
182 },
183
184 _destory: function () {
185 this.unBindEvents();
186 this.$el.remove();
187 // delete this;
188 },
189
190 destory: function () {
191 _.wrapmethod(this._destory, ‘onViewBeforeDestory‘, ‘onViewAfterDestory‘, this);
192 }
193 });

这个控制器是连接view以及Adapter的桥梁,三者合一便可以处理一些问题,接下来看一个简单的demo

Ajax例子

  1 <!doctype html>
2 <html lang="en">
3 <head>
4 <meta charset="UTF-8">
5 <title>ToDoList</title>
6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <link rel="stylesheet" type="text/css" href="http://designmodo.github.io/Flat-UI/bootstrap/css/bootstrap.css">
8 <link rel="stylesheet" type="text/css" href="http://designmodo.github.io/Flat-UI/css/flat-ui.css">
9 <link href="../style/main.css" rel="stylesheet" type="text/css" />
10 <style type="text/css">
11 .cui-alert { width: auto; position: static; }
12 .txt { border: #cfcfcf 1px solid; margin: 10px 0; width: 80%; }
13 ul, li { padding: 0; margin: 0; }
14 .cui_calendar, .cui_week { list-style: none; }
15 .cui_calendar li, .cui_week li { float: left; width: 14%; overflow: hidden; padding: 4px 0; text-align: center; }
16 </style>
17 </head>
18 <body>
19 <article id="container">
20 </article>
21 <script type="text/underscore-template" id="template-ajax-init">
22 <div class="cui-alert" >
23 <div class="cui-pop-box">
24 <div class="cui-hd">
25 <%=title%>
26 </div>
27 <div class="cui-bd">
28 <div class="cui-error-tips">
29 </div>
30 <div class="cui-roller-btns" style="padding: 4px; "><input type="text" placeholder="请设置数据 [{ title: ‘‘},{ title: ‘‘}]" style="margin: 2px; width: 100%; " id="ajax_data" class="txt"></div>
31 <div class="cui-roller-btns">
32 <div class="cui-flexbd cui-btns-sure"><%=confirm%></div>
33 </div>
34 </div>
35 </div>
36 </div>
37 </script>
38 <script type="text/underscore-template" id="template-ajax-suc">
39 <ul>
40 <% console.log(ajaxData) %>
41 <%for(var i = 0; i < ajaxData.length; i++) { %>
42 <li><%=ajaxData[i].title %></li>
43 <% } %>
44 </ul>
45 </script>
46
47 <script type="text/underscore-template" id="template-ajax-loading">
48 loading....
49 </script>
50
51 <script type="text/javascript" src="../../vendor/underscore-min.js"></script>
52 <script type="text/javascript" src="../../vendor/zepto.min.js"></script>
53 <script src="../../src/underscore.extend.js" type="text/javascript"></script>
54 <script src="../../src/util.js" type="text/javascript"></script>
55 <script src="../../src/message-center-wl.js" type="text/javascript"></script>
56 <script src="../../src/mvc-wl.js" type="text/javascript"></script>
57 <script type="text/javascript">
58
59 //模拟Ajax请求
60 function getAjaxData(callback, data) {
61 setTimeout(function () {
62 if (!data) {
63 data = [];
64 for (var i = 0; i < 5; i++) {
65 data.push({ title: ‘我是标题_‘ + i });
66 }
67 }
68 callback(data);
69 }, 1000);
70 }
71
72 var AjaxView = _.inherit(Dalmatian.View, {
73 _initialize: function ($super) {
74 //设置默认属性
75 $super();
76
77 this.templateSet = {
78 init: $(‘#template-ajax-init‘).html(),
79 loading: $(‘#template-ajax-loading‘).html(),
80 ajaxSuc: $(‘#template-ajax-suc‘).html()
81 };
82
83 this.wrapperSet = {
84 loading: ‘.cui-error-tips‘,
85 ajaxSuc: ‘.cui-error-tips‘
86 };
87 }
88 });
89
90 var AjaxAdapter = _.inherit(Dalmatian.Adapter, {
91 _initialize: function ($super) {
92 $super();
93 this.datamodel = {
94 title: ‘标题‘,
95 confirm: ‘刷新数据‘
96 };
97 this.datamodel.ajaxData = {};
98 },
99
100 format: function (datamodel) {
101 //处理datamodel生成viewModel的逻辑
102 return datamodel;
103 },
104
105 ajaxLoading: function () {
106 this.setStatus(‘loading‘);
107 this.notifyDataChanged();
108 },
109
110 ajaxSuc: function (data) {
111 this.datamodel.ajaxData = data;
112 this.setStatus(‘ajaxSuc‘);
113 this.notifyDataChanged();
114 }
115 });
116
117 var AjaxViewController = _.inherit(Dalmatian.ViewController, {
118 _initialize: function ($super) {
119 $super();
120 //设置基本的属性
121 this.view = new AjaxView();
122 this.adapter = new AjaxAdapter();
123 this.viewstatus = ‘init‘;
124 this.container = ‘#container‘;
125 },
126
127 //显示后Ajax请求数据
128 onViewAfterShow: function () {
129 this._handleAjax();
130 },
131
132 _handleAjax: function (data) {
133 this.adapter.ajaxLoading();
134 getAjaxData($.proxy(function (data) {
135 this.adapter.ajaxSuc(data);
136 }, this), data);
137 },
138
139 events: {
140 ‘click .cui-btns-sure‘: function () {
141 var data = this.$el.find(‘#ajax_data‘).val();
142 data = eval(‘(‘ + data + ‘)‘);
143 this._handleAjax(data);
144 }
145 }
146 });
147
148 var a = new AjaxViewController();
149 a.show();
150
151 </script>
152 </body>
153 </html>

这段代码的核心在此


 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生成,整个代码如上......

结语


今天对之前的学习进行了一些整理,由于过程中多数时间在编码,所以描述少了一点,整个这块还是有一些问题,我们留待后期解决吧

【单页应用】view与model相关梳理,布布扣,bubuko.com

时间: 2024-08-04 05:59:31

【单页应用】view与model相关梳理的相关文章

【单页应用】view与model相关梳理(转载)

[单页应用]view与model相关梳理 前情回顾 根据之前的学习,我们形成了一个view与一个messageCenterview这块来说又内建了一套mvc的东西,我们这里来理一下首先View一层由三部分组成:① view② dataAdpter③ viewController view一块两个重要数据是模板以及对应data,一个状态机status这里view只负责根据状态取出对应的模板,而后根据传入的数据返回组装好的html这里一个view多种状态是什么意思呢?比如我有一个组件,但是里面有一圈

HTML5单页框架View.js介绍

什么是单页应用单页应用,是指将用户视觉上的多个页面在技术上使用一个载体来实现的应用. 换句话来讲,用户视觉效果,与技术实现的载体,并不是一定要一一对应的.采取哪种技术方案,取决于产品设计.技术组成以及方案之间的优劣平衡.放到 Web 前端环境中,这个承载了多个视觉效果的载体,就是 html 文件(或 asp,jsp 等). 为便于描述,本文将使用多个术语.其名称及对应的含义如下所示: 页面:技术上的一个html文件:视图:视觉上的一页内容:初步实现单页应用直观效果的单页应用,其实现过程其实并不复

【单页应用之通信机制】view之间应该如何通信

前言 在单页应用中,view与view之间的通信机制一直是一个重点,因为单页应用的所有操作以及状态管理全部发生在一个页面上 没有很好的组织的话很容易就乱了,就算表面上看起来没有问题,事实上会有各种隐忧,各种坑等着你去跳 最初就没有一定理论上的支撑,极有可能是这么一种情况: ① 需求下来了,搞一个demo做交待 ② 发现基本满足很满意,于是直接在demo中做调整 上面的做法本身没有什么问题,问题发生在后期 ③ 在demo调整后应用到了实际业务中,发现很多地方有问题,于是见一个坑解决一个坑 ④ 到最

【读书笔记】WebApi 和 SPA(单页应用)--knockout的使用

Web API从MVC4开始出现,可以服务于Asp.Net下的任何web应用,本文将介绍Web api在单页应用中的使用.什么是单页应用?Single-Page Application最常用的定义:一个最初内容只包含html和JavaScript,后续操作通过Restful风格的web服务传输json数据来响应异步请求的一个web应用.SPA的优势就是少量带宽,平滑体验,劣势就是只用JavaScript这些平滑的操作较难实现,不像MVC应用,我们可以异步form,partview.不用担心,我们

七天学会ASP.NET MVC(七)——创建单页应用

注:本文为学习摘录,原文地址为:http://www.cnblogs.com/powertoolsteam/p/MVC_Seven.html 目录 引言 最后一篇学什么 实验32—整理项目组织结构 关于实验32 实验33——创建单页应用——第一部分—安装 什么是Areas? 关于实验33 实验34——创建单页应用——第二部分—显示Employee 实验35——创建单页应用——第三部分—新建Employee 实验36——创建单页应用——第三部分—上传 实验32 ———整理项目组织结构 实验32与其

vue入门(三)----使用vue-cli搭建一个单页富应用

上面两节我们说了vue的一些概念,其实说的知识一点基础,这部分知识我觉得更希望大家到官网进行学习,因为在这里说的太多我觉得也只是对官网的照搬照抄而已.今天我们来学习一下vue-cli的一些基础知识,并且用vue-cli来搭建一个单页富应用.那么我们首先介绍一下什么是vue-cli? 首先要学习vue-cli,我觉得我们需要知道什么是cli吧!首先回到"原始"前端,什么是前端?无非就是html,css,js,虽然定义有些粗略.但是随着前端的不断发展,前端的内容越来越多,也越来越丰富.前端

Nodejs之MEAN栈开发(六)---- 用Angular创建单页应用

在上一节中我们学会了如何在页面中添加一个组件以及一些基本的Angular知识,而这一节将用Angular来创建一个单页应用(SPA).这意味着,取代我们之前用Express在服务端运行整个网站逻辑的方式(jade.路由都需要在服务端编译),我们将用Angular在客户端浏览器上跑起来.PS:在正常的开发流程上,我们可能不会在服务器端创建了一个网站,然后又用SPA重建它.但从学习的角度来说这还不错,这样掌握了两种构建方式. 上一节所有Angular相关的代码都在一个js里面,这不便管理和维护,这一

大熊君学习html5系列之------History API(SPA单页应用的必备)

一,开篇分析 Hi,大家好!大熊君又和大家见面了,(*^__^*) 嘻嘻……,这系列文章主要是学习Html5相关的知识点,以学习API知识点为入口,由浅入深的引入实例, 让大家一步一步的体会"h5"能够做什么,以及在实际项目中如何去合理的运用达到使用自如,完美驾驭O(∩_∩)O~,好了,废话不多说,直接进入今天的主题, 今天主要讲的是“History API”及在单页应用中的作用,并且会引入一个实际的例子做为讲解的原型范例,先来看看“History API”: 为了提高Web页面的响应

[Angularjs]asp.net mvc+angularjs+web api单页应用

小分享:我有几张阿里云优惠券,用券购买或者升级阿里云相应产品最多可以优惠五折!领券地址:https://promotion.aliyun.com/ntms/act/ambassador/sharetouser.html?userCode=ohmepe03 写在前面 最近的工作一直在弄一些h5的单页应用,然后嵌入到app的webview中.之前一直在用angularjs+html+ashx的一套东西.实在是玩腻了.然后就尝试通过asp.net mvc的方式构建单页应用.用到的技术angularjs