随着互联网的发展,咱们的业务也日益变得更加复杂且多样化起来,前端工程师也再也不只是作简单的页面开发这么简单,咱们须要面对的十分复杂的系统性问题,例如,业务越来越复杂,咱们要如何清晰地梳理;团队人员越来越多,咱们要如何更好地进行团队协做;功能越来越多,咱们要如何保证页面的性能不至于降低,等等。全部的这些均可以归结为如何提高开发体验和性能问题。css

提高开发体验

咱们主要从如下三个方面来提高咱们的开发体验。html

规范化

当团队人员不断扩充时,咱们须要制定统一的规范来对平时的开发工做作出必定约束和指导。统一的规范包括前端的代码规范,根据规范定义好一套代码检查的规则,在代码提交的时候进行检查,让开发人员知道本身的代码状况。前端

同时,根据以往的开发经验,咱们制定了统一的项目框架,根据业务功能不一样,将一个项目(app)拆分红不一样的业务模块(module),而每个模块都包含自身的页面(page)以及构成页面所须要的组件(widget),每个项目涉及到app、module、page、widget这些已经约定好的概念,这样让项目结构更加清晰,并且让团队内不一样业务的人员之间切换无障碍。git

项目架构

组件化

在项目中引入组件化的概念,这里的组件对应上文讲到的widget,每个组件都会包含组件自身的模板、css、js、图片以及说明文件,咱们使用组件来拼装页面,像搭积木同样来拼装咱们的页面,同时一个组件内能够调用另外一个组件。github

组件化

在拿到设计稿后,咱们首先须要肯定哪些须要作成公共组件,那些是要作成独立组件,以及组件间如何进行通讯。在页面中调用这些组件后,会自动加载组件的模板以及组件的静态资源,而当组件再也不须要时,只要移除掉组件引用,那么相应的模板和静态资源也会再也不加载。json

组件化的好处主要有这么几点后端

  • 管理方便,咱们能够把一个独立功能相关的文件在工程目录中放在一块儿,这样代码管理起来会很是便利
  • 组件复用,经过抽取公共组件,能够实现组件复用,从而减小工做量,创造价值
  • 分而治之,这是组件化最重要的一点,将页面组件化,就是对页面功能的拆分,将一个大的工程拆成小的零件,咱们只须要关注每个零件的功能,极大地下降了页面的开发与维护的难度

自动化编译

在前端开发中,咱们老是会去使用不少工具、手段来优化代码、提高开发效率,例如,咱们会使用sass、less等CSS预处理工具来编写更好维护的样式代码,咱们也会使用CSSLint、eslint等代码检查工具来检查代码的语法错误,使用文件合并压缩等手段来减小资源大小,除此以外咱们还会去作雪碧图合并、多倍图处理、字体压缩处理、代码发布等等。前端工程化

曾经有大神说过,超过90s的工做都应该自动化掉。而以上全部的这些工做,贯穿咱们整个开发流程,可是不一样工具的切换不但显得凌乱,并且影响开发效率。在自动化、工程编译的思想早已深刻人心的当下,咱们固然也要紧跟潮流,因此咱们考虑经过自动化手段来提高咱们的效率,让全部操做能够一键式开速执行完。浏览器

咱们将经过定义好一系列的编译任务,按照必定顺序依次对咱们的项目自动进行编译操做,最后产生出可上线的代码。缓存

提高性能

咱们主要从如下四个方面来作好性能优化。

首屏优化

页面的打开速度一直是你们很是关心的一个指标,一个页面打开太慢会让让用户失去等待的耐心,为了让用户更快地看到页面,咱们考虑将页面中部分静态资源代码直接嵌入页面中,咱们经过工具处理,在工程编译阶段,将指定的静态资源代码内嵌入页面中,这样能够减小HTTP请求,提高首屏加载速度,同时下降页面裸奔风险。

按需加载

同时,咱们考虑经过尽可能减少页面体积来提高页面打开速度,在业务上咱们将页面划分为一个个楼层组件,以京东美妆馆为例,页面中从上而下分为首焦、至IN尖货、今日特惠、潮流前沿、口碑榜单这么几个楼层组件,其实这个页面还有很长,内容很是多且复杂。

京东美妆馆

