美团点评境外度假团队前端项目开发实践总结

随着前端项目数量和规模越来越大,参与的人员也越来越多,如何在前端项目开发过程中保证优质的开发者体验和项目的可维护性,同时确保极致的用户体验将会是一个非常大的挑战。

为了应对这个挑战,美团点评境外度假前端研发团队自2016年6月起启动了面向C端用户的”赫尔墨斯”项目,主要围绕以下几个方面进行展开:

  • 前后端分离:前端拥有完整独立的开发、测试、部署的流程,与后端完全分离,减少沟通成本。
  • 模块化与组件化:封装可重用UI组件、业务逻辑,提升代码库的可复用性、可测试性。
  • 流程自动化:提升效率、避免重复手工工作、保证质量、自动资源优化等等。
  • 页面加载性能优化:建立前端监控体系、优化资源加载、使用离线化策略。

在之前的项目中,页面是由Java后端项目中通过FTL模板引擎拼装,前端团队会维护另外一个前端的项目,存放相应的CSS和JS文件,最后通过公司内部的Cortex系统打包发布。

这个流程的问题在于前端对于整个页面入口没有控制力,需要依赖后端的FTL拼装,页面的内容需要更改时,前后端同学就要反复沟通协调,整体效率比较差,容易出错,也不方便实现前端相关的优化。更坑的是有时候还要求前端同学安装一整套后端的开发环境,费时费力不说,光维护这套不断变换的环境就要费不少精力。

因此,我们认为前后端分离的关键点在于前端拥有完整独立的开发、测试、部署的流程,与后端完全分离。

在赫尔墨斯项目中,我们把页面的组装完全放置到了前端项目,后端只提供AJAX的接口用于获取和提交数据。前端页面完全静态化,构建完毕之后连同相应的静态资源通过CI直接发布到CDN。

模块化开发在其它开发领域(比如客户端后端开发)已经实施了很多年了,而在前端开发领域,一直没有一个统一的模块化的规范。随着ES6 Module规范的落地,这个问题终于(部分)解决了。模块化开发的优势主要有以下几个方面。

  • 更好的代码组织结构和开发协作:通过细致的文件夹、文件拆分,更易于管理复杂的代码库,更易于多人协作开发,降低文件合并时候冲突的发生概率,方便编写单元测试。
  • 依赖管理:不再需要手动管理脚本的加载顺序。
  • 优化:
    • 代码打包(Bundle):合并小模块,抽取公共模块,在资源请求数和浏览器缓存利用方面进行合适的取舍。
    • 代码分割(Split):允许按需加载JS代码(分路由、异步组件),解决单页面应用(SPA)首屏加载速度问题。
    • Tree Shaking:利用ES6模块的静态化特性,可以在构建过程中分析出代码库中未使用到的代码,从最终的bundle中去除,从而减少JS Bundle的尺寸。
    • Scope Hoisting:ES6模块内容导入和导出绑定是活动的,可以将多个小模块合并到一个函数当中,对于重复变量名进行合适的重命名,从而减少Bundle的尺寸和提升加载速度。

如果说模块化是解决如何封装和复用一段逻辑代码的话,组件化要解决的是如何封装和复用一个用户界面元素,例如,一个按钮、一个弹出框,亦或是一个轮播图。由于浏览器原生并没有提供这么一套组件化开发的API,这个领域目前也是处在相对不稳定的状态中,各种框架层出不穷,比较有代表性的有React、Vue和Angular。我们最终选择的是Vue.js作为我们组件化开发的基础API(W3C实际上有一套Web Component的规范,目前已定稿,但是浏览器支持非常有限。同时功能上缺乏了现在框架普遍拥有的数据绑定、同构渲染等等)。

