《紅包日歷》ios版本——基于vue全家桶的webapp單頁應用

項目介紹:制作一個網賺應用
平臺:ios手機
技術選型:vue + vuex + vue-router + vue-resource + webpack + es6 + sass + postcss
最終效果圖:更多可到App Store下載“悅動music”


首頁
tipLayer

背景

畢業(yè)之后首個用新技術單獨完成的項目,項目是從2015.10月開始的,當時vue還沒有2.0。

產品需求

業(yè)務角度(不展開說硝烂,有興趣私聊)
用戶做任務(下載應用)-->檢測有效性-->給分給用戶

技術角度

  1. 可快速迭代
  2. 獲取ios手機上的一些信息
  3. 用戶體驗友好

技術架構

出于以下的考量:
a. 獲取ios手機上的一些信息视事,只能通過ios客戶端來實現惕稻,這是業(yè)務的重心
b. ios客戶端需要上傳到App Store崎弃,每次迭代都需要至少2天的審核,這樣對可快速迭代不利
c. 因為a的一些實現跟App Store的規(guī)則有打擦邊球的嫌疑狐血,有時候會突然下架

參考競品,以及綜合分析易核,然后我們定出了這樣的架構:
Web App + Native App + 后端


架構

這里稍微說一下
黑線路徑匈织,要經過客戶端,發(fā)揮客戶端的優(yōu)勢耸成,譬如說加密报亩、一些客戶端的功能(譬如說截圖、第三方軟件分享井氢、登錄)
藍色路徑弦追,只要前端跟客戶端拿到token,就可以直接跟后端通訊花竞,免除每次都經過客戶端

前端技術選型分析

  1. 客觀角度
    問題來了劲件,作為多頁Web App掸哑,需要考慮的地方(更多點擊這里):
    i. 狀態(tài)管理
    譬如說,多頁面應用下零远,A頁面跳去B頁面苗分,在B頁面提交了數據,返回A的時候牵辣,我想利用B頁面的數據摔癣,是不可以的,因為進行了刷新纬向,又或者說這兩個頁面沒有可以通訊的中介择浊。其實我們可以用localStorage、url參數來作為中介逾条,這兩個頁面之間的通訊還好說琢岩,但是如果是兩個以上的,就很難維護了师脂。
    ii. 喪失入口担孔、路徑控制權限

  2. 主觀角度
    2015年10月,vue還沒出1.0吃警,第一次接觸mvvm框架糕篇,也聽說過當時很熱的Angular、React汤徽。當時是抱著越簡單越好的心態(tài)去選娩缰,然后就無意中挑選了vue。關于mvvm的框架對比谒府,可以看看這里拼坎。
    而且也只有我一個人負責,所以更大膽地用新框架完疫。途中經歷過入職答辯泰鸡,以及跟網友聊天。才發(fā)現自己在技術選型上是隨意的壳鹤,我問了網友一個關于vue-resource的跨域問題盛龄,網友問了我一個問題:為什么選擇vue-resource,有什么特別之處嗎芳誓?為什么不用普通的ajax余舶?我這才從懵逼中醒悟過來,技術為需求而生锹淌。

單頁面應用架構匿值,有以下特點:
1)在一個頁面下切換視圖,而不需要重新加載整個網頁赂摆,這樣一來就減輕了加載資源的負載挟憔、縮短了用戶的等待時間钟些;
2)路由控制視圖切換
3)組件化開發(fā),利于分治绊谭、復用
4)MV*政恍,免掉繁重的dom操作
5)方便共享數據

Vue是前端MVVM框架,它實現了組件化达传、模板渲染等功能
VueRouter可以控制路由篙耗,從而切換視圖
VueResource封裝了Promise的寫法以及對Restful API更友好

最后不謀而合,我們就選取了Vue全家桶來制作單頁應用

當然單頁應用也有缺點:
1)首次加載比較慢
2)對SEO不友好
3)瀏覽器本身的歷史回退

JUST DO IT √

  • 構建項目
    vue-cli:目錄結構
    webpack gulp:構建項目趟大,壓縮代碼鹤树,自動化腳本铣焊,打包代碼
    npm:包管理

  • 開發(fā)flow
    git flow
     搭建開發(fā)/測試環(huán)境:webpack-dev-server hot-reload webpack.conf
    webpack打包大小優(yōu)化:code splitting逊朽、壓縮
    webpack本地構建優(yōu)化:把第三方庫放在vendor或者externals等等

  • 功能區(qū)分以及開發(fā)
    utils
    config
    mixins
    公用組件
    view

  • 布局
    z軸上,采用weui的規(guī)范
    區(qū)分公用組件和view進行布局
    自適應布局flexible.js + rem + flex布局
    -webkit-overflow-scrolling : touch造成的堆疊上下文

  • 其他
    官網上百度搜索曲伊,zhanzhang.baidu.com


