随着前端项目数量和规模越来越大,参与的人员也越来越多,如何在前端项目开发过程中保证优质的开发者体验和项目的可维护性,同时确保极致的用户体验将会是一个非常大的挑战。
为了应对这个挑战,美团点评境外度假前端研发团队自2016年6月起启动了面向C端用户的"赫尔墨斯"项目,主要围绕以下几个方面进行展开:
在之前的项目中,页面是由Java后端项目中通过FTL模板引擎拼装,前端团队会维护另外一个前端的项目,存放相应的CSS和JS文件,最后通过公司内部的Cortex系统打包发布。
这个流程的问题在于前端对于整个页面入口没有控制力,需要依赖后端的FTL拼装,页面的内容需要更改时,前后端同学就要反复沟通协调,整体效率比较差,容易出错,也不方便实现前端相关的优化。更坑的是有时候还要求前端同学安装一整套后端的开发环境,费时费力不说,光维护这套不断变换的环境就要费不少精力。
因此,我们认为前后端分离的关键点在于前端拥有完整独立的开发、测试、部署的流程,与后端完全分离。
在赫尔墨斯项目中,我们把页面的组装完全放置到了前端项目,后端只提供AJAX的接口用于获取和提交数据。前端页面完全静态化,构建完毕之后连同相应的静态资源通过CI直接发布到CDN。
模块化开发在其它开发领域(比如客户端后端开发)已经实施了很多年了,而在前端开发领域,一直没有一个统一的模块化的规范。随着ES6 Module规范的落地,这个问题终于(部分)解决了。模块化开发的优势主要有以下几个方面。
如果说模块化是解决如何封装和复用一段逻辑代码的话,组件化要解决的是如何封装和复用一个用户界面元素,例如,一个按钮、一个弹出框,亦或是一个轮播图。由于浏览器原生并没有提供这么一套组件化开发的API,这个领域目前也是处在相对不稳定的状态中,各种框架层出不穷,比较有代表性的有React、Vue和Angular。我们最终选择的是Vue.js作为我们组件化开发的基础API(W3C实际上有一套Web Component的规范,目前已定稿,但是浏览器支持非常有限。同时功能上缺乏了现在框架普遍拥有的数据绑定、同构渲染等等)。
主要是基于以下几个方面的考虑。
Vue.js提供了一种单文件组件的格式允许把一个组件相关联的模板、逻辑和样式写在一个文件当中,通过上文提到的一个定制化的webpack loader可以把它转换为一个包含Vue.js的组件配置对象的模块被其它模块引用。
基于Vue.js,我们开发了一套适合移动端开发的组件库dora-ui,提供了一套符合我们团队业务需求的基础组件库,它主要由以下几个部分构成
示例 | 文档 | 组件开发目录示例 |
---|---|---|
组件到页面引用关系 | 页面到组件引用关系 |
---|---|
在工程标准化自动化方面,我们想要达到的目标是统一技术栈,保持技术栈的先进性,规范化代码样式以及自动化一切可以自动化的任务。
所有可以自动化的任务都应该被自动完成。
我们建立了统一的项目模板,基于约定大于配置的理念,简化了新项目创建的流程以及页面和组件的开发和调试。
为了方便开发和测试单个组件,我们在每个组件的目录下面会创建一个demo目录。在构建过程中,借助webpack的require.context API来获取components目录下所有组件的demo文件,随后为每个组件Demo创建一个路由。
var demoRequire = require.context('@component', true, /demo\/.*\.vue$/); //遍历取出所有demo组件 const demos = demoRequire.keys().map(demoKey => { var [componentName, demoName] = demoKey.split('/demo/'); componentName = componentName.substring(2); demoName = demoName.substring(0, demoName.lastIndexOf('.')); return { componentName: componentName, demoName: demoName, component: demoRequire(demoKey) } }); //组成key + value 形式的demo组件对象集合 const demosByComponent = _.groupBy(demos, demo => { return demo.componentName; }); //整个组件页面的路由 const routesByComponent = Object.keys(demosByComponent).map(componentName => { return { path: '/' + componentName, component: require('./component.vue'), meta: { componentName: componentName, demoComponents: demosByComponent[componentName] } } }); //组件页面内调试每个单独demo的路由 const routesByDemo = demos.map(demo => { return { path: '/' + demo.componentName + '/' + demo.demoName, component: demo.component, meta: { componentName: demo.componentName, } } });
前后端分离之后,为了加速前后端并行开发的效率,我们基于webpack-dev-server,实现了一套本地Mock服务,能够在本地开发环境模拟任意API请求的响应。
同时为了提升效率,根据模板工程目录的约定,这些Mock文件能够被自动发现同时一旦发生变更可以实时刷新。
关于页面加载性能优化,我们首先要建立监控体系,收集用户侧真实数据,然后基于数据进行页面加载的优化。
同时,为了进一步提升用户体验,我们还进行了前端离线化的支持。
建立一个完整的监控体系是性能优化的前提条件。我们认为,前端监控体系大体由3部分构成(下图)。
技术监控服务于开发人员,收集开发人员所需要的性能及异常相关的数据。
用户行为监控服务于产品和运营,主要收集用户在页面上操作的行为,比如点击、曝光等等。
展示查询提供可视化查询工具,通过报表、图表、仪表盘的形式,满足对于数据可视化的需求。
对于静态资源,从海外回源的成本非常高,通过对接海外的CDN供应商,能够在世界各地部署多个静态资源的缓存代理,根据用户的地理位置选择最近的位置进行静态资源的分发。
同时通过增加香港中间源,及中间源到源站的专线减少从海外直接回源源站造成的性能开销。
对于AJAX请求,在香港部署了SLB来做中转,SLB与后台服务是通过专线连接的。
由于主文档无法进行长缓存,针对主文档回源过于频繁的问题,我们通过在CDN边缘节点覆盖源站缓存设置,将主文档缓存30天,使得主文档回源减少(注意:用户侧看到的仍然是源站设置的缓存时间,用户侧设置为10分钟)。
同时,通过在发布流程当中加入主动清除海外CDN缓存的功能,来解决缓存更新的问题。
存在问题:
我们的做法是把第三方脚本打包到我们的代码里面,并抽取公共代码已增加缓存的效率,同时把所有静态资源和主文档公用一个域名。
由于境外行中场景网络不稳当,无法保持实时在线,我们有些工具类的页面比方说汇率助手等等实际上在离线情况下也能够使用。此外,离线化也能提升加载速度,因为主文档也不再需要网络请求了。
考虑到浏览器多平台兼容性问题,我们最终是基于HTML Application Cache API来打造了我们的离线化方案(下图)。
在构建流程中,通过分析页面资源依赖关系,自动生成资源manifest文件,这样就能够确保页面及资源发生变更时,manifest文件内容同步更新。
需要特别注意的是,当用户再次访问访问页面的时候,如果页面的manifest发生变更,浏览器会自动重新下载manifest里面的文件,完成之后会在applicationCache对象上发出updateready事件,但是并不会自动刷新页面,也就是说这个时候用户会看到之前的版本,而不是最新的版本。当用户再次进入这个页面的时候,将会访问到最新的版本。在大部分情况下,这都不是问题,因为移动端网页的停留时间是非常有限的。假设某一次页面更新非常重要,期待用户立即就进行页面刷新,我们可以在监听到updateready事件之后,给用户一个友好的提示,让他主动刷新页面(如上图左下所示)。
现在使用的静态页+前端渲染的策略,针对初次访问的用户在首屏时间上仍然有可改善的空间。后续我们会采用基于Vue的同构渲染+代码分割对于这一问题进行进一步优化。
对于离线化方案,AppCache未来会逐步被Service Worker所取代,无论从灵活性还是可扩展性而言,SW都更胜一筹。后续我们会逐步过渡到基于SW的方案,实现一个更加透明的网络层代理,能同时处理静态资源和动态请求。
Web平台正在以飞快地速度向前发展,比如WebGL、WebVR、HTTP/2、Service Worker、Web Assembly、WebRTC这些激动人心的功能逐渐在各大浏览器中落地,前端开发人员能够写出更快更酷炫的用户界面,用户能够得到更优质的Web体验。
在赫尔墨斯项目中,我们实施了前后端分离、模块化和组件化改造、流程自动化、接入了监控和报表系统,极大的提高了我们的开发效率和项目代码的可维护、可复用性,同时通过自动化的资源优化,确保了有效的优化策略被以极低的成本在多个项目中复用。
毓杰,美团点评前端技术专家,全栈开发工程师。2016年加入美团点评,负责境外度假前端研发组的工作。崇尚自由、开放、互通的技术平台,追求极致的用户体验和开发效率。
回答“思考题”、发现文章有错误、对内容有疑问,都可以来微信公众号(美团点评技术团队)后台给我们留言。我们每周会挑选出一位“优秀回答者”,赠送一份精美的小礼品。快来扫码关注我们吧!