主要是基于以下几个方面的考虑。

  • 体积:19kB(min+gzip)
  • API和学习成本:
    • 声明式组件模板和分离样式表,更接近于传统开发模式,抵触心理小。
    • 响应式的组件状态跟踪:更新状态代码更简洁,组件树重新渲染效率更高。
    • 清晰简洁的生命周期钩子函数和单向数据流:页面逻辑和状态更新更可控。
    • 运行时报错和告警详细:方便新手入门和规避常见错误。
  • 工具链完整性:webpack Loader(加载Vue单文件组件)、开发者工具(Dev Tools)、脚手架(vue-cli)、单元测试友好(vue-test-utils)。
  • 运行时性能:
    • Virtual DOM来管理组件树渲染到真实DOM的状态同步,使用高效的算法来最小化DOM操作的次数。
    • 由于响应式设计,不需要优化组件树再次渲染的范围。
    • 组件树静态部分被单独处理,重新渲染不需要重新构建。
  • 同构渲染:
    • 高性能、开箱即用的方案,包括前后端可用的路由和状态管理组件,降低了使用的门槛。
    • 深度webpack集成,简化了代码分割和构建调试流程。

Vue.js提供了一种单文件组件的格式允许把一个组件相关联的模板、逻辑和样式写在一个文件当中,通过上文提到的一个定制化的webpack loader可以把它转换为一个包含Vue.js的组件配置对象的模块被其它模块引用。

基于Vue.js,我们开发了一套适合移动端开发的组件库dora-ui,提供了一套符合我们团队业务需求的基础组件库,它主要由以下几个部分构成

  • 20个Vue.js 2.0兼容组件,涵盖布局、导航、数据输入、数据展示、信息反馈等等方面。
  • 组件文档:每一个组件需要有一个相应的Readme(markdown格式)文件,描述组件的用途、属性、事件、插槽等等。
  • 组件示例:每一个组件可以有一个或者多个示例,来展示组件的用法。
  • 组件复用度查询:可以快速查找一个组件被多少个页面所引用以及一个页面引用了多少个组件。
    • webpack plugin:在项目构建时候收集项目页面和组件引用关系,输出一个JSON文件。
    • 查询页面:通过读取上述JSON文件,提供一个界面供开发人员查询。
示例 文档 组件开发目录示例
组件到页面引用关系 页面到组件引用关系

在工程标准化自动化方面,我们想要达到的目标是统一技术栈,保持技术栈的先进性,规范化代码样式以及自动化一切可以自动化的任务。

所有可以自动化的任务都应该被自动完成。

工程模板

我们建立了统一的项目模板,基于约定大于配置的理念,简化了新项目创建的流程以及页面和组件的开发和调试。

本地组件测试开发

为了方便开发和测试单个组件,我们在每个组件的目录下面会创建一个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,
    }
  }
});

本地Mock服务

前后端分离之后,为了加速前后端并行开发的效率,我们基于webpack-dev-server,实现了一套本地Mock服务,能够在本地开发环境模拟任意API请求的响应。

同时为了提升效率,根据模板工程目录的约定,这些Mock文件能够被自动发现同时一旦发生变更可以实时刷新。

关于页面加载性能优化,我们首先要建立监控体系,收集用户侧真实数据,然后基于数据进行页面加载的优化。

同时,为了进一步提升用户体验,我们还进行了前端离线化的支持。

监控体系

建立一个完整的监控体系是性能优化的前提条件。我们认为,前端监控体系大体由3部分构成(下图)。

技术监控服务于开发人员,收集开发人员所需要的性能及异常相关的数据。

用户行为监控服务于产品和运营,主要收集用户在页面上操作的行为,比如点击、曝光等等。

展示查询提供可视化查询工具,通过报表、图表、仪表盘的形式,满足对于数据可视化的需求。

网络链路优化

对于静态资源,从海外回源的成本非常高,通过对接海外的CDN供应商,能够在世界各地部署多个静态资源的缓存代理,根据用户的地理位置选择最近的位置进行静态资源的分发。

同时通过增加香港中间源,及中间源到源站的专线减少从海外直接回源源站造成的性能开销。

对于AJAX请求,在香港部署了SLB来做中转,SLB与后台服务是通过专线连接的。

主文档回源优化

由于主文档无法进行长缓存,针对主文档回源过于频繁的问题,我们通过在CDN边缘节点覆盖源站缓存设置,将主文档缓存30天,使得主文档回源减少(注意:用户侧看到的仍然是源站设置的缓存时间,用户侧设置为10分钟)。

同时,通过在发布流程当中加入主动清除海外CDN缓存的功能,来解决缓存更新的问题。

域名收敛 & 减少请求数

