響應(yīng)式Web設(shè)計(jì)(Responsive Web design)的理念是:集中創(chuàng)建頁面的圖片排版大小,可以智能地根據(jù)用戶行為以及使用的設(shè)備環(huán)境(系統(tǒng)平臺、屏幕尺寸、屏幕定向等)進(jìn)行相對應(yīng)的布局。
以我粗淺的理解,響應(yīng)式的提出,其實(shí)就是單純的根據(jù)不同的尺寸,以最優(yōu)的展示方式呈現(xiàn)罷了,僅僅而已,不能再多了,如果真要更多點(diǎn),便是根據(jù)不同的尺寸對靜態(tài)資源加載上有所控制,節(jié)約流量,換句話說,響應(yīng)式設(shè)計(jì)不涉及業(yè)務(wù)邏輯,jser神馬都不需要做,css同事便可完全解決,但事實(shí)上最近碰到的需求完全不是這么回事嘛。
以最簡單圖片輪播來說,手機(jī)上是這個(gè)樣子的:
而在ipad橫屏上,卻變成了這個(gè)樣子了:
我當(dāng)時(shí)就醉了,iPad豎著保持手機(jī)樣式,橫著iPad樣式,什么CSS有這么偉大,可以完成這個(gè)功能,而真實(shí)的場景是這個(gè)樣子的:
手機(jī)端:首頁搜索頁->list頁面->詳情頁->預(yù)定頁
但是到了ipad橫屏上:首頁左屏是搜索頁,右邊是日期選擇/城市選擇/......,然后到了list頁面,左邊是list,右邊是詳情頁,單擊左邊的list左邊詳情直接變化!
其實(shí)單獨(dú)頁面做的話,好像沒有什么問題,但是手機(jī)業(yè)務(wù)早已鋪開了,老板的意思是,代碼要重用,還是全局改改CSS實(shí)現(xiàn)就好啦,我當(dāng)時(shí)真為我們的UED捏了一把汗。到了具體業(yè)務(wù)實(shí)現(xiàn)的同事那里情況又變了,UED只是給出了兩個(gè)設(shè)計(jì)好了的靜態(tài)html+css,要怎么玩還得那個(gè)業(yè)務(wù)同事自己搞。
那天我去支援時(shí),看到了其牛逼的實(shí)現(xiàn),不由的菊花一緊,里面媒體查詢都沒有用,直接display: none 搞定一切問題了,這個(gè)對手機(jī)程序帶來了很大的負(fù)擔(dān):原來一個(gè)view就是用于手機(jī),現(xiàn)在無端的在里面加入了大量的pad端程序,直接造成了兩個(gè)結(jié)果:
① 業(yè)務(wù)邏輯變得復(fù)雜,容易出BUG
② js尺寸變大,對手機(jī)端來說,流量很寶貴
雖然知道他那種做法不可取,當(dāng)時(shí)忙于其它事情,并且天意難違,天意難測也只有聽之任之,但是這里要說一點(diǎn),響應(yīng)式布局不太適合業(yè)務(wù)復(fù)雜的webapp,各位要慎重!
雖然如此,問題還是需要解決,并且需要在框架層做出解決,這類需求本不應(yīng)強(qiáng)加與CSS,好在曾經(jīng)我們業(yè)務(wù)層的View設(shè)計(jì)基本是滿足條件的,現(xiàn)在只需要擴(kuò)展即可,仍然以blade框架為例:
每個(gè)頁面片完成的工作僅僅依賴了一個(gè)View類,既然View是類,那么繼承mobile的View,實(shí)現(xiàn)ipad的View,似乎是可能的,這一切的基石便是繼承
我們這里的View Controller index.js開始是不完全滿足我們的需求的,我們做一些調(diào)整,這里是調(diào)整前的代碼:
調(diào)整后的代碼如下:
PS:上面的代碼是我?guī)讉€(gè)月前寫的,今天一看又覺得可以優(yōu)化,當(dāng)真優(yōu)化無極限啊?。。?/p>
變化的關(guān)鍵點(diǎn)是每次我點(diǎn)擊的事件全部放到了Index這個(gè)類的prototype上:
1 searchItemAction: function (e) { 2 var gindex = $(e.currentTarget).attr('data-group'); 3 var index = $(e.currentTarget).attr('data-index'); 4 this.forward(this.uidata[gindex].data[index].uiname); 5 }, 6 7 closeSearchAction: function () { 8 this.closeSearch(); 9 }, 10 11 demoItemAction: function (item, groupIndex, index, e) { 12 scope.demoItemAction(item, groupIndex, index, e); 13 },
這里粒度到哪個(gè)程度與具體業(yè)務(wù)相關(guān),我這里不做論述,于是我這里繼承至index產(chǎn)生一個(gè)新的index類:index.ipad.js,這個(gè)是其基本實(shí)現(xiàn):
1 define([getViewClass('index'), getViewTemplatePath('index'), 'UIGroupList'], function (View, viewhtml, UIGroupList) { 2 return _.inherit(View, { 3 4 onCreate: function ($super) { 5 $super(); 6 }, 7 8 onPreShow: function ($super) { 9 $super(); 10 this.turning(); 11 }, 12 13 onShow: function ($super) { 14 $super(); 15 this.initGoupList(); 16 }, 17 18 onHide: function ($super) { 19 $super(); 20 }, 21 22 events: { 23 24 }, 25 26 searchItemAction: function (e) { 27 var gindex = $(e.currentTarget).attr('data-group'); 28 var index = $(e.currentTarget).attr('data-index'); 29 this.forward(this.uidata[gindex].data[index].uiname); 30 }, 31 32 demoItemAction: function (item, groupIndex, index, e) { 33 scope.forward(item.uiname); 34 } 35 36 }); 37 });
這個(gè)時(shí)候直接運(yùn)行blade/ipad/debug.html#index.ipad的話,頁面與原來index保持一致:
第二步便是重寫其事件的關(guān)鍵位置了,比如要跳出的兩個(gè)事件點(diǎn):
1 searchItemAction: function (e) { 2 var gindex = $(e.currentTarget).attr('data-group'); 3 var index = $(e.currentTarget).attr('data-index'); 4 this.forward(this.uidata[gindex].data[index].uiname); 5 }, 6 7 demoItemAction: function (item, groupIndex, index, e) { 8 scope.forward(item.uiname); 9 } 10 11 //簡單改變 12 13 searchItemAction: function (e) { 14 var gindex = $(e.currentTarget).attr('data-group'); 15 var index = $(e.currentTarget).attr('data-index'); 16 alert(this.uidata[gindex].data[index].uiname); 17 }, 18 19 demoItemAction: function (item, groupIndex, index, e) { 20 alert(item.uiname); 21 }
這個(gè)時(shí)候原版本的跳轉(zhuǎn),變成了alert:
這個(gè)時(shí)候便需要進(jìn)一步重寫了,比如這里:我點(diǎn)擊alert,事實(shí)上是想在右邊加載那個(gè)子view,所以框架全局控制器APP需要新增loadSubView的接口了:
loadSubView要實(shí)現(xiàn)實(shí)例化某一View非常簡單,但是該接口的工作并不輕松,換句話說會(huì)非常復(fù)雜,因?yàn)椋?/p>
History與路由歸一化是mobile與pad版本整合的難點(diǎn)
mobile的view與ipadview是公用的,所以本身不存在主次關(guān)系,是業(yè)務(wù)給予了其主次,這里需要一個(gè)管理關(guān)系
子View的實(shí)例化會(huì)涉及到復(fù)雜的History與路由管理,我們這里先繞過去,下個(gè)階段再處理,因?yàn)橥瓿蓀ad版本,框架的MVC核心要經(jīng)過一次重構(gòu)
1 //這里暫時(shí)不處理History邏輯,也不管子View的管理,先單純實(shí)現(xiàn)功能 2 //這樣會(huì)導(dǎo)致back的錯(cuò)亂,View重復(fù)實(shí)例化,這里先不予關(guān)注 3 loadSubView: function (viewId, wrapper, callback) { 4 5 //子View要在哪里顯示需要處理 6 if (!wrapper[0]) return; 7 8 this.loadView(viewId, function (View) { 9 10 var curView = new View(this, viewId, wrapper); 11 12 //這個(gè)是唯一需要改變的 13 curView.turning = $.proxy(function () { 14 curView.show(); 15 curView.$el.show(); 16 }, this); 17 curView.onPreShow(); 18 callback && callback(curView); 19 20 }); 21 22 },
在樣式上再做一點(diǎn)調(diào)整就變成這個(gè)樣子了:
這里History管理還是亂的,但是整個(gè)這個(gè)方案是可行的,所以我們前哨戰(zhàn)是成功的,方案可行的話便需要詳細(xì)的設(shè)計(jì)了
今天,我們對ipad與mobile統(tǒng)一使用一套view代碼做了研究,有以下收獲與問題:
① 繼承可實(shí)現(xiàn)ipad與mobile代碼復(fù)用,并且不會(huì)彼此污染,至少不會(huì)污染mobile程序
② pad版本中History與路由管理需要重構(gòu)
③ MVC需要重構(gòu),特別是View一塊,甚至需要完全重新寫
④ 樣式方面還需要處理優(yōu)化
總而言之,今天的收獲還是有的,剩下的問題,需要在核心框架上動(dòng)大動(dòng)作了,最后的目標(biāo)是能夠出一套同用于ipad與mobile的框架。
源碼:
https://github.com/yexiaochai/blade
demo在此:
http://yexiaochai.github.io/blade/ipad/debug.html#index.ipad