vue的功能:(√ 表示項目中用到的)

  • 數據驅動更新視圖 √
  • 試圖切換&過渡效果 √
  • 路由 √
  • 組件之間的通訊※ √
  • 狀態(tài)管理 √
  • vdom
  • 單元測試
  • 后端渲染

制作的過程中叽讳,我覺得組件間通訊比較重要:
vue1.x:
方法①broadcast、dispatch(父子坟募、兄弟組件通訊)vue2.x廢棄該方法
方法②this.$root岛蚤、this.$children(父子、兄弟組件通訊)
方法③prop懈糯、emit(父子組件通訊)vue1.x prop支持雙向綁定
方法④this.$refs(父->子單向通訊)
vue2.x:
方法①event bus(兄弟組件通訊)
方法②prop涤妒、emit(父子組件通訊)vue2.x prop不支持雙向綁定


遇到的問題

1. 跨域cookie共享
因為需要知道用于的登錄狀態(tài),所以使用token來作為標示赚哗,前端傳送的http頭部如果帶有token的cookie她紫,后端檢驗token通過,就代表該用戶有效且處于登錄狀態(tài)屿储。但是跨域傳輸cookie需要配置一些東西贿讹。

如果不跨域,前端直接使用document.cookie够掠,發(fā)送請求到后端民褂,會自動帶上cookie;
如果跨域疯潭,默認是不帶cookie的赊堪,如果需要跨域帶上cookie需要做以下步驟:
1)前端設置cookie的domain為后端的域名

document.cookie = "key=value;domain=backend.website.com;path=/"

如果前端域名和后端域名的主域名是相同的,可以直接設置為主域名竖哩,譬如這個項目是前后端分離的哭廉,瀏覽器訪問html文件,是m.hongbaorili.com期丰,這個域名對應的是前端的文件群叶,而后端的接口是ios.hongbaorili.com吃挑,他們有相同的部分hongbaorili.com,直接把cookie的domain設置為這個也可以街立。

document.cookie = "token=xxx;domain=hongbaorili.com;path=/"

2)前端設置xhr.withCredentials=true
3)后端設置http返回頭部

// 設置允許跨域的域名舶衬,注意如果是跨域傳送cookie,是不能設置為*的赎离,必須指定域名
Access-Control-Allow-Origin: http://m.hongbaorili.com
// 設置允許跨域共享cookie
Access-Control-Allow-Credentials: true

2. http的簡單請求和非簡單請求(preflight)
因為使用vue-resource來進行處理請求逛犹,其實它主要就是使用了promise包了一層ajax,當然還有設置了一些勾子讓用戶靈活設置梁剔,譬如我在這里提問的問題虽画。
它還默認對post、get方法設置了一些方法和html頭部荣病,其中的post方法(vue-resource v0.9.3)默認設置了HTTP頭部Content-Type:"application/json;charset=utf-8码撰。當我嘗試使用vue-resource的方法的時候,會失敗个盆,抓包一看狀態(tài)是403(method not allow)脖岛,它發(fā)送了一個method為OPTION的包,我當時是想著有沒有方法不走OPTION直接走post方法颊亮,就查到了相關的資料:

瀏覽器將CORS的請求分為:簡單請求非簡單請求柴梆。
簡單請求必須同時滿足以下要求,否則為非簡單請求:

來源:阮一峰博客

非簡單請求:
請求方法是PUT或者DELETE
Content-Type: application/json
凡是非簡單請求终惑,在正式通訊之前绍在,會發(fā)送一個OPTION方法的數據包,作為預檢請求(preflight)雹有,詢問后端當前請求是否在許可名單(Origin)偿渡、可以使用哪些http方法(Access-Control-Request-Method)、可以帶上哪些頭部信息字段(Access-Control-Request-Headers)件舵。后端通過查詢對應的Access-Control-Allow-Origin卸察、Access-Control-Allow-Methods、Access-Control-Allow-Headers字段铅祸,如果通過就返回一個200坑质,然后就進行數據通訊。如果不通過临梗,返回的數據不包含跨域的信息頭部涡扼,表示失敗,這時候xhr的onerror就會響應盟庞。

