浅谈前端工程化

前端工程化

1. 背景及现状

  不知道你的团队如何定义前端开发,据我所知,时至今日仍然有很多团队会把前端开发归类为产品或者设计岗位(这点本人深有体会!!!),虽然身份之争多少有些无谓,但我对这种偏见还是心存芥蒂,试着从工程的角度系统的介绍一下我对前端的理解。只要我们还把自己的工作看作为一项软件开发活动,那么我相信读过下面的内容你也一定会有所共鸣。

  现如今web端可谓包罗万象,产品形态五花八门,涉猎极广,什么高大上的基础库/框架,拽炫酷的宣传页面,还有屌炸天的小游戏……不过这些一两个文件的小项目并非是前端技术的主要应用场景,更具商业价值的则是复杂的Web应用,它们功能完善,界面繁多,为用户提供了完整的产品体验,可能是购物平台,可能是社交网站,可能是管理、监测系统,可视化监控平台……

  从本质上讲,所有Web应用都是一种运行在网页浏览器中的软件,这些软件的图形用户界面(Graphical User Interface,简称GUI)即为前端。

  如此复杂的Web应用,动辄几十上百人共同开发维护,其前端界面通常也颇具规模,工程量不亚于一般的传统GUI软件。

  尽管Web应用的复杂程度与日俱增,用户对其前端界面也提出了更高的要求,但时至今日仍然没有多少前端开发者会从软件工程的角度去思考前端开发,来助力团队的开发效率,更有甚者还对前端保留着”如玩具般简单“的刻板印象,日复一日,刀耕火种。

  “历史悠久”的前端开发,始终像是放养的野孩子,原始如斯,不免让人慨叹!

  随着前端开发复杂度的日益增加,各种优秀的组件框架也遍地开花。同时,我们面临业务规模的快速发展和工程师团队的不断扩张。目前来说,Web业务日益复杂化和多元化,前端开发已经由以 WebPage 模式为主转变为以 WebApp 模式为主了。现在随便找个前端项目,都已经不是过去的拼个页面+搞几个jQuery插件就能完成的了。工程复杂了就会产生许多问题,如何进行高效的多人协作?如何保证项目的可维护性?如何提高项目的开发质量?...

  我们希望能在日常开发中制订一个规范化的前端工作流,很好地规范统一项目的模块化开发和前端资源,让代码的维护和互相协作更加容易更加方便,令前端开发自动化成为一种习惯。同时,让大家能够释放生产力,提高开发效率,更好更快地完成团队开发以及项目后期维护和扩展。

2. 构建前端工程

2.1 第一阶段:库/框架选型

  前端工程建设的第一项任务就是根据项目特征进行技术选型(ps:以上排序不分排名先后)。

  基本上现在没有人完全从0开始做网站,哪怕是个传统项目用个 jQuery 都很正常吧,React、Vue、Angular等框架横空出世,解放了不少生产力,合理的技术选型可以为项目节省许多工程量这点毋庸置疑。

2.2 第二阶段:构建、管理工具

img

  选型之后基本上就可以开始敲码了,不过光解决开发效率还不够,必须要兼顾运行性能。前端工程进行到第二阶段会选型一种或多种构建工具,对代码进行压缩、校验、管理,之后再以页面为单位进行简单的资源合并(ps:以上排序不分排名先后)。

  前端开发工程化程度之低,常常出乎我们的意料。很多人在大型互联网公司工作时是没有多少概念的,直到离开大公司的温室,去到业界与更多的团队交流才发现,能做到这个阶段在业界来说已然超出平均水平,属于“具备较高工程化程度”的团队了,查看网上形形色色的网页源代码,能做到最基本的JS/CSS压缩的Web应用都已跨入标准互联网公司行列,不难理解为什么很多前端团队对于前端工程构建的认知还仅停留在“压缩、校验、合并”这种程度。

2.3 第三阶段:JS/CSS模块化开发

img

  分而治之是软件工程中的重要思想,是复杂系统开发和维护的基石,这点放在前端开发中同样适用。在解决了基本开发效率运行效率问题之后,前端团队开始思考维护效率,模块化是目前前端最流行的分治手段(ps:以上排序不分排名先后)。

  很多人觉得模块化开发的工程意义是复用,其实应该是模块化开发的最大价值应该是分治。不管你将来是否要复用某段代码,你都有充分的理由将其分治为一个模块。

  JS 模块化方案很多,AMD、CMD、UMD、CommonJS、ES6 Module 等,对应的框架和工具也一大堆。

  CSS 模块化开发基本都是在 Less、Sass、Stylus 等预处理器的 import、mixin 特性支持下实现的。

  虽然上述这些技术由来已久,在如今这个 “组件化开发” 的时代略显落伍,但想想业界的绝大多数团队的工程化落后程度,放眼望去,毫不夸张的说,能达到第三阶段的前端团队已属于高端行列,基本具备了开发维护一般规模Web应用的能力。

  尤其是 Node 的出现,除了用 javasript 可以写服务端业务,众多基于它的模块化、构建、开发工具真的是丰富了整个前端生态圈。

