王斌:兴趣部落-亿万访问量的前端架构探索

8月8日,由HTML5梦工场主办的第四届HTML5峰会暨攻城师嘉年华(iWeb峰会)北京站,会上腾讯AlloyTeam前端工程师 王斌表了《兴趣部落-亿万访问量的前端架构探索》主题演讲。

以下是演讲实录:

我们开始这个主题。我这个主题就是偏技术一点,可能也是有一些会比较抽象一点的东西。先看一下自我介绍,我是来自Alloy Team。我们的项目叫兴趣部落,大家如果没接触过这个项目,可以拿起手机点到第三个,然后我们上面的右上角就是兴趣部落。点进去之后里面会有四个列表,你可以随便在排行里面找一个兴趣部落点进去,下面就是部落首页,在里面有很多的话题列表,再点一个你比较感兴趣的话题,这样就完成了一个比较完整的观看帖子的流程。在介绍这个架构之前,我们先看一组数据了解一下我们项目的规模。


我们这个项目是从去年3月份开始,去年的时候也就是大概有三个前端开发的人开发,到现在已经有两个组,大概20,但不是全部,也不是全部投入进行项目的开发。我们原来有三个页面,现在到了51个页面。我们的PV也是从去年的百万量的级别,到现在日的两亿多的PV访问量。然后我们详情页的规模从原来几百行的代码扩展到现在4000行的代码,这样一个规模。我们可以看到部落项目它的特点,实际上是多页面的,其实里面有很多页面,也是多环境的。你可以在手Q里面看,也可以在微信里面看,在手Q里面调用了很多手期的API的一些我们Web做不到的东西。


我们来看一下部落的前期架构,这个也是很传统的Web架构的过程。比如说我们先看第一层,是我们的通用层。第二层,是业务层。第三层是第0层,是这样的架构。我们把这三个层面划分出来之后,我们每个人可以关注每个东西,做一些极致的优化。我们先来看一下我们的第一层,是通用层。我们一般放一些基础库,汽油一些kit,一些组件。我们看一下基础库的选择,现在的基础库有zepto、jQwery,还有JQ。但是我们用了zepto。我们的Kit层,有Web图片查看器,Web发表组件,评论组件。我们在Kit详情页有一个分享功能,在另外的页面也是需要这个分享功能,刚开始在业务层上面,但是有人想用这个可以迁移到应用层,可以很快的获得这个功能。


这样的架构也是有问题的,那Kit层带来的问题,我们的代码冗余,有些功能在某些页面是不用的,但是放在通用层里面,就带来一个问题,我们的代码是达不到百分之百的利用率,这样就会增大我们整个代码的大小。另外一个问题,我们一般应用层的样式和逻辑是分离的,这是按照以前的策略。再看一下我们的业务层,开发业务层的人只是关心我业务层的东西,比如举个简单的例子,我们的部落首页,我们这里有一些简单的逻辑。第一次进来之后要进行一个tab的定位,渲染tab,请求cgi,流程还是很多的,因为牵扯到各种各样的逻辑。


那我们来看随着项目增大,我们业务层带来的一些问题,第一个就是风格不统一,因为我们人很多,我们需要很多人一起开发这个项目。这里的风格是我们编写代码逻辑的风格,不仅仅是说,我今天这个可能用tab缩紧,这个不在考虑范围内。另外一个逻辑不清晰,对于他自己来说逻辑是很清晰的,但是对于别人来说不清晰。再一个冗余逻辑,一个新东西加进来了,他做这个功能在别的页面已经实现了,他自己又写了一份,就是冗余逻辑。另外,每个人的页面性能是不一样的。我们部落在发展过程当中遇到的问题,我们自己开发的同事也会吐槽一些问题,3000行怎么看?整个项目逻辑通篇下来有3000行代码,新同学进来不知道从哪里入手。另外,我们会做一些优化,有些新同学开发的界面比较慢,你为什么不做缓存的界面吗?还有出现未知的加载,未知的BUG。还有项目赶的比较快,复制、粘贴就把这个复制过来了,没有放到通用层,就带来一个问题。这个时候对于项目变大就需要一定的统一。然后我们经过反思了之后,我们发现实际上是这样的,就是说业务层的问题,如果你业务层出现了问题,经常是通用化的程度不够,可能你什么东西都不够。这个时候我们是想我们原来的传统的Web的架构是不够的,我们需要一个中间的这一层,叫做框架层,这一层架构来做一些可能是在业务层里面更多抽象的东西。我们可以看一下,比如现在业界推崇的框架,一个是MVVM的思想,一个是面向组件的开发,我们所有东西都是组件,我们用组件搭建我们的页面,这是未来Web的另外一个趋势。