以前咱们的作法是整个页面直出,这样一次性加载的内容会很是多,为了提高打开速度,咱们考虑经过按需加载的方式来优化页面的加载。咱们在页面中只放每个楼层的框架性代码,楼层的模板和数据都经过异步的方式去拉取,来实现楼层组件的按需加载,同时咱们能够对模板以及数据进行缓存,以此来减小请求,作更极致的优化。在开发中咱们以正常组件的方式去开发整个页面,随后经过编译工具,在代码编译阶段自动将楼层的模板抽离成一个独立的JS文件,并给楼层容器打上标记位,经过页面加载逻辑去按需拉取模板,再进行渲染。

经过给楼层容器和模板分别加上标记位 o2-out-tpl-wrapper o2-out-tpl

按需加载1

在编译时自动将指定的模板代码抽离成独立js文件

按需加载2

而且给楼层容器打上标记

按需加载3

同时在逻辑脚本适当位置自动加入模板的版本

按需加载4

经过上述步骤,实现按需加载的自动化生成,在提高性能的同时,很好地解放咱们生产力。

基于资源表加载

根据页面组件化,经过工具分析,咱们将得到页面与组件的依赖关系表,同时也能确认页面所引用资源的依赖关系,例如,咱们在页面hello中同步引用组件topbar,那么依赖关系表中将会记录同步引用关系hello引用topbar.tpl、topbar.css、topbar.js,那么页面hello将会自动加载组件topbar的CSS与JS,同时依赖表会记录异步引用的关系,假如咱们在组件C中经过API异步引用了组件D的js,那么会在依赖表中记录C异步引用D.js这一个依赖关系,这样D.js这个资源将会在用到的时候被异步调用。

组件依赖

资源依赖

同步引用的资源经过生成combo形式连接,在服务端进行文件合并,这样在页面加载的时候,页面只会加载本身须要的同步资源,异步的资源将会在用到的时候再加载,有效避免资源冗余。同时删除、增长组件也很是方便,只需改动模板中对组件调用,经过编译工具会自动从新生成模板以及combo连接。

咱们能够将资源加载的操做抽离出来,造成一套统一的资源加载框架设计,这样咱们使用的模板能够变得更加灵活,不管是纯html模板,仍是PHP或Java之类的后端模板都能有效支持。编译工具扫描代码后只生成资源依赖表,咱们经过实现各语言平台的资源加载框架,让不一样语言的模板都能基于同一个资源依赖表进行资源加载。

同时,对资源进行MD5重命名处理,文件md5重命名也是一种提高性能的有效手段,使用文件md5后开启服务器强缓存,能够提高缓存的利用率并避免没必要要的缓存判断处理。但文件md5重命名后会出现开发时引用的文件名对不上的问题,这就须要在资源表中记录原文件名与md5重命名后之间的对应关系,当咱们引用一个资源时,就会经过查表获取重命名后的资源名,而后利用代码中引用资源定位的能力来进行资源名自动替换。

md5

静态资源预加载

所谓静态资源预加载,就是当用户在进行浏览页面的时候,咱们能够在当前页面静默加载下一个页面的静态资源,这样当用户进入到下一个页面时就能快速打开页面,从而在不知不觉中提高页面的打开速度。

静态资源预加载设计

咱们会在静态资源预加载平台上配置每个页面id对应须要预加载页面资源的id,而后系统经过读取资源依赖表获取到所须要预加载的静态资源,生成预加载资源列表文件,再将文件推送到线上服务器,经过页面挂载js请求获取预加载资源列表,随后静默加载资源。在有了资源依赖表后,咱们能够准确地分析到每个页面引用资源的请求,就能够很好地实现静态资源预加载的功能。

静态资源预加载

Athena

工欲善其事,必现利其器。为了实现咱们对提高开发效率和产品性能的诉求,咱们提出了比较完整的工程化解决方案以及对应的工具Athena

Athena是由京东【凹凸实验室】(aotu.io) 推出的一套项目流程工具,经过Athena,咱们能够很流程地跑完整个开发流程。Athena分为两部分,一是本地自动化编译工具,二是资源管理平台,其架构以下

Athena

本地自动化工具

Athena本地编译工具是一个基于NodeJs的命令行工具,经过执行命令的方式来优化咱们的开发流程,目前Athena的主要功能有

  • 自动建立项目、模块、页面、组件结构
  • 轻量组件化功能,根据组件加载状况生成资源依赖表
  • Sass/less 编译
  • 代码检查
  • CSS prefix等处理
  • CSS合并压缩,JS合并压缩
  • 自动生成雪碧图,自动多倍图,图片压缩
  • 字体文件压缩
  • 自定义图片转base64
  • 文件内联,能够内联样式及JS代码
  • 文件MD5戳,将文件进行使用MD5进行重命名
  • 本地预览,直接查看整个项目
  • 资源定位(图片等资源路径替换)
  • 生成CSS页面片,提供将页面引用的CSS/JS抽离成页面片的形式,方便管理CSS资源
  • 部署到预览机和开发机