所以找到了問題的關鍵是vue-resource默認的post方法使用了Content-Type: application/json觸發(fā)了preflight吃沪。所以可以直接把默認的選項去掉,然后加上emulateJSON: true來表示application/x-www-form-urlencoded什猖,然后就能觸發(fā)簡單請求票彪。具體的解決步驟在這里红淡。

以上兩個問題詳見:
http://www.ruanyifeng.com/blog/2016/04/cors.html

3. 組件:無限滾動
關鍵點:
1)判斷滾動到底部,觸發(fā)拉取新數據降铸,添加新數據
判斷滾動是否到底部有用到:滾上去的高度scrollTop + 頁面的高度clientHeight === 網頁的高度scrollEle.offsetHeight
2)零部件:
設置flag變量在旱,防止?jié)L動到底部發(fā)送多個請求;
設置page推掸、size變量表示拉取的頁數桶蝎、數據條數;
3)優(yōu)化點:
首次進入的時候谅畅,未獲取到數據的時候登渣,用變量loading來記錄;
當加載完畢(條目<size || 第一個的size===0)毡泻,用變量nodata來記錄胜茧。
使用-webkit-overflow-scrolling: touch在ios端滑動起來體驗很好

可繼續(xù)優(yōu)化的點:
上拉加載更多,或者下拉加載更多牙捉,有多余的塊顯示

4. 布局竹揍、組件與功能的考慮
首先我們把組件分為公用組件和私有組件,后來看到資料邪铲,發(fā)現私有組件都在view(視圖)里面,所以應該是這樣分類:components(組件)和view(視圖)无拗。

然后以App.vue為根組件带到,公共組件和router view掛載在App.vue下,大概是這樣的:

我的布局

其中遇到的問題:
1)一些可復用組件英染,譬如說confirm組件揽惹,它的模子就只有提示框的骨架,其中的內容需要用slot來寫四康。它應該放在公共組件的位置搪搏,還是view里面?
當初我沒有細想闪金,就放在公共組件的位置疯溺,執(zhí)行起來的時候,遇到超級不爽的地方:
①每個view都要跟公用組件通訊哎垦,傳遞提示框的自定義信息囱嫩,包括插圖地址、主標題漏设、副標題墨闲、正文、提示郑口;
②每個提示框的“取消事件”還好說鸳碧,都是把confirm組件隱藏掉盾鳞;但是”確定事件”就不是每個confirm組件是一樣的,所以也需要動態(tài)綁定瞻离。
這種方案用以下兩種方法實現組件間的通訊:
a. confirm組件作為全局組件雁仲,狀態(tài)記錄在vuex。這樣對于①的操作就很簡單了琐脏,傳一個json過去就可以攒砖;但問題在于如何動態(tài)綁定“確定事件”,我的做法是在vuex添加一個變量yesCounter日裙,每次confirm組件的“確定”按鈕點擊之后吹艇,yesCounter就+1,在view里面watch這個變量(yesCounter)昂拂,方法寫在view的methods里面受神,當監(jiān)測到改變就觸發(fā)事件。

b. confirm作為公共組件格侯,掛在在App.vue下鼻听,指定組件名稱confirm。在view里面联四,用this.$root.refs.confirm來調用里面的東西撑碴、以及賦值。

c. confirm組件作為view里面的組件朝墩,對于①的操作醉拓,很直觀簡單;對于②的操作用emit事件收苏;而且這個方案的好處在于“按需加載”亿卤,因為有些view是不需要confirm組件的。

于是我采用方案c鹿霸,但是呢排吴,這又有一個問題,就是當confirm組件的出現的時候懦鼠,我希望它把全屏遮住了钻哩,但是它又內嵌到view里面「鹈疲《當時我沒找到方法憋槐,就徘徊地用回方案a,但是的確太惡心了淑趾,就狠下心來把方案c產生的問題解決掉(這種習慣應該拋棄把糇小!)。當時想了三個方案:
i) app__header近范、app__content放在同一個wrapper里面嘶摊,控制content的高度,超出的范圍滾動條顯示评矩。這種ok


方案 i

ii) app__header叶堆、app__content放在同一個wrapper里面,但是是使用flex布局的斥杜。這種方案肯定不可以虱颗,因為view怎么樣都覆蓋不了header的;