我们看一下组件化,我们看一下组件化它带来的问题,一个问题是解决代码复用的问题,另外一个它是希望我们比如说有业务层的代码,像搭积木一样搭起来。另外一个组建的好处,我组建多了,就有一个很大的可控性。再有一个可以做迅速的开发,可能别人开发的组建,我拿过来直接用就可以了。当然组建化也有一种劣势,比如说我们的运营平台。你可以看到我们每一个页面,这些都是表格,有一些特殊的场景需要我们去改里面的东西,这会带来一个组建的很多逻辑导致我的组建越来越大,也是有一些冗余存在的。所以说并不一定是完全的列表。我们部落做了这样的探索。


像Angular和React都是在通用层,所以我们需要在业务层做更多的处理,我们的部落有自己的框架产生。我们部落里面对业务层的抽象是这样的,第一个我们其实跟React有点相似,我们走向了更多是面向模块开发。下面一个是渲染模式,上面有关系模型,关系模型是描述渲染模型的关系,上面有通讯模型,描述每个模块之间。我们回到这个页面,这个页面还是有很多复杂的逻辑。比如说我让一个新同学写这个页面,他们会按照线性的页面去写,就是流水的方式去写,就是一各种Object,这种架构体系是自顶向下的模块分析,交给你一个页面过来之后,我们看这个页面大家可以拿起手机看一下,我们四个tab,我们并不是实现分析它的细在的逻辑,而是自顶向下的分析它之间的关系,我们可以看到这四个tab分别对应四个tab的内容。我们可以把每个tab看成四个模块,这就有四个模块,这四个模块对应我们的体系就属于渲染模型,四个模块承担的作用就是渲染页面。我们部落里面抽象出来,就是有三到四个,render、model,这个可能还是有点抽象。假如说我们看一下这个页面,比如说在最近上面有一个个人信息一个列表的展示,这个时候你会看到我们部落代码是这样去写的。对于它来说这个地方只是做了一次渲染,我们就是一个渲染模型,我们都是配置化的,配下我们渲染的元素,还有模板,我要加工数据,我要完成之后做一些事情,接着绑定的事件,这是一个模块,所有的事件和一些处理都放在这里面进行。最后进行模块的渲染,这就完成了这样的方法。我们的框架重新刷新这一个模块的展示,我们重置模块,还有停止这个模块,就是这个模块的生命周期结束了,销毁掉。


我们动态的tab是不停往下拉,我们要写一个滚动加载。我们的滚动加载跟Render model没任何区别。我们需要对模块进行重置,现在我们的同学可以对这个模块进行刷新,充分请求刷新就OK了。上面只是说的这种模型、模块之间的,那模块之间肯定有一些关系,我们抽象模块之间的关系,一种是互斥关系,一种是同时进行的关系,我们用状态机描述它。互斥关系就是说我们同级的模块之间,如果一个激活了,那么另外全部属于非激活的。上面的模块如果被激活,下面的全部被激活,这是两种关系,用这两种关系构建我们整个部落的项目。第一个互斥关系,它可以做什么样的架构呢?我们可以看到我们四个tab,实际上每一个用户只可以看到一个,就是属于互斥的关系,我点最近的tab,其他的全部消失了,所以最上面的tab就是互斥tab的一个关系。


