TL;DR: 若是沒有把握解決混合應用的性能問題,請儘可能使用 native 方式開發。javascript
在爲期四周的開發比賽(培訓、比賽,形式無所謂)結束後,天然應該作個總結,否則這段時間的廢寢忘食不就白費了?css
咱們小組共有15個成員,包括產品、設計、開發、QA 等全部該有的角色。通過1周(微妙地加粗:整個開發週期只有4周好嗎)的需求討論以後,咱們的結論是作一個以找室友爲核心,周邊功能有找房源、社交等,這樣一個app。前端
現象:在作需求的那周,發生了許多產品與開發互相撕X的場景,在雙方互相讓步後,處理地還挺優雅的。若是能在激烈討論後定下需求就萬事大吉了,問題是當開發進行了幾天以後,以爲功能點仍是太繁雜,因而又只能砍,有一些開發成果就直接廢棄了。java
其實需求是很難肯定的,這點相信各位都能體會到。(道理我都懂,可是……)react
特別是對於工做經驗比較缺少的新人來講,他們很喜歡天馬行空地幻想,把這個功能加進去,那個不錯,也要,還有還有……是由於心太大了嗎?仍是對開發同窗的能力過於高估?那我做爲開發,就先說本身的不是好了。只要水平到位,還不是有什麼需求就作什麼?爲了防止抨擊,這句話的前提是,不考慮需求是否合理。就是說,當開發人員抱怨產品的不切實際時,也應該反思一下本身的水平爲何不足以支撐這個需求。因此開發人員更多時候應該去提升本身的技術水平,作好本職工做。android
另外一方面對於PM,其實他們有兩方面因素須要權衡。一個是產品定位,什麼功能是非要不可的,失去它,就沒法體現出產品核心了。第二是開發成本,若是你的開發團隊水平夠的話,固然能夠適當增長一些需求,以提升競爭力。然而,咱們幾個開發大多數都是沒有工做經驗的,都是應屆本科生或者碩士生。ios
那擺在PM面前的可用資源就是這些了,他必須得認清現實,有幾斤麪粉就作多少饅頭。一個年輕的PM每每很難估計每一個成員的能力,更別說咱們這15我的纔剛認識不久,因此爲何需求那麼不穩定?其實也就很容易理解了。git
說到理解,產品和開發之間要怎麼互相理解?產品總會認爲開發搞了一天都沒什麼成果,而開發總是以爲產品什麼都不懂還在那瞎XX。這個問題用經驗和溝通手段就能夠解決了。經驗的意思是說,工做經驗(不是混幾年)越多,這方面的問題就越少,由於你們都是過來人,不少事情都心領神會。但經驗在咱們團隊中貌似行不通,至於溝通手段,就是說怎麼向對方解釋了。若是產品一上來就說,須要增長一個「搖一搖」功能,相信絕大部分開發的第一反應都是拒絕的。而若是開發跟產品說,cookie 中不能獲取 session id,因此自動登陸無法作,相信產品就會想,別人能夠作,你怎麼作不了。程序員
這樣對雙方都是很傷的。做爲PM,他能夠先問嘛,「圖片裁剪」能不能作?大概須要多久?而開發的話,永遠把提升技術水平放首位,而後有空再關注一些產品方面的東西。github
如何提升時間利用率?這段時間的集中式開發過程當中,我發現一個頗有趣的現象,就是不少人來到公司後,須要花大量的時間才能投入狀態。好比說,9點到座位上,花10分鐘吹空調,再打開郵件、音樂、微博、知乎等等,花去半個小時,周圍的人陸陸續續到了以後,再聊上半個小時,路上怎麼賭了,今每天氣怎麼樣。真當打開IDE後,須要花半個小時回想昨天作了什麼,從哪裏着手今天的任務。更可悲的是,正要寫代碼的時候,立刻拉去開晨會,回來又無所適從了。
那解決辦法是什麼?專一。程序員這個職業很特殊,不像跑步,跑20分鐘,休息10分鐘,再來一次,減肥效果更好。一旦開發人員的思緒被打斷,再要投入進去就難了,編程20分鐘,休息10分鐘,再來一次,啥也沒有。
這是我的的時間,那團隊時間就更復雜了。原本對於新人來講就沒什麼時間概念,而新人又恰恰過於自信,以爲這個很簡單,那個很簡單,兩三天確定作得完吧!通過不少次挫敗和反思以後,我得出的結論就是,不要低估任何需求的難度,多估一點時間總不會錯的。
在最後技術答辯時,有評委問我在開發過程當中有沒有作 code review,我老實說沒有。但很慶幸他問到了這一點,說明公司仍是比較注重代碼規範的。
爲何代碼審查很重要?工做中,咱們基本上是在一個現有的代碼庫中添加或修改代碼,而不是從頭開始編寫全新的代碼。若是要求改動的代碼必須符合原有的規範,那麼請別人作審查就頗有必要了。這是第一,第二是能夠下降錯誤率。當局者迷,旁觀者清,別人多多少少能夠看出一點代碼中潛在的問題,這些問題每每會被做者的自信所掩蓋。第三點能夠從另外一方面來說,就是給別人作審查的時候(沒錯,並非只有資深員工給新員工作審查,反過來也能夠),能夠學習到他們的編碼技巧。不管是被動仍是主動,對我的對團隊,都是有幫助的。
那爲何咱們沒有作代碼審查呢?一是時間不容許,二是,好吧,能把功能完成就能夠了,既然是短時間項目,不須要償還技術債務,就不必把規範進行到底了。
精簡再精簡,非技術部分寫得仍是有點多。
我在團隊中擔任的是前端開發這個角色,因此技術方面就只能總結前端相關的內容了。
一開始說要作 app 的時候,我實際上是拒絕的。考慮到咱們全部 4 個前端都只會 web,很難去估量作 app 的複雜度,以及遇到問題是否是必定能解決,不能解決怎麼辦。但本着體驗新事物的態度(被產品知道要氣死了)就答應了。
那混合型應用的技術有什麼呢?無非就是用 cordova 把一個單頁應用打包成 apk(android),或者用 React Native 作 ios 平臺的應用。
最後咱們前端用的是 cordova + ionic,後臺就萬年 spring mvc 啦。ionic 它是一個移動端 css 框架兼 JS 框架。其中 JS 具體是指它內置了豐富的 angular directive,因此開發效率會快不少,畢竟不用本身寫側邊欄佈局、圖片輪播、對話框等組件了。有興趣的同窗能夠了解下,官網。
後來評委也問到爲何大家明知道 angular 有那麼多性能方面的問題,卻仍是要用它?我實在是想不到更合適的技術了。什麼是「合適」?拋開具體狀況光談技術,那永遠沒有最佳答案。而咱們的具體狀況是,你們都是 web 前端,沒用過 react,不熟悉 angular,平時會點 jQuery,有些 css 屬性還想不起來了,須要查手冊。因此若是各位看客大人有更好的建議,在下是真心求教。
雖說前端的技術多,但不少都是大同小異,好比 underscore 和 lodash,說不定過段時間就合併了。
最後我想提一個疑惑就是,爲何在瀏覽器中調試時,這頁面跳轉、列表的下拉都流暢得飛起,怎麼裝到手機上就很卡了呢?難道真的要把一切都怪罪於 angular 嗎?
對於沒有接觸過的領域就應該大膽嘗試,學習大概就是這樣一個過程吧。
移動端的特色就是屏幕大小極其碎片化,但兼容性還過得去;網速慢、有電量限制。
對於屏幕適配,能夠經過 media query 作成響應式的,難點在於設計師的腦洞了,他若是非要作成 pintrest 這樣的瀑布式列表,我想你的關注點應該在於怎麼實現而不是怎麼適配其餘的屏了吧。
至於網速,css 和 js 都打包在 app 的安裝包裏了,因此網速不會影響這兩個資源的加載,它的瓶頸在於圖片,這個在後面小節有提。
那怎麼下降電耗呢?就只能在視覺上妥協了。好比說,兩個頁面之間的轉場動畫,每每有滑動的效果,它多是 js 強行計算出來的,也多是 css 中 animation 和 transition 的功勞(這個可能性大點),靠 GPU 計算每幀的畫面,也算流暢。但計算都是耗電的,因此權衡一下,哪些特效能夠不用,哪些資源能夠不須要加載(圖片和 DOM)。
這個問題在開發過程當中糾結了好久,徹底能夠另寫一篇文章來詳說。
首先,以傳統 web 開發的思惟來考慮,每次發送http請求時,服務端會判斷這是否是已有的 session,以 tomcat 爲例,它根據 cookie 中是否有 JSESSIONID 來判斷,若是沒有這個屬性,就新建一個,在返回的響應中,寫到 Set-Cookie 中,瀏覽器會自動把這個值寫到 cookie 裏的,對前端開發者來講徹底透明,甚至後臺開發人員也不須要知道細節,只要會調用 getSession() 相似的方法就能夠了。
可是,當一切碰到跨域的時候,問題的性質就變了。顯然,咱們的 app 和服務端接口不是同一個域,至少在瀏覽器中調試頁面時不是。因此,服務端必須在響應頭中加上
Access-Control-Allow-Origin: * Access-Control-Allow-Methods: OPTIONS, POST, GET
然而,跨域時,雖說瀏覽器能夠取得 JSESSIONID,但它沒法傳回服務端,這就致使服務端認爲每次請求都是一個新的 session 。固然有辦法能夠作到傳回 JSESSIONID,就是在響應頭中加上
Access-Control-Allow-Credential: true
問題是,一旦這麼設置,Access-Control-Allow-Origin 就不能是通配符。
不要緊,咱們幾個開發在本地調試時都用一個端口部署前端頁面好了,這樣後臺設置起來也方便,好比統一設置成
Access-Control-Allow-Origin: http://localhost:3000/
當一切彷佛在和平中進展時,冒出一個新的需求,叫自動登陸。
這和跨域有什麼關係呢?咱們原覺得只須要把 JSESSIONID 存到 localStorage 中,若是它沒過時,再放到 cookie 中就好了,可是 AJAX 是沒法取得 Set-Cookie 響應頭中的信息的,請見標準。
也就是說,這是死衚衕。基於 cookie 的驗證方式在咱們的場景中是行不通的。
隨後,咱們使用了 JWT 的方式,即忘掉 session 的概念,當登陸接口成功調用後,後臺計算一個用戶 token,而後前端把該 token 存到 localStorage 中,而後每次請求服務端接口時,都把這個 token 傳過去。註銷後就從 localStorage 中刪除 token 。
那麼可否自動登陸就是判斷 localStorage 中是否有 token 了。
爲了避免改變原有的前端調用方式,咱們經過 angular 的 httpProvider 注入了一個攔截器,對於每一個請求,都把 token 寫到 request header 的 Authorization 屬性中。
app.factory('authInterceptor', function ($rootScope, $q, $window) { return { request: function (config) { config.headers = config.headers || {}; if ($window.localStorage.access_token) { // config.headers.Authorization = 'Bearer ' + $window.localStorage.access_token; config.headers.Authorization = $window.localStorage.access_token; } return config; }, responseError: function (response) { // console.log('intercept error response', response.status); if (response.status === 401 || response.status === 403) { // 用戶無權限時跳轉到登陸頁 $rootScope.go('/login'); } return $q.reject(response); } }; });
必須明確一點,angular 不適合作大型數據的展現,由於 ng-repeat 的性能實在太差,還由於它的髒檢查。必要時就本身寫 directive 。
想象一個圖片 gallery,當元素個數達到必定程度時,在瀏覽器中顯示都不免會出現卡頓的現象,更別說在移動端了。
圖片方面有哪些能夠優化?簡單的作法是壓縮圖片,佔的內存少了,天然就好點。複雜一點就作響應式,什麼意思?一樣一個列表,在移動端顯示 100 100 的圖,在瀏覽器中顯示 300 300 的圖。怎麼作?用 img 元素的 srcset 和 sizes 屬性來指定圖片集和判斷條件,這方面的內容也能夠花一篇文章來講明,先給個連接預熱一下。
當圖片的大小能夠控制時,如今就來看,就算是 100 * 100 的圖,若是顯示 500 張,那仍是大啊,內存佔的多,仍是會卡。那麼在大型的列表中,對這些元素又該如何顯示呢?簡單(相對而言,最簡單就是什麼都不作嘛)的作法就是,把視窗以外的元素設置成
visibility: hidden;
儘管這個元素還佔着位置,但貌似內存會消耗得少一點,不妨一試。不過要注意的是,怎麼去判斷元素已經在視窗以外了,這是個難點。
另外一個作法,我沒有試過,就是把視窗以外的圖片節點刪除。因爲圖片是緩存的,因此當從新添加圖片節點是,只要地址沒變,就不會產生服務端請求,這點不用擔憂,關鍵是還原正確的圖片,以及添加和刪除節點會致使 reflow,這個代價真的值得嗎?
其實在這個項目中,個人觀點都是基於「15個新人在4周內完成一個不會上線的小型項目」而出發的。這意味着什麼?咱們必須把重點放在產品的完整性上,而不是規範和維護。因此不少細節都沒有作好,最關鍵的就是性能優化,移動端在性能方面的要求比瀏覽器端高多了。另外一方面,評委不會這樣想,評委就是喜歡扯負載均衡,分佈式數據庫等,固然能夠理解。我想說的是二者的標準不一樣,這多是我本身犯的錯,由於題目或者贏的途徑就是走高大上、走情懷。
或許是技術選型上的失誤,但至少我體驗到了它的樣子。
不入紅塵焉能看破紅塵,未曾拿起談何放下。(仙劍一)