建立项目结构

在执行建立命令时,Athena会从管理平台下载自定义好的项目模板,能够根据模板建立项目、模块、页面、和组件。Athena有四个建立命令:

经过执行 $ ath app demo 命令就能够生成定义好目录结构的项目。

建立项目

随后能够经过 $ ath module home来建立一个业务模块;

经过 $ ath page index 来建立页面;

经过 $ ath widget widgetName 来建立组件。

开发使用

组件化

Athena中实现组件化主要是分为两种,一是针对纯HTML模板,经过扩展模板引擎方法实现,提供了组件化API widget.load,它能够方法接收三个参数,第一个参数是widget的名称,后面两个参数是可选参数,第二个是向widget传递的一些参数,第三个是widget所属的模块,若是是本模块,能够不传例如

1
2
3
4
5
6
7
<%= widget.load('user') %>
<%=
widget.load( 'user', {
param: 'test'
})
%>
<%= widget.load('user', null, 'gb') %>

经过模板引擎编译,执行widget.load方法,能够实现加载模板,记录依赖关系的目的。

组件化形式

二是针对不一样语言的后端模板,经过实现各自的组件化框架来进行组件的加载,例如 PHP 下使用 <?= $widget->load('user', NULL, 'gb') ?> 来进行组件加载,再经过代码扫描得出组件依赖关系。

Athena中的API

Athena针对模板提供了一系列的API来扩展丰富的功能,例如前面提到的 <%= widget.load() %> 来实现组件化。

同时Athena中还提供了其余API:

<%= getCSS() %><%= getJS() %> 用来引用CSS/JS文件,传入文件名和模块名;

<%= uri() %> 提供了资源定位功能,能够在模板中标记资源,编译过程当中会进行替换,并且在JS中也有资源定位API __uri()

<%= inline() %> 提供了内联资源的功能,传入文件名和模块名,能够在模板中内联任意资源,例如图片以及JS脚本;并且 inline 也能够内联一段网络资源,例如线上的JS文件,一样的在JS中也有内联资源API __inline()

雪碧图标识 ?__sprite ,在CSS中引用图片最后加上标识 ?__sprite 能够自动生成自定义名称雪碧图,同时支持自定义生成多张雪碧图,只须要要标识后面带上一个文件名,就能够生成一张以这个文件名来命名的雪碧图,例如 ?__sprite=icons ,这样全部带一样标识的图片就会生成一张以 icons 为文件名的雪碧图。

编译预览

编译任务

在编写完项目,就能够经过命令来对项目进行编译了,执行编译命令 $ ath build,会针对指定模块执行已经定义好的编译任务,根据项目需求,目前编译都是基于业务模块去编译,编译任务的最小执行单位是页面,每次编译都会执行如下编译列表

编译任务

编译截图

本地预览

执行预览命令 $ath serve 会执行精简版编译任务来编译项目,编译完项目后会生成一份站点地图,随后打开一个本地服务器来预览项目,使用这个命令能够很方便地进行开发,在预览时会同时watch目录和文件的改动,而且提供了livereload功能,咱们能够在预览时任意修改文件,都将实时地反映到页面中,同时能够新建另外一个窗口执行新增组件和页面的操做,让整个开发过程很是顺畅,咱们只需关注开发自己就好,不须要再关注其余事。

ath serve

执行完编译任务后,默认自动打开浏览器,预览站点地图

站点地图

Mock server

在进行项目预览的同时,Athena同时提供了mock data的服务,咱们能够配置相应的路由,以及路由接口对应的假数据,全部的接口请求会发送到mock server上,在mock server中能够选择将请求代理到假数据平台仍是代理到线上接口,这样就能够脱离后端进行开发联调了,以此实现数据的先后端分离。

mock server

项目部署

在开发预览完后,经过命令 $ ath publish 就能够将项目发布到配置好的测试机上,发布同时支持ftp、sftp以及http形式。

组件维护