然后我们来看一下,比如说我们到底这个框架是怎么搭建出来的,大家可以看一下那边的代码。它是一个滚动加载的列表,我们下面包含了很多个模块,我们把模块加进去,它是具有这样一种关系的,最后你发现它们之间是具有互斥关系的,所以把一个互斥模型的关系把它们加进去,结果调用最后一个tab的方法,我这个页面就可以渲染出来了,这样就很好的解决了,我这个页面的滚动加载到另外一个页面也有。就是这样的这种模块化的思想可以解决我们这种代码复用和耦合矛盾之间的问题。


你可以看到我们用了这种面向模块开发之后,我们部落带来的改变,有清晰的主体逻辑,每个人可以看到大部分都在做一些配置性的东西。统一的代码风格,这个也不用详细说,因为它已经强约束了写代码的方式。统一的优化,还有良好的复用性。一处优化用全球,我们在框架层做一次优化,所有的页面都可以享受。比如说缓存优化,我们的页面先进行读取缓存、渲染,等回来的时候再刷新掉。那可能是我们的业务同学他们开发的时候不用关心这一层的优化,他只要按照那样的方式去写之后,就得到了那样的优化,渲染完了再进行CGI的渲染,当然也可以关闭这样的功能。另外还有延时的加载,都可以在这层去做,而不是说在具体的业务层去做。我们看一下首屏,我们为首屏加速做的事情。你可以这四个tab并不是一次全部展示出来,所以有些tab的资源可以延后加载,他可以等渲染完之后再加载资源。如果是传统的业务做,可能需要写很多的代码,然后可能别的页面还不一定能够有这样的优化。但是我们的框架层帮他做之后,我们只需要按照后面的把上面的配置模块的资源就可以获得这样的优化。


再有一个就是我们的cgi预拉取的策略,一般都是点了之后再拉取再渲染,但是我们可以提前拉回来,等你需要用的时候可以拉回来,这样用户基本看不到任何的延迟。对于我们这个框架,我们已经完全开源出去,并且放到我们团队的Git上面去,大家可以看一下。我们在部落另一块的优化,就是在部落模板这一块做的优化,这个是之前的模板,后来我们用另外的一种模板,也是我们自己部落内部的,它的语法是一致的语法,我们自己写了这样一个模块引擎,来支持这样的语法。你可以看起来很清楚,然后呈单一性的原则,模块只负责处理,上面可能会添加一堆的逻辑在里面,很难看的。


我们的这个引擎也是开源出去的,当然这个下面的框架,它默认集成的就是上面的模板进行渲染。我们还有一些更细致的优化,这属于在我们的业务层做一些更细致的优化,比如说我们的依赖加载,还有Cgi更早提前的优化,另外就是长列表优化,我们部落是加载,很多滚到下面的时候很多机器会很卡,我们也做了很多优化。到了第0层,发布层这一层,就是我们构建这一层,还有发布这一层发布这一层我们就有构建工具,就是去打包。我们发布会发成有在线和离线包两个版本。我们借助手Q的能力,我们部署了离线包的版本,我们提前帮用户拉好,可以直接执行脚本。关于离线包还有构建工具还有很多东西讲,但是由于时间关系就不讲了。我们的监控是用SB系统,还有方便在手机上直接查看的工具。还有更极致的优化,我们做的直出,我们把前端的渲染能力放到后端去做,但是我们的直出这块,创新一点来说我们的直出和前端代码是一个版本,一个版本就可以完成直出的功能。


未来,可能我们在部落这一块,可能探索的就是下面这些,比如说一个模块的强约束。我们探索的你的变量都要被我们约束到,统一的生命方式。下面还有MVVM的方式,现在放在手机端是不好的,但是这块是有很大的吸引力,Web方面的发展趋势。还有可视化的配置,我们现在也是基于配置型的代码,我们以后会走可视化的道路。还有组建化开发模式的探索,另外一点作为对Web这一层交互、动画体验这一块做更深入的研究。

最后,我们部落就是为了做到Web的极致优化,感谢大家。