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周内完成一个不会上线的小型项目”而出发的。这意味着什么?咱们必须把重点放在产品的完整性上,而不是规范和维护。因此不少细节都没有作好,最关键的就是性能优化,移动端在性能方面的要求比浏览器端高多了。另外一方面,评委不会这样想,评委就是喜欢扯负载均衡,分布式数据库等,固然能够理解。我想说的是二者的标准不一样,这多是我本身犯的错,由于题目或者赢的途径就是走高大上、走情怀。
或许是技术选型上的失误,但至少我体验到了它的样子。
不入红尘焉能看破红尘,未曾拿起谈何放下。(仙剑一)