咱们经过组件化的手段已经将咱们的项目进行组件化了,这样咱们通过业务迭代积累,产出不少业务公共组件,但在以往的项目开发中,公共组件的更新与维护一直很受限制,并且有哪些公共组件、公共组件长什么样子,只能依靠口口相传或者手工维护的文档。因此在Athena中咱们加入了组件平台,在组件平台上统一展现各个业务的公共组件,而得益于本地工具,组件平台不须要人工干预维护,咱们能够在本地经过命令 $ ath widget-publish [widgetName] 命令来发布一个组件到组件平台,这样其余人就能够当即在组件平台进行组件的预览,而其余人若想使用该组件时,在本地经过命令 ath widget-load [widgetId] 就能够下载该组件到本身的模块目录下了。

这样组件的维护更加自动化,公共组件的使用也更加方便了。

组件发布

组件发布

组件下载

组件下载

自身优化

为了提高开发效率,Athena作了一些优化操做

精简项目预览时的任务

在开发时进行项目预览时,会执行精简版的编译任务,剔除了相似文件压缩、雪碧图生成、模板抽离处理等耗时的操做,只保留核心、必须的编译任务,这样能够极大地减小编译时间,提高开发的效率。

预览时监听细化

在开发进行预览时,会对全部文件的改动进行监听,而针对每一类文件都有很是细化的操做,当文件改动时只会执行改文件所须要的编译任务,而不会进行总体编译,这样能够很好地提高开发效率。例如改动某一组件的CSS文件,则只会针对该文件执行一些相关的CSS操做。

同时得益于全部文件依赖关系的记录,在监听时会根据依赖关系进行文件编译,例如某sass文件中引入了另外一个sass库文件,修改这个sass库文件的时候,会根据引用关系表同时更新到全部引用到这个sass文件的文件,这样项目文件更新及时,让开发流程更加流畅。

编译缓存

在图片压缩和sass编译时,开启文件缓存,将已经编译过且没有改动的文件过滤掉,再也不编译,大幅提高编译速度。

发布缓存

设置发布过滤,根据文件md5过滤掉已经发布过的文件,提高发布速度。

技术选型

Athena本地工具早期技术选型是 Yeoman + Gulp 的方式,但后来因为安装、更新很是麻烦,命令太长很难打的缘由,咱们改为了本身开发一个全局安装包的方式,编译核心使用的仍是 Gulp 的 vinyl-fs 来实现文件流处理,经过 ES6 Promise 来进行编译流程控制,最小以页面为单位,通过一系列编译任务,最后产出编译好的文件。

技术选型

管理平台

性能优化一直是前端工程师探索的课题,不少时候就是资源的分配问题,也就是资源管理。为了更好地配合本地构建工具来管理资源,咱们搭建了管理平台。咱们来看下,结合本地构建工具和管理平台,工做流程变成了怎样?

工做流程

  1. 在管理平台上建立项目,输入项目名称和预览机,以及选择相应的模板等;
  2. 在终端执行ath app指令,工具会优先拉取远程服务器的项目信息来初始化项目,若是没有获取到相关信息,就会在本地生成项目,并将项目信息上报给服务器;
  3. 项目初始化后,就能够建立模块、页面、组件了;
  4. 在编码过程当中,可经过ath server预览页面;
  5. 在本地经过后,可执行ath publish将代码发布到开发机或者预览机。

在上面的publish指令中,工具会扫描全部文件,执行代码检查,扫描页面文件,获取组件依赖关系,根据组件依赖关系进行文件合并,而后会进行样式处理、js处理以及图片的处理,根据配置是否进行md5重命名文件,组装html,插入样式、js和图片,最后将编译好的文件发布到相应的机器。在整个过程里面,会生成资源关系依赖表,最终会将资源关系表及编译后的文件上传至管理平台。

除此以外,每一个指令的操做都会上报给管理平台。管理平台收到数据后,会对数据进行处理,最终能够在平台上看到项目相关的信息。

总体工做流程图以下:

工做流程

从上面的工做流程中,咱们能够看到,管理平台须要有数据统计、资源管理以及项目管理的功能。总体架构图以下:

管理平台架构

数据统计

数据统计包含项目操做日志,主要是用于统计团队每一个成员具体的操做,方便项目成员查看项目代码变动;另外一部份是统计样式表、脚本以及图片的压缩数据,用于显示工具给咱们项目带来的提高。

如下是操做日志统计:

数据统计

资源管理

资源管理是管理平台的核心,主要分为4个部分:模块展现、依赖关系、组件预览和权限控制。这部分功能主要经过本地构建工具提供的资源关系表来完成。

