摘自 https://github.com/fouber/blog/blob/master/201508/01.md#
php
喂喂喂,那个切图的,把页面写好就发给研发工程师套模板吧。css
你好,切图仔。html
不知道你的团队如何定义前端开发,据我所知,时至今日仍然有不少团队会把前端开发归类为产品或者设计岗位,虽然身份之争多少有些无谓,但我对这种偏见仍是心存芥蒂,酝酿了许久,决定写一个系列的文章,试着从工程的角度系统的介绍一下我对前端,尤为是Web前端的理解。前端
只要咱们还把本身的工做看做为一项软件开发活动,那么我相信读过下面的内容你也必定会有所共鸣。jquery
现现在前端可谓一应俱全,产品形态五花八门,涉猎极广,什么高大上的基础库/框架,拽炫酷的宣传页面,还有屌炸天的小游戏……不过这些一两个文件的小项目并不是是前端技术的主要应用场景,更具商业价值的则是复杂的Web应用,它们功能完善,界面繁多,为用户提供了完整的产品体验,多是新闻聚合网站,多是在线购物平台,多是社交网络,多是金融信贷应用,多是音乐互动社区,也多是视频上传与分享平台……git
从本质上讲,全部Web应用都是一种运行在网页浏览器中的软件,这些软件的图形用户界面(Graphical User Interface,简称GUI)即为前端。github
如此复杂的Web应用,动辄几十上百人共同开发维护,其前端界面一般也颇具规模,工程量不亚于通常的传统GUI软件:ajax
尽管Web应用的复杂程度与日俱增,用户对其前端界面也提出了更高的要求,但时至今日仍然没有多少前端开发者会从软件工程的角度去思考前端开发,来助力团队的开发效率,更有甚者还对前端保留着”如玩具般简单“的刻板印象,日复一日,刀耕火种。算法
历史悠久的前端开发,始终像是放养的野孩子,原始如斯,难免让人慨叹!json
如今的前端开发倒也并不是一无全部,回顾一下曾经经历过或听闻过的项目,为了提高其前端开发效率和运行性能,前端团队的工程建设大体会经历三个阶段:
前端工程建设的第一项任务就是根据项目特征进行技术选型。
基本上如今没有人彻底从0开始作网站,哪怕是政府项目用个jquery都很正常吧,React/Angularjs等框架横空出世,解放了很多生产力,合理的技术选型能够为项目节省许多工程量这点毋庸置疑。
选型以后基本上就能够开始敲码了,不过光解决开发效率还不够,必需要兼顾运行性能。前端工程进行到第二阶段会选型一种构建工具,对代码进行压缩,校验,以后再以页面为单位进行简单的资源合并。
前端开发工程化程度之低,经常出乎个人意料,我以前在百度工做时是没有多少概念的,直到离开大公司的温室,去到业界与更多的团队交流才发现,能作到这个阶段在业界来讲已然超出平均水平,属于“具有较高工程化程度”的团队了,查看网上形形色色的网页源代码,能作到最基本的JS/CSS压缩的Web应用都已跨入标准互联网公司行列,不难理解为何不少前端团队对于前端工程构建的认知还仅停留在“压缩、校验、合并”这种程度。
分而治之是软件工程中的重要思想,是复杂系统开发和维护的基石,这点放在前端开发中一样适用。在解决了基本开发效率运行效率问题以后,前端团队开始思考维护效率,模块化是目前前端最流行的分治手段。
不少人以为模块化开发的工程意义是复用,我不太承认这种见解,在我看来,模块化开发的最大价值应该是分治,是分治,分治!(重说三)。
无论你未来是否要复用某段代码,你都有充分的理由将其分治为一个模块。
JS模块化方案不少,AMD/CommonJS/UMD/ES6 Module等,对应的框架和工具也一大堆,提及来很烦,你们自行百度吧;CSS模块化开发基本都是在less、sass、stylus等预处理器的import/mixin特性支持下实现的。
虽然这些技术由来已久,在现在这个“言必及React”的时代略显落伍,但想一想业界的绝大多数团队的工程化落后程度,放眼望去,绝不夸张的说,能达到第三阶段的前端团队已属于高端行列,基本具有了开发维护通常规模Web应用的能力。
然而,作到这些就够了么?Naive!
前端是一种技术问题较少、工程问题较多的软件开发领域。
当咱们要开发一款完整的Web应用时,前端将面临更多的工程问题,好比:
扩展阅读:大公司里怎样开发和部署前端代码?
这些无疑是一系列严肃的系统工程问题。
前面讲的三个阶段虽然相比曾经“茹毛饮血”的时代进步很多,但用于支撑第四阶段的多人合做开发以及精细的性能优化彷佛还欠缺点什么。
到底,缺什么呢?
读过《人月神话》的人应该都据说过,软件工程 没有银弹。没错,前端开发一样没有银弹,但是如今是连™铅弹都没有的年月!(刚有了BB弹,摔)
前端从来以“简单”著称,在前端开发者群体中,小而美的价值观占据着主要的话语权,甚至成为了某种信仰,想与其余人交流一下工程方面的心得,获得的回应每每都是两个字:过重。
重你妹!你的脑容量只有4K吗?
工程方案其实也能够小而美!只不过它的小而美不是指代码量,而是指“规则”。找到问题的根源,用最少最简单明了的规则制定出最容易遵照最容易理解的开发规范或工具,以提高开发效率和工程质量,这一样是小而美的典范!
2011年我有幸参与到 FIS 项目中,与百度众多大中型项目的前端研发团队共同合做,不断探索实践前端开发的工程化解决方案,13年离开百度去往UC,面对彻底不一样的产品形态,不一样的业务场景,不一样的适配终端,甚至不一样的网络环境,过往的方法论仍然可以快速落地,为多个团队的不一样业务场景量身定制出合理的前端解决方案。
这些经历让我明悟了一个道理:
进入第四阶段,咱们只需作好两件事就能大幅提高前端开发效率,而且兼顾运行性能,那就是——组件化开发与资源管理。
分治的确是很是重要的工程优化手段。在我看来,前端做为一种GUI软件,光有JS/CSS的模块化还不够,对于UI组件的分治也有着一样迫切的需求:
如上图,这是我所信仰的前端组件化开发理念,简单解读一下:
其中第二项描述的就近维护原则,是我以为最具工程价值的地方,它为前端开发提供了很好的分治策略,每一个开发者都将清楚的知道,本身所开发维护的功能单元,其代码必然存在于对应的组件目录中,在那个目录下能找到有关这个功能单元的全部内部逻辑,样式也好,JS也好,页面结构也好,都在那里。
组件化开发具备较高的通用性,不管是前端渲染的单页面应用,仍是后端模板渲染的多页面应用,组件化开发的概念都能适用。组件HTML部分根据业务选型的不一样,能够是静态的HTML文件,能够是前端模板,也能够是后端模板:
不一样的技术选型决定了不一样的组件封装和调用策略。
基于这样的工程理念,咱们很容易将系统以独立的组件为单元进行分工划分:
因为系统功能被分治到独立的模块或组件中,粒度比较精细,组织形式松散,开发者之间不会产生开发时序的依赖,大幅提高并行的开发效率,理论上容许随时加入新成员认领组件开发或维护工做,也更容易支持多个团队共同维护一个大型站点的开发。
结合前面提到的模块化开发,整个前端项目能够划分为这么几种开发概念:
名称 | 说明 | 举例 |
---|---|---|
JS模块 | 独立的算法和数据单元 | 浏览器环境检测(detect),网络请求(ajax),应用配置(config),DOM操做(dom),工具函数(utils),以及组件里的JS单元 |
CSS模块 | 独立的功能性样式单元 | 栅格系统(grid),字体图标(icon-fonts),动画样式(animate),以及组件里的CSS单元 |
UI组件 | 独立的可视/可交互功能单元 | 页头(header),页尾(footer),导航栏(nav),搜索框(search) |
页面 | 前端这种GUI软件的界面状态,是UI组件的容器 | 首页(index),列表页(list),用户管理(user) |
应用 | 整个项目或整个站点被称之为应用,由多个页面组成 |
以上5种开发概念以相对较少的规则组成了前端开发的基本工程结构,基于这些理念,我眼中的前端开发就成了这个样子:
示意图 | 描述 |
---|---|
![]() |
整个Web应用由页面组成 |
![]() |
页面由组件组成 |
![]() |
一个组件一个目录,资源就近维护 |
![]() |
组件可组合, 组件的JS可依赖其余JS模块, CSS可依赖其余CSS单元 |
综合上面的描述,对于通常中小规模的项目,大体能够规划出这样的源码目录结构:
若是项目规模较大,涉及多个团队协做,还能够将具备相关业务功能的页面组织在一块儿,造成一个子系统,进一步将整个站点拆分出多个子系统来分配给不一样团队维护,针对这种状况后面我会单开文章详细介绍。
以上架构设计历经许多不一样公司不一样业务场景的前端团队验证,收获了不错的口碑,是行之有效的前端工程分治方案。
吐槽:我本人很是反对某些前端团队将前端开发划分为“JS开发”和“页面重构”两种岗位,更倾向于组件粒度的开发理念,对GUI软件开发的分工规划应该以功能为单位,而不是开发语言;对开发者的技术要求也应该是掌握完整的端内技术。
上面提到的模块化/组件化开发,仅仅描述了一种开发理念,也能够认为是一种开发规范,假若你承认这规范,对它的分治策略产生了共鸣,那咱们就能够继续聊聊它的具体实现了。
很明显,模块化/组件化开发以后,咱们最终要解决的,就是模块/组件加载的技术问题。然而前端与客户端GUI软件有一个很大的不一样:
前端是一种远程部署,运行时增量下载的GUI软件
前端应用没有安装过程,其所需程序资源都部署在远程服务器,用户使用浏览器访问不一样的页面来加载不一样的资源,随着页面访问的增长,渐进式的将整个程序下载到本地运行,“增量下载”是前端在工程上有别于客户端GUI软件的根本缘由。
上图展现了一款界面繁多功能丰富的应用,若是采用Web实现,相信也是不小的体量,若是用户第一次访问页面就强制其加载全站静态资源再展现,相信会有不少用户由于失去耐心而流失。根据“增量”的原则,咱们应该精心规划每一个页面的资源加载策略,使得用户不管访问哪一个页面都能按需加载页面所需资源,没访问过的无需加载,访问过的能够缓存复用,最终带来流畅的应用体验。
这正是Web应用“免安装”的魅力所在。
由“增量”原则引伸出的前端优化技巧几乎成为了性能优化的核心,有加载相关的按需加载、延迟加载、预加载、请求合并等策略;有缓存相关的浏览器缓存利用,缓存更新、缓存共享、非覆盖式发布等方案;还有复杂的BigRender、BigPipe、Quickling、PageCache等技术。这些优化方案无不围绕着如何将增量原则作到极致而展开。
因此我以为:
第四阶段前端开发最迫切须要作好的就是在基础架构中贯彻增量原则。
相信这种贯彻不会随着时间的推移而改变,在可预见的将来,不管在HTTP1.x仍是HTTP2.0时代,不管在ES5亦或者ES6/7时代,不管是AMD/CommonJS/UMD亦或者ES6 module时代,不管端内技术如何变迁,咱们都有足够充分的理由要作好前端程序资源的增量加载。
正如前面说到的,第三阶段前端工程缺乏点什么呢?我以为是在其基础架构中缺乏这样一种“智能”的资源加载方案。没有这样的方案,很难将前端应用的规模发展到第四阶段,很难实现落地前面介绍的那种组件化开发方案,也很难让多方合做高效率的完成一项大型应用的开发,并保证其最终运行性能良好。在第四阶段,咱们须要强大的工程化手段来管理”玩具般简单“的前端开发。
在个人印象中,Facebook是这方面探索的伟大先驱之一,早在2010年的Velocity China大会上,来自Facebook的David Wei博士就为业界展现了他们使人惊艳的静态网页资源管理和优化技术。
David Wei博士在当年的交流会上提到过一些关于Facebook的一些产品数据:
- Facebook整站有10000+个静态资源;
- 每一个静态资源都有可能被翻译成超过100种语言版本;
- 每种资源又会针对浏览器生成3种不一样的版本;
- 要针对不一样带宽的用户作5种不一样的打包方法;
- 有三、4个不一样的用户组,用于小批次体验新的产品功能;
- 还要考虑不一样的送达方法,能够直接送达,或者经过iframe的方式提高资源并行加载的速度;
- 静态资源的压缩和非压缩状态可切换,用于调试和定位线上问题
这是一个状态爆炸的问题,将全部状态乘起来,整个网站的资源组合方式会达到几百万种之多(去重以后统计大概有300万种组合方式)。支撑这么大规模前端项目运行的底层架构正是魏博士在那次演讲中分享的Static Resource Management System(静态资源管理系统),用以解决Facebook项目中有关前端工程的3D问题(Development,Deployment,Debugging)。
那段时间 FIS 项目正好遇到瓶颈,当时的FIS仍是一个用php写的task-based构建工具,那时候对于前端工程的认知度很低,以为前端构建不就是几个压缩优化校验打包任务的组合吗,写好流程调度,就针对不一样需求写插件呗,看似很是简单。但当咱们支撑愈来愈多的业务团队,接触到各类不一样的业务场景时,咱们深入的感觉到task-based工具的粗糙,团队天天疲于根据各类业务场景编写各类打包插件,构建逻辑异常复杂,隐隐看到不可控的迹象。
咱们很快意识到把基础架构放到构建工具中实现是一件很愚蠢的事,试图依靠构建工具实现各类优化策略使得构建变成了一个巨大的黑盒,一旦发生问题,定位起来很是困难,并且每种业务场景都有不一样的优化需求,构建工具只能经过静态分析来优化加载,具备很大的局限性,单页面/多页面/PC端/移动端/前端渲染/后端渲染/多语言/多皮肤/高级优化等等资源加载问题,总不能给每一个都写一套工具吧,更况且这些问题彼此之间还能够有多种组合应用,工具根本写不过来。
Facebook的作法无疑为咱们亮起了一盏明灯,不过惋惜它并不开源(不是技术封锁,而是这个系统依赖FB体系中的其余方面,通用性不强,开源意义不大),咱们只能尝试挖掘相关信息,网上对它的完整介绍仍是很是很是少,分析facebook的前端代码也没有太多收获,后来无心中发现了facebook使用的项目管理工具phabricator中的一个静态管理方案Celerity,以及相关的说明,看它的描述很像是Facebook静态资源管理系统的一个mini版!
简单看过整个系统以后发现原理并不复杂(小而美的典范),它是经过一个小工具扫描全部静态资源,生成一张资源表,而后有一个PHP实现的资源管理框架(Celerity)提供了资源加载接口,替代了传统的script/link等静态的资源加载标签,最终经过查表来加载资源。
虽然没有真正看过FB的那套系统,但眼前的这个小小的框架给了当时的咱们足够多的启示:
静态资源管理系统 = 资源表 + 资源加载框架
多么优雅的实现啊!
资源表是一份数据文件(好比JSON),是项目中全部静态资源(主要是JS和CSS)的构建信息记录,经过构建工具扫描项目源码生成,是一种k-v结构的数据,以每一个资源的id为key,记录了资源的类别、部署路径、依赖关系、打包合并等内容,好比:
json { .theme-peacock } { "a.js": { "url": "/static/js/a.5f100fa.js", "dep": [ "b.js", "a.css" ] }, "a.css": { "url": "/static/css/a.63cf374.css", "dep": [ "button.css" ] }, "b.js": { "url": "/static/js/b.97193bf.js" }, "button.css": { "url": "/static/css/button.de33108.js" } }
而资源加载框架则提供一些资源引用的API,让开发者根据id来引用资源,替代静态的script/link标签来收集、去重、按需加载资源。调用这些接口时,框架经过查表来查找资源的各项信息,并递归查找其依赖的资源的信息,而后咱们能够在这个过程当中实现各类性能优化算法来“智能”加载资源。
根据业务场景的不一样,加载框架能够在浏览器中用JS实现,也能够是后端模板引擎中用服务端语言实现,甚至两者的组合,不一而足。
有关加载框架的具体实现我曾写过不少文章介绍,能够扩展阅读:
这种设计很快被验证具备足够的灵活性,可以完美支撑不一样团队不一样技术规范下的性能优化需求,前面提到的按需加载、延迟加载、预加载、请求合并、文件指纹、CDN部署、Bigpipe、Quickling、BigRender、首屏CSS内嵌、HTTP 2.0服务端推送等等性能优化手段均可以很容易的在这种架构上实现,甚至能够根据性能日志自动进行优化(Facebook已实现)。
由于有了资源表,咱们能够很方便的控制资源加载,经过各类手段在运行时计算页面的资源使用状况,从而得到最佳加载性能。不管是前端渲染的单页面应用,仍是后端渲染的多页面应用,这种方法都一样适用。
此外,它还很巧妙的约束了构建工具的职责——只生成资源表。资源表是很是通用的数据结构,不管什么业务场景,其业务代码最终均可以被扫描为相同结构的表数据,并标记资源间的依赖关系,有了表以后咱们只需根据不一样的业务场景定制不一样的资源加载框架就好了,今后完全告别一个团队维护一套工具的时代!!!
恩,如你所见,虽然完全告别了一个团队一套工具的时代,但彷佛又进入了一个团队一套框架的时代。其实仍是有差异的,由于框架具备很大的灵活性,并且不那么黑盒,采用框架实现资源管理相比构建更容易调试、定位和升级变动。
深耕静态资源加载框架能够带来许多收益,并且有足够的灵活性和健壮性面向将来的技术变革,这个咱们留做后话。
回顾一下前面提到过的前端工程三个阶段:
如今补充上第四阶段:
因为先天缺陷,前端相比其余软件开发,在基础架构上更加迫切的须要组件化开发和资源管理,而解决资源管理的方法其实一点也不复杂:
一个通用的资源表生成工具 + 基于表的资源加载框架
近几年来各类你听到过的各类资源加载优化策略大部分均可以在这样一套基础上实现,而这种优化对于业务来讲是彻底透明的,不须要重构的性能优化——这不正是咱们一直所期盼的吗?正如魏小亮博士所说:咱们能够把优秀的人集中起来去优化加载。
如何选型技术、如何定制规范、如何分治系统、如何优化性能、如何加载资源,当你从切图开始转变为思考这些问题的时候,我想说:
你好,工程师!
前端工程实际上是一个很大的话题,开发仅是其中的一部分。