iii) app__header用fixed布局蔗喂,app__content的高度是100%忘渔,header跟content的堆疊上下文是相同的,但是我需要把header置頂缰儿,所以直接把header的順序放在下面畦粮。然后放在app__content的confirm組件設置z-index就可以了。

App.vue
confirm

無意中看到weui的布局乖阵,印證了當初自己的思考也比較合理

weui頁面層級

5. 抽象view的邏輯 && promise && es6
由于每個view都有以下特點:
①每次加載的時候都會向后端ajax請求數據宣赔;
②通過設置route的data選項,如果①請求數據失敗瞪浸,視圖就切換回去之前的儒将;
③每次按刷新的時候,向后端ajax請求數據默终,更新data椅棺;

稍微分析一下,其實①②③的加載數據是可以復用的齐蔽,但是②中,路由的data勾子要傳入transition這個變量床估,用transition.next()和transition.abort()控制視圖切換含滴,是否但是在①③不需要。綜合以上需求,就寫了一個mixin,如下:

export let routerDataMixin = {
    route: {
        data: function (transition) {
            var that = this;
            if (this.assist.token && this.fetchOption) {
                new Promise(function(resolve, reject) {
                    that.fetchData({resolve, reject})
                })
                .then(function(data){
                    transition.next();
                })
                .catch(function(error){
                    console.log(error);
                    transition.abort();
                })
            } else {
                transition.next();
            }
        }   
        //waitForData: true
    },
    methods: {
        fetchData: function(...rest) {  // 用上es6的rest爪模,很方便
            if (!this.fetchOption) {
                return false;
            }

            this.$http.get(
                this.fetchOption.url,
                {
                    params: this.fetchOption.params || {},
                    credentials: /hongbaorili/g.test(this.fetchOption.url)
                }
            ).then(
            function (response) {
                if (response.data.c === 0) {
                    if ( this.fetchSuccess ) {
                        this.fetchSuccess(response);
                    } else {
                        this.userData = response.data.d
                    }
                    try {
                        rest[0].resolve();
                    } catch(e) {}
                } else if (response.data.c === -10000){
                } else {
                    if ( this.fetchAbnormal ) {
                        this.fetchAbnormal(response);
                    }

                    try {
                        rest[0].reject(new Error("fetchData: c!=0"))
                    } catch(e) {}
                }
                this.endProgress();
            },
            function (response) {
                if ( this.fetchFail ) {
                    this.fetchFail(response);
                } else {
                    this.showToast();
                }

                try {
                    rest[0].reject(new Error("fetchData: fail"));
                } catch(e) {}

                this.endProgress();
            });
        }
}

6. BEM類命名方法
.component-name__component-part_component-status
eg:
.tab__tab-item_active
當然也可以靈活處理
.tab__tab-item.active
更多詳見這里

7. Restful API
增刪查改
post del get put
后端同事說項目小沒必要這么復雜橡庞,就只做了get和post

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市辐怕,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖祝闻,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異遗菠,居然都是意外死亡联喘,警方通過查閱死者的電腦和手機华蜒,發(fā)現死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來豁遭,“玉大人叭喜,你說我怎么就攤上這事”托唬” “怎么了捂蕴?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長闪幽。 經常有香客問我啥辨,道長,這世上最難降的妖魔是什么沟使? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任委可,我火速辦了婚禮,結果婚禮上腊嗡,老公的妹妹穿的比我還像新娘着倾。我一直安慰自己,他們只是感情好燕少,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布卡者。 她就那樣靜靜地躺著,像睡著了一般客们。 火紅的嫁衣襯著肌膚如雪崇决。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天底挫,我揣著相機與錄音恒傻,去河邊找鬼。 笑死建邓,一個胖子當著我的面吹牛盈厘,可吹牛的內容都是我干的。 我是一名探鬼主播官边,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼沸手,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了注簿?” 一聲冷哼從身側響起契吉,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎诡渴,沒想到半個月后捐晶,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年租悄,在試婚紗的時候發(fā)現自己被綠了谨究。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡泣棋,死狀恐怖胶哲,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情潭辈,我是刑警寧澤鸯屿,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站把敢,受9級特大地震影響寄摆,放射性物質發(fā)生泄漏。R本人自食惡果不足惜修赞,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一婶恼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧柏副,春花似錦勾邦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至荔泳,卻和暖如春蕉饼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背玛歌。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工昧港, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人支子。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓慨飘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親译荞。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容