存在问题:

  • 页面引用的第三方脚本,比如监控、打点,缺乏海外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年加入美团点评,负责境外度假前端研发组的工作。崇尚自由、开放、互通的技术平台,追求极致的用户体验和开发效率。

展开阅读全文

Git 实用技巧

11-24
这几年越来越多的开发团队使用了Git,掌握Git的使用已经越来越重要,已经是一个开发者必备的一项技能;但很多人在刚开始学习Git的时候会遇到很多疑问,比如之前使用过SVN的开发者想不通Git提交代码为什么需要先commit然后再去push,而不是一条命令一次性搞定; 更多的开发者对Git已经入门,不过在遇到一些代码冲突、需要恢复Git代码时候就不知所措,这个时候哪些对 Git掌握得比较好的少数人,就像团队中的神一样,在队友遇到 Git 相关的问题的时候用各种流利的操作来帮助队友于水火。 我去年刚加入新团队,发现一些同事对Git的常规操作没太大问题,但对Git的理解还是比较生疏,比如说分支和分支之间的关联关系、合并代码时候的冲突解决、提交代码前未拉取新代码导致冲突问题的处理等,我在协助处理这些问题的时候也记录各种问题的解决办法,希望整理后通过教程帮助到更多对Git操作进阶的开发者。 本期教程学习方法分为“掌握基础——稳步进阶——熟悉协作”三个层次。从掌握基础的 Git的推送和拉取开始,以案例进行演示,分析每一个步骤的操作方式和原理,从理解Git 工具的操作到学会代码存储结构、演示不同场景下Git遇到问题的不同处理方案。循序渐进让同学们掌握Git工具在团队协作中的整体协作流程。 在教程中会通过大量案例进行分析,案例会模拟在工作中遇到的问题,从最基础的代码提交和拉取、代码冲突解决、代码仓库的数据维护、Git服务端搭建等。为了让同学们容易理解,对Git简单易懂,文章中详细记录了详细的操作步骤,提供大量演示截图和解析。在教程的最后部分,会从提升团队整体效率的角度对Git工具进行讲解,包括规范操作、Gitlab的搭建、钩子事件的应用等。 为了让同学们可以利用碎片化时间来灵活学习,在教程文章中大程度降低了上下文的依赖,让大家可以在工作之余进行学习与实战,并同时掌握里面涉及的Git不常见操作的相关知识,理解Git工具在工作遇到的问题解决思路和方法,相信一定会对大家的前端技能进阶大有帮助。

实用主义学Python(小白也容易上手的Python实用案例)

12-24
原价169,限时立减100元! 系统掌握Python核心语法16点,轻松应对工作中80%以上的Python使用场景! 69元=72讲+源码+社群答疑+讲师社群分享会  【哪些人适合学习这门课程?】 1)大学生,平时只学习了Python理论,并未接触Python实战问题; 2)对Python实用技能掌握薄弱的人,自动化、爬虫、数据分析能让你快速提高工作效率; 3)想学习新技术,如:人工智能、机器学习、深度学习等,这门课程是你的必修课程; 4)想修炼更好的编程内功,优秀的工程师肯定不能只会一门语言,Python语言功能强大、使用高效、简单易学。 【超实用技能】 从零开始 自动生成工作周报 职场升级 豆瓣电影数据爬取 实用案例 奥运冠军数据分析 自动化办公:通过Python自动化分析Excel数据并自动操作Word文档,最终获得一份基于Excel表格的数据分析报告。 豆瓣电影爬虫:通过Python自动爬取豆瓣电影信息并将电影图片保存到本地。 奥运会数据分析实战 简介:通过Python分析120年间奥运会的数据,从不同角度入手分析,从而得出一些有趣的结论。 【超人气老师】 二两 中国人工智能协会高级会员 生成对抗神经网络研究者 《深入浅出生成对抗网络:原理剖析与TensorFlow实现》一书作者 阿里云大学云学院导师 前大型游戏公司后端工程师 【超丰富实用案例】 0)图片背景去除案例 1)自动生成工作周报案例 2)豆瓣电影数据爬取案例 3)奥运会数据分析案例 4)自动处理邮件案例 5)github信息爬取/更新提醒案例 6)B站百大UP信息爬取与分析案例 7)构建自己的论文网站案例
©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值