2.4 第四阶段:组件化开发与资源管理

  前端是一种技术问题较少、工程问题较多的软件开发领域。

  当我们要开发一款完整的Web应用时,前端将面临更多的工程问题,比如:

    1. 大体量:多功能、多页面、多状态、多系统;
   2. 大规模:多人甚至多团队合作开发;
   3. 高性能:CDN部署、缓存控制、文件指纹、缓存复用、请求合并、按需加载、同步/异步加载、移动端首屏CSS内嵌、HTTP 2服务端资源推送。

  这些无疑是一系列严肃的系统工程问题。前面讲的三个阶段虽然相比曾经“茹毛饮血”的时代进步不少,但用于支撑第四阶段的多人合作开发以及精细的性能优化似乎还欠缺点什么。到底,缺什么呢?

2.4.1 组件化开发

  前端历来以“简单”著称,在前端开发者群体中,小而美的价值观占据着主要的话语权,甚至成为了某种信仰,想与其他人交流一下工程方面的心得,得到的回应往往都是两个字:太重。

  工程方案其实也可以小而美!只不过它的小而美不是指代码量,而是指“规则”。找到问题的根源,用最少最简单明了的规则制定出最容易遵守最容易理解的开发规范或工具,以提升开发效率和工程质量,这同样是小而美的典范!

  分治的确是非常重要的工程优化手段。在我看来,前端作为一种GUI软件,光有JS/CSS的模块化还不够,对于UI组件的分治也有着同样迫切的需求:

img

  如上图,这是我所信仰的前端组件化开发理念,简单解读一下:

    1. 页面上的每个独立的可视/可交互区域视为一个组件;
   2. 每个组件对应一个工程目录,组件所需的各种资源都在这个目录下就近维护;
   3. 由于组件具有独立性,因此组件与组件之间可以 自由组合;
   4. 页面只不过是组件的容器,负责组合组件形成功能完整的界面;
   5. 当不需要某个组件,或者想要替换组件时,可以整个目录删除/替换。

  其中第二项描述的就近维护原则,是最具工程价值的地方,它为前端开发提供了很好的分治策略,每个开发者都将清楚的知道,自己所开发维护的功能单元,其代码必然存在于对应的组件目录中,在那个目录下能找到有关这个功能单元的所有内部逻辑,样式也好,JS也好,页面结构也好,都在那里。

  组件化开发具有较高的通用性,无论是前端渲染的单页面应用,还是后端模板渲染的多页面应用,组件化开发的概念都能适用。组件HTML部分根据业务选型的不同,可以是静态的HTML文件,可以是前端模板,也可以是后端模板:

img

  不同的技术选型决定了不同的组件封装和调用策略。

  基于这样的工程理念,我们很容易将系统以独立的组件为单元进行分工划分:

img

  由于系统功能被分治到独立的模块或组件中,粒度比较精细,组织形式松散,开发者之间不会产生开发时序的依赖,大幅提升并行的开发效率,理论上允许随时加入新成员认领组件开发或维护工作,也更容易支持多个团队共同维护一个大型站点的开发。

  结合前面提到的模块化开发,整个前端项目可以划分为这么几种开发概念:

名称说明举例
JS模块 独立的算法和数据单元 浏览器环境检测(detect),网络请求(ajax),应用配置(config),DOM操作(dom),工具函数(utils),以及组件里的JS单元
CSS模块 独立的功能性样式单元 reset样式,栅格系统(grid),字体图标(icon-fonts),动画样式(animate),以及组件里的CSS单元
UI组件 独立的可视/可交互功能单元 页头(header),页尾(footer),导航栏(nav),搜索框(search)
页面 前端这种GUI软件的界面状态,是UI组件的容器 首页(index),列表页(list),用户管理(user)
应用 整个项目或整个站点被称之为应用,由多个页面组成,甚至由多个应用或者系统组成 SPA(单一页面应用)、PWA(渐进式Web应用)、微前端

  以上5种开发概念以相对较少的规则组成了前端开发的基本工程结构,基于这些理念,前端开发就成了这个样子:

示意图描述
img 整个Web应用由页面组成
img 页面由组件组成
img 一个组件一个目录,资源就近维护
img 组件可组合, 组件的JS可依赖其他JS模块, CSS可依赖其他CSS单元

  综合上面的描述,对于一般中小规模的项目,大致可以规划出这样的源码目录结构:

img

  如果项目规模较大,涉及多个团队协作,还可以将具有相关业务功能的页面组织在一起,形成一个子系统,进一步将整个站点拆分出多个子系统来分配给不同团队维护。以上架构设计历经许多不同公司不同业务场景的前端团队验证,收获了不错的口碑,是行之有效的前端工程分治方案。

2.4.2 静态资源管理

  上面提到的模块化/组件化开发,仅仅描述了一种开发理念,也可以认为是一种开发规范,倘若你认可这规范,对它的分治策略产生了共鸣,那我们就可以继续聊聊它的具体实现了。

  很明显,模块化/组件化开发之后,我们最终要解决的,就是模块/组件加载的技术问题。然而前端与客户端GUI软件有一个很大的不同:

  前端是一种远程部署,运行时增量下载的GUI软件!

  前端应用没有安装过程,其所需程序资源都部署在远程服务器,用户使用浏览器访问不同的页面来加载不同的资源,随着页面访问的增加,渐进式的将整个程序下载到本地运行,“增量下载”是前端在工程上有别于客户端GUI软件的根本原因。

img

  上图展示了一款界面繁多功能丰富的应用,如果采用Web实现,相信也是不小的体量,如果用户第一次访问页面就强制其加载全站静态资源再展示,相信会有很多用户因为失去耐心而流失。根据“增量”的原则,我们应该精心规划每个页面的资源加载策略,使得用户无论访问哪个页面都能按需加载页面所需资源,没访问过的无需加载,访问过的可以缓存复用,最终带来流畅的应用体验。

  这正是Web应用“免安装”的魅力所在。由“增量”原则引申出的前端优化技巧几乎成为了性能优化的核心:

  • 有加载相关的按需加载、延迟加载、预加载、请求合并等策略
  • 有缓存相关的浏览器缓存利用,缓存更新、缓存共享、非覆盖式发布等方案
  • 还有复杂的 BigRender、BigPipe、Quickling、PageCache 等技术

  这些优化方案无不围绕着如何将增量原则做到极致而展开。所以:

  第四阶段前端开发最迫切需要做好的就是在基础架构中贯彻增量原则。

  相信这种贯彻不会随着时间的推移而改变,在可预见的未来,无论在ES5亦或者ES6(泛指下一代标准)时代,无论是 AMD、CMD、UMD、CommonJS 亦或者 ES6 Module 时代,无论端内技术如何变迁,我们都有足够充分的理由要做好前端程序资源的增量加载。正如前面说到的,第三阶段前端工程缺少点什么呢?我觉得是在其基础架构中缺少这样一种“智能”的资源加载方案。没有这样的方案,很难将前端应用的规模发展到第四阶段,很难实现落地前面介绍的那种组件化开发方案,也很难让多方合作高效率的完成一项大型应用的开发,并保证其最终运行性能良好。

  在第四阶段,我们需要强大的工程化手段来管理”玩具般简单“的前端开发。

2.5 第五阶段:规范化

  模块化和组件化确定了开发模型,而这些东西的实现就需要规范去落实。规范化其实是工程化中很重要的一个部分,项目初期规范制定的好坏会直接影响到后期的开发质量:

    * 目录结构的制定
   
   * 编码规范
   
   * 前后端接口规范
   
   * 输入输出文档
   
   * 文档规范
   
   * 组件管理
   
   * Git分支管理
   
   * Commit描述规范
   
   * 定期CodeReview
   
   * 视觉图标规范 ...

2.6 第六阶段:自动化

  现在我们假设你项目中的发布流程是这样的:

    1. 提交代码

   2. 合并代码

   3. 打包构建

   4. 备份服务器上项目文件

   5. 将打包文件上传服务器项目目录

   6. 合并至master分支...

  上面这一系列的过程,可能都是你手动一步一步的打开文件、敲命令等纯劳力的重复性去做,而且还要保证每个步骤都是正确的才能进行下一步操作,一旦发生错误还没有可追溯可跟踪的相关日志和记录。

在业界内有这么一句话:任何简单机械的重复劳动都应该让机器去完成。

  现代前端技术不再是以前刀耕火种的年代了。所以前端工程化的很多脏活累活都应该交给自动化工具来完成。如现在社区和市场上有 Jenkins、Travis CI、Circle CI、Codeship 等很多知名持续集成和持续部署工具(CI/CD),这些工具都可帮你自动完成构建、测试、部署代码的过程。

3. 总结

  个人总结经验,认为前端工程化主要应该从模块化、组件化、规范化、自动化四个方面来思考。如何选型技术、如何定制规范、如何分治系统、如何优化性能、如何加载资源,当你从切图开始转变为思考这些问题的时候,我想说:你好,工程师!

  最后要特别感谢 Fouber 工程师的精心分享。

posted @ 2018-09-01 16:54  Feesir  阅读(15766)  评论(2编辑  收藏  举报