模块展现

模块展现,用于记录项目具体包含哪些模块以及模块具体的信息。在日常开发中,咱们的项目会分为许多模块,不一样的模块有不一样的人来开发和维护。当项目越大的时候,能够经过管理平台清晰地看到模块具体的信息。

模块展现

依赖关系

依赖关系,主要是html、css、js和图片相互之间的关系。经过分析资源关系依赖表,能够获取到各个资源被引用的状况以及线上版本的状况。当线上环境采用md5来作资源管理时,咱们不是很清晰地知道静态资源对应线上哪一个版本的资源,而有了这个依赖关系表,当出现问题时,咱们能够更快地定位到具体的资源。

依赖关系

组件管理

咱们采用组件来拼凑页面,当项目越大时,组件越多,那么如何管理组件成为了一个棘手的问题。好比说,有一些比较老的冗余组件,咱们不肯定是否为其余页面所引用,那么就不能愉快地删除它。有了组件管理,能够清晰地知道组件的被调用状况,就能够对组件作相应的操做。

组件管理,结合组件平台来使用,在管理平台上引用组件地址预览组件,同时能够获取到组件被引用以及引用资源(如css、js、图片)的相关状况。

组件管理

咱们的组件分为两种,一类是经过ath w自动建立的,经过ath pu提交到管理平台的,在管理平台上进行组件的相关分析和编译,获得组件的信息,这类组件主要是跟业务绑定的;另外一类是经过ath widget-publish提交到组件平台的,由组件平台进行相关处理,这类组件是通用组件,与业务无关,用于展现给开发以及相关业务方看的。

组件提交

在组件平台上能够预览与编辑相关的组件,经过与设计师约定相关的设计规范来促使组件达到尽量地复用,进而减小设计师的工做量,提高咱们的工做效率。

组件平台

组件提交到组件平台

经过ath widget-publish指令将组件提交到组件平台,组件平台会对组件源码进行编译,将组件名称md五、组件归类以及组件版本记录等等。

组件发布

从组件平台上下载组件

经过ath widget-load指令将组件下载到本地,当本地构建工具向组件平台发起请求时,会带上组件名称,组件平台会将源码进行编译,将组件名称重命名,而且相应地替换源码中的组件名称,同时记录组件的被引用记录。

组件下载

权限控制

权限控制,项目中存在公共组件模块,公共组件比较稳定,好比说轮播组件、选项卡组件等等,这部分代码通常比较少变更,可由少部分人来更新和维护,因此加入了权限控制机制,保证公共组件的稳定性。

项目管理

咱们在使用本地构建工具时,须要配置多个参数,好比主机信息、选择模版等,在命令行环境下有些不直观。为了简化这个操做,管理平台提供了项目建立的功能,同时提供了模版建立的功能。

项目管理

在项目信息、模块信息以及组件信息发生变动的时候,为了第一时间可以通知项目成员更新,加入了消息通知的功能,目前经过发送邮件的方式,后期能够加入微信提醒的功能。

技术选型

管理平台前端采用React+Redux的方式,后端采用Express+MongoDB,总体技术选型以下:

技术选型

假数据服务

存在的问题

在日常的开发中,常常须要先后端联调,可是在项目开始之初,不少接口并无提供,在之前的开发模式下,须要等待后端提供接口或者本身先定义接口,前端开发的进度可能会受影响。

Mock数据平台

为了避免影响前端开发的进度,咱们搭建了Mock数据平台,经过与后端协商数据格式,自定义数据接口,这样子就能够作到先后端分离,让前端独立于后端进行开发。

Mock数据平台基于mockjs搭建而成,经过简单的mock语法来生成数据。

Mock数据平台目前有以下功能:

  1. 建立模拟数据,使之符合各类场景;
  2. 生成json数据接口,支持CORS以及jsonp。

Mock数据平台

写在最后

本次分享首先讲述了咱们在业务膨胀、人员不断增长的背景下遇到的项目开发上的问题,并提出了咱们本身对于这些问题思考总结后得出的解决方案与思路,最后产出适合咱们团队、业务的开发工具—— Athena。但愿咱们的方案能给你们带来必定的借鉴做用。

感谢您的阅读,本文由  凹凸实验室 版权全部。如若转载,请注明出处:凹凸实验室( https://aotu.io/notes/2016/07/19/A-little-exploration-of-front-end-engineering/