【本文系ITA1024原创首发,转载或节选内容前需获授权(授权后一周以后可以转载),并在文章开篇注明:本文转载自ITA1024前端技术专题月分享实录,微信公众号ita1024k】
2016年3月19日,由互联网技术联盟(ITA)举办的1024前端技术峰会,在中关村WEPAC盛大举办!
400+位经验丰富的前端工程师共同参与,是一场业内最顶级讲师阵容的前端技术峰会,而且,这是一场不落幕的峰会,因为3月开始的每一周,都有线上的分会场如期分享着各个一线互联网公司在前端技术方面的最佳实践。
58同城资深前端架构师刘浩源分享话题:前后端分离开发模式实践。
如下是1024前端技术峰会上,刘浩源的分享实录。
大家好!我是刘浩源,在58同城做了将近五年的FE,现在在技术委员会负责FE技术支线。
我 今天分享的主题是前后端分离开发模式。这个概念是三四年前出现,可能大公司出现的会更早一些,就是所谓大前端的开发模式。现在有不少公司已经在做,特别是 大型公司已经在做了。58——当时在我来58,是在2012年——那边开发模式还不是大型公司的开发模式,所以我们一直在推进这个事,去年有一些成果,接 下来的时间,我会把技术相关东西摘出来,给大家介绍一下这个事情怎么推进的。
大家看一下流程图——模版开发现状——大部分FE工作状态就是这样。
首 先FE是一条工作线,那边RD是另外一条;一开始交给UI做设计图出来,然后是FE人进入,同时RD做数据库设计和代码;等到RD需要做模块的时候会把 FE的开发结果,HTML拿过去,做页面组装;组装好之后那边跟自己的后端进行联调,这个过程当中会出现很多问题,因为HTML是我们做的,FE做的不太 关注页面逻辑什么样,数据什么样,我们不会关注到实际数据库里到底是5位还是6位,还是更多。这时联调就有很多BUG,就从RD联调又回来,找到FE帮他 们调HTML,甚至于如果我们随便调又会跟UI出的结果还不太一样,还得往上倒腾让UI重新给出设计等等。等RD自测联调完了,提测,所有输出的结果有问 题都会找到FE帮助处理这些问题,甚至对测试人员来说是如何确认RD失误还是FE失误,测试很难做到。测试完了发现作为FE,我们上线的时候,我们不会上 模版,我们FE上线上JS、CSS,要等到等模版上线之后服务端重启完成才能上,提前上就可能引起旧代码出现问题。
整个开发过程中FE虽然是一条工作线,但是这条工作线绝大部分工作是附属到RD工作线上,并不是真正独立的开发人员。这样的开发现状就是有一些地方是大家都不爽的,并不只是FE觉得不爽的。
首 先第一点人员项目多,周期长,成本大。哪怕页面上很简单文案的修改,都不会涉及到数据结构,逻辑结构的东西,都得UI出图,FE切图,之后给RD,RD给 完整开发流程,FE这边相对开发流程会比RD简单很多,快捷性好很多。但是即使是一个文字的修改,现在也必须RD来,由原本FE+测试就搞定的项目,还一 定要RD也进来,还把RD相关所有的管理流程都加进来才能完成。这样的话,特别是产品和运营相关的人员就觉得非常不可理解了。
第 二点专业性错配,成就感归零。FE是专业的,我做HTML是专业的,但是我工作结果并不是由我最终放在线上,对于RD来说,他们可能对HTML并不算专 业,只是附属技能,好的一点可能对HTML了解,做的好模版结构,了解模版里的标签涵义;但是大部分RD都不在乎这个东西。对于RD来说就是模版上改一个 文字要耗费开发测试上线流程走一遍,对他来说也是没有任何成就感的,他很不愿意的。
第三点FE的项目归属感 过低。我们经常出现一个什么问题?就是两周前或者三周前把一个运营的页面切完了,RD在排期中,等他们两三周后开始拿这个页面套模块时出问题了,甚至更恶 劣一点产品跟RD说要改什么,改完之后不通知FE,上线前四五个小时说这地儿不对,帮我改一下,FE两三周前做的东西,中间有变化我们不知道,拿过来直接 蒙了,没法做。因为对于RD来说在刚才那个流程图里,RD是核心流程,管控项目的绝对进度,对产品来说就会催着RD,跟RD说有没有时间,有没有开始做, 是不是有问题,但是他不会跟FE说这个事,FE是被边缘化的,我们很难说管控一些事情。
最后一点前面这些东 西结果就是FE在团队里,特别是小公司,在一个公司切三年页面,换一家公司再切三年页面,我们不会了解这个页面上这个区域放的是什么,这个价格和某一个产 品几种价格,这互相之间的关系是什么。产品也不会告诉你,他只是告诉UI告诉你我放三个价格,交给FE切,切了给RD,RD清楚某个产品会有6个价格,但 是有可能会有复杂的地方,比方说进价是一个,卖价是一个,产品业务员可能又会很多个价格,但对FE来说不了解这些,我们对业务流程合理不合理没有发言权 的。
整个结果是这样的,那怎么办呢?——前后端重新分工。
现 在的这样分工,背景是什么?我刚到58的时候,前端工作组里边大概20个人,只有三个做FE的,剩下全部是java,我去了这个部门也是一段时间之后,发 现这个所谓前端跟服务的区分,是RD内的前端,前端工作就是做数据组装,后端就是提供数据服务,搜索服务,提供数据库的服务,这是后端。而它的前端是RD 前端。我进去之后FE人员非常少,对于这样的业务工程流程勉强可以认可的,FE人员不足,当时那批RD人员相对素质高一点,因为一直在做这些事,所以他们 的HTML和业务流程都比较熟。
但是现在58赶集集团已经大不一样了,现在RD人员是几百的概念,三四百了,现在业 务线好几条,前端业务组已经不是一个了,按产品线分5个大业务线,里面还有各种各样的分支业务,FE人员也大不一样,FE光是我们这边UBU事业部已经 70个人。在这个阶段下,刚才那个业务工作流程就完全没有什么合理性了,14年底我们对这个问题重新进行了考量,最后决定——重新分工。
我 们把传统三层结构View层拿过来,再好好的观察一下,对于FE来说,FE本身也可以分这三层,但是这里列得更多是后端,因为我们部门是从后端部门派生出 来的。RD负责Controller,Model是专门的数据服务,FE对HTML的控制是很弱的,所以我们想把View接过来。
这 是经过实际调研之后改进出来的一个新的工作流程,再往前是产品,产品在这步会分两边传达需求,这边给UI做图片设计,给到FE之后,静态开发之前需要跟 RD进行数据格式约定。FE现在不仅要知道页面的区域放一个价格,还要知道这个价格哪来的。这个数据格式的设计会出现一个问题,就是怎么约定才能让双方认 可?
我们后边会讲,数据格式完了之后FE做静态开发,样子渲染出来,之后模块开发又要涉及到环境搭建,这是这边的工 作路线。那边RD数据设计之后有自己的代码结构设计,业务逻辑开发。两边工作之后进行数据联调,FE做自己的模块提测,debug和上线,然后RD做他们 的测试和上线。
FE和RD只有两个地方是必须一起工作的,一个是数据格式约定,另外一个是联调,但是这是一个比较大 的或者比较常规的日常需求才需要的工作路径。而如果是一个偏向于运营的项目的话,这个工作路径可能所有RD相关的东西就被过滤掉了,我们就是静态页面,不 需要RD帮我们上线,我们文案修改,图片修改,路径修改,版本变更不需要RD,FE直接走我们这边就可以了。
调研之后,我们讨论了一下,包括跟RD人员进行讨论,最后决定试一下,这个事情在58也是一个比较大的业务开发模式的变化。
这 样做有什么好处呢?RD和FE专业性都可以了,RD就负责把后面数据服务提供的数据拼装成需要的内容就行了,FE把HTML模块拿过来,我们负责HTML 格式。个人成就感上升,对于FE需要新掌握一门技能,而且需要我们掌控到到一部分服务器端的知识,RD就更多会把他们的注意力和沟通内容放到数据结构拼装 上面。现在FE项目参与度真的提高了,现在所有的产品或者运营做一个项目的时候不会先找RD,而是先找FE。个人发展就有了新的可能,对于RD来说,每天 应付产品和运营的文字性工作肯定是不愿意的,对FE来说,现在真正开始做一个程序员了,这样才有自己提升的空间。
我们跟RD那边相关人员沟通之后就要推进这个事,第一个难点就是RD改开发模式。
旧的开发模式是这样,因为我们是java框架,后端有纯后端的代码,这些代码会访问后端的数据库服务,访问缓存的东西,访问完之后通过这边代码把数据结构拼 装出来,放到context里,这是java的关键词(其他的php或者。Net也有类似东西),从程序到模版这段要推送数据过来到模版。
模 版再对数据对象进行渲染,同时调用传过来的工具方法,它还可以做一些其它的事情。但是这个开发模式肯定是有问题的,第一虽然context有数据内容传过 来,但是并不太关注数据模型,根据页面迭代部署新的页面。当一个小型项目,比如一个一次性或者很短时间要做完的项目没有长时间维护需求的项目这样可能还 好,但是像58一个列表页维护了四五年,RD换了好几拨,模版改了好几版,会发现一个问题——后做的人不知道前面的人有没有把对象推送到页面上,为了快速 进行功能迭代就会重新把对象推送一下,数据结构就会越来越混乱,模块这曾代码结构会越来越杂乱。我们FE帮他们整理过模版,几乎30%都是无用的。
那 怎么办呢?这时FE对RD开发有规范要求,要求你这样做,logic这点我们FE不关注,但context给我们的第一要有完整的数据结构树,不再是根据 产品今天提一个需求,明天提一个需求,可以随便变,随便放到这上面,需要根据页面的功能提供什么内容,规则格式化的结构树出来,而且这个结构树如果我们通 过URL访问传特定参数,就可以不通过模块渲染呈现HTML页面,还可以直接渲染出一个JSON数据结构,可以直观的看到拼装的数据是不是正确。
第 二,在实际应用当中发现可能还是有一些特殊的方法还是需要RD帮我们封装,但是这个封装不太希望用对象的方法,最常见的,字符串的处理方法,不太希望直接 调用字符串类型的方法,更多希望是一个单独的过程方法。这样做之后FE和RD通过数据结构树就可以完成了解到数据结构是什么样的,有没有重复,有没有变化 是一目了然,而且现在很明确跟RD区分出功能,模版上面只负责数据显示和只会有数据显示相关的代码、相关的判断,不能再给我页面上写调用一个对象方法,再 调后端的服务拿数据回来,这是不允许的,一定要拼装好一个完整的context数据结构过来给我好做渲染。
这个为什 么是难点?这种开发模式对RD来说工作量非常大的,它要完整把后端代码整理一遍,而之前开发模式RD是不太需要关注context里面已经有的,只需要关 注当前需求过来,我需要取什么数据,模块需要显示什么数据,只要这两边能对上就可以了,不会关注有没有重复获取,有没有效率问题。之前很多RD不会关注这 个,现在他们是一定要保证数据结构的合理性,如果发现别人已经访问过某个服务,你不能再做重复访问,这样他们做这个事一开始很痛苦,但这个事只要做完一遍 他们就非常轻松了,而我们接下来的事情刚刚开始。
难点二、FE开发模版。
第 一就是模版新的知识,对于FE来说这些模版的语法本身是全新的知识,可能会有愿意学习的同学会做这方面的学习,但是对大部分FE来说模版语法是全新的知 识,需要重新学习。我当年学的时候是我一个前辈给我一个3页的WORD文档,但是因为我是后端出身,对我还是可以理解的。但是对FE,特别没有接触过很多 后端知识的FE来说,还是需要一点时间来学习的,花点精力才能进入状态的。
第二点才是真正阻碍FE进入模版 的工作,就是开发环境的搭建。我不知道别的公司,大家用PHP很好,现在全智能化很好,但是在58的java环境搭建,对于FE来说我工作的时候如果要搭 建一个java开发环境,做了模版解析,我的机器上面就会跑不动。因为我一般情况下会开一个ps切页面的工具,再开一个eclipse、再开一个HTML 的编辑器,一般的工作的电脑就肯定扛不住了。
此外,对58来说大部分的数据存储都不是简单的数据库,而是后端大量的 服务,配置环境就非常非常复杂。对于58成熟的RD来说,招的java开发人员大概需要两到三天进行环境部署,而且很多情况下java环境下现在对模版开 发并不算太友好。当时为了推行我们这个想法,开发环境搭建花了很多时间,想了很多办法,最后怎么办呢?我们发现一个好东西node.js,当时找到了淘宝 开源的项目Velocityjs,我们搭建了本地的开发环境,主要做什么事呢?
第一点最基础可以模拟数据环境,把 RD给我们实现的数据结构和方法可以拿node.js重新实现一遍,这样子在开发过程当中可以不做数据联调,只需要按照一开始跟RD规定好的数据结构就可 以做我们的模版开发。此外,我们还通过某个参数变化可以把RD输出的HTML变成纯JSON数据结构的输出,我们就可以拿node.js直接连那个页面, 这时候就可以做到使用线上数据的开发,看模版是不是正常的。我也挺推荐这个东西的,我知道有一些公司没有办法就跟淘宝一样写自己的node下的模块解析, 需要注意一点,各种各样的模版规则是脱离语言的,极端情况下可以在java实现一个模版开发模式,也可以在PHP下模版解析,我们能够找到开源就用开源, 开源找不到自己写一套,这是有点难度的。
最后一个难点,对于FE来说是测试。
有 的公司好一点会提供比对工具,管理工具,只要保证能顺利放到线上去就可以了。我们很少直接跟测试人员做沟通。但是现在需要深入介入到测试流程,需要有单元 测试,保证最后代码开发出来之后,测试那边bug率比较低。但我们之前模版开发是交给RD的,我们不太关心这个,我们更多帮助RD改bug。我们做推进项 目的时候,这个地方也是遇到了一些坑,主要是人员素质方面,FE之前很少有这样的机会跟测试人员一步一步跟进测试,甚至有人测试工具看不懂,或者跟测试人 员要描述清情况,这个测试环境的情况,怎么去部署都描述不清楚。
这是一个解耦前后的效果。
我 们去年花了大半年在M站尝试进行了开发模式的推进,开发效率整体提升了30%,整个排期开始一看就很明显,大的项目提升40%。M房产业务线解耦前改一次 版花24个工作日,14年之前的一次改版,之后解耦后新工作方式13.5工作日,整个效率提升43.75%,几乎是腰斩。小项目,M招聘红包,开发效率提 升30%。日常运营和产品维护就不再需要RD介入了,所有跟数据结构,数据变化不相关的需求就不要RD了,没他们事——RD其实也很高兴,58的RD有很 多人都有个人理想,技术理想,他们也不想整天在HTML浪费自己的时间,现在他们绝大部分不用他们帮助了,FE全都做了。实际上FE上线跟RD上线,从时 间效率上讲,FE还是要占优势的,这是毋庸置疑的。
我们把这个项目做的过程当中和做完之后还发现了一些附加的效果。
首 先方案选择更加合理,以前如果有一个运营项目RD实在排不开,我们要给FE做,产品运营说FE把这事全部搞定行不行,现在纯的HTML页面要实现一些纯运 营商的需求应该是没有任何问题的,这点是肯定的。但是之前因为RD时间没有办法保证,所以我们做的时候只能通过JS跟API服务器进行数据交互,这样页面 要发很多需求,有一些特殊的功能没有办法实现的,比如seo,只能静态处理。现在不一样了,现在模版在我们手里,数据只要是之前有的我就可以用,seo数 据想要什么用我就可以直接改,跟着类详情页运营页seo信息可以跟当前产品的分类、地域有很高的相关性。
第二点可以在模块上按需执行。早先的时候如果纯粹FE做的话,这些东西所有状态值这边都需要考虑到,即使是通过前面判断完全不会存在的dom,这边模版也需要隐藏存在。现在就不需要了,我已经能知道这个模块上会显示哪些,输出的时候就可以按需执行了。
第 三种各种优化不求人。我的jS和CSS要做升级,CSS结构和jS结构要升级必须找RD,一些图片一但通过了cdn服务器,很难保证所有用户看到最新图 片,如果模版在我这边,我就能保证这些。另外RD维护的HTML中写的一些代码是很不专业的,比如说简单的标签闭合,空白字符的处理,甚至能找到一些大部 分编辑器看不着的字符,RD一查两三天查不出来,但是现在是我们自己负责,处理这些问题我们也不用等RD的排期。
最 后一点,为更完整模块化开发做准备。之前,FE虽然会负责输出HTML,但是一旦上线管的模块就只包含JS和CSS,HTML上线之后跟我开发内容不一样 了,里面加一些判断,循环之后,DOM结构跟我们一开始做的不太一样了,所以很难把这三块真正做到一起处理,现在可以了,我说一个模块,包含的DOM结构 是这样的,包含的JS和CSS是那样子的。
回过头再看一下解耦之后新的工作流程。解耦这个事是不是就完成了,差不多年前年后推差的不多了,那是不是就彻底完成了?其实我们公司推的这个开发流程仅仅是开始。
FE还可以负责哪些事情?
首先,路由还是在RD手里,现在改变一个、新增一个路由配置还是要RD,还是要java上去解析的。
其次,逻辑层返回的数据是一个整体,数据结构树对双方是进步,但是这个结构树是整体,假设还是一个商品详情页,除了当前商品信息具体内容之外,可能还会有推荐信息、关联信息、广告位等等,我们如果想在某一个运营的情况下,只读取商品信息的具体内容,我还是得找RD帮忙。
第三点公共的工具类也还依赖RD,还是模块。
第四个最核心的这个项目还是java项目,只要是java项目就很难说更深入做一些想做的开发和架构调整的事情。
怎么办?我们目前的工作设想——一个理想的大前端开发模式。
我 知道在淘宝、美团,其它的一些友商已经有一些项目在这么做了,而且在现阶段的开发里面,用这种方式还可能有特殊的收益。先说这个开发模式是什么样的,就是 希望FE能够用node/php服务器搭建一个中间层,这里四个功能,第一点是路由,希望有FE能控制的路由配置器或者使用路由配置功能,这样可以控制一 些东西,比如说摩托车和自行车是共同的模版,当要把这两个模版拆开的时候就可以把这两个路由到不同的模版解析。
第二 是参数解析,现在这个东西完全是java做的,我们还是希望FE能够介入到这个参数解析上面去,当然这个参数处理有可能是全自动化的。接下来是数据请求与 拼装,现在有一些公司在推服务端的异步化,服务端开发绝大部分是同步的,比如说这个页面对后端服务器发8个请求,它是按顺序一个个发的,不是同时发起的, 时间长度是叠加的。
但是现在有一些公司在推异步,按照需求先发四个,根据回来的情况,再发后面的,这样节省很大的时 间等待。但是我希望这种事情最后也是由FE做,数据请求也是FE来做的。最后模版渲染,不管是哪种模版渲染效益都不算高,我们还是希望FE能够有权限管理 HTML的渲染,能选择一种比较高的方式渲染,不管是node.js还是其它的方式,我们希望FE有自己权限做这样的事。
这 样做(搭建中间层)还有一个好处,如果我们把中间层抹掉换APP,发现这个结构完全可以用的。这个中间层跟我们手机端APP是同样的一层,后端服务是可以 共用的,对一个大型公司来说这样做现在可能有风险,(对于小公司来说)如果为M端、PC端搭建这样的服务器,IOS、安卓开发的时候,后端服务器就可以共 同用的。而传统的RD的工作,后退在这个下面,做复杂的业务服务,一些页面可能很复杂,比如一个发布页信息提交过来之后要保存,保存之前会有大量的校验等 等的,这些服务是RD需要做的事情。而对于RD来说,这样难度,这样复杂程度工作才会是有意义的工作。
一个完整的模块结构。
因 为我做FE,专业FE到现在也没多少年,以前做java。我对模块化的概念很重视,很想在FE这边能有比较完整的模块化结构,那么我们真正FE前端模块应 该是什么样子的?当我把前面的事情都能够做完的话,模块化会发现一个新东西就是大前端下的模块组成应该是什么样的——一个完整前端模块包括数据接口配置、 模版(HTML)、js、css,不管你通过什么方式,任何HTML模块真的要跟业务逻辑绑在一起是应该包含数据接口配置,我这个模块要做什么内容。
模 版的HTML,就是DOM结构是什么样、js和css有一部分实现数据接口和显示,有一部分实现DOM层变化,这样才是比较完整的前端模块,才能在大型公 司里面,比如房产某一个页面写了模块,要在另外一个页面分享模块,就必须把所有东西做模块告诉那边,那边才能够直接用这个东西,否则只拿出来结构就有问 题。
这是更超前的想法,大型的模块配置,我希望的是由路由规则,当发现一个请求符合路由规则的时候,后面模 块是树状模块结构,最后的结果这个请求几乎是半自动化全部做完,RD和FE,RD那边只需要做复杂业务重新实现,如果没有的话就现有的这些。通过解析模块 之后把模版拼装出来。js和css也是,解析模块就可以把js和css单独拿出来再拼装好去用。
谢谢大家!
————Q&A————
提问:FE和RD之间是有交互的,以模块方式交互空间,把数据隔开,数据肯定就需要有解析的过程,现在交互量上去的话可能会是一个瓶颈,遇到这个问题,这个场景对所有模块的场景是通用还是适用哪些?
刘 浩源:数据量压力大小的问题,58访问量真的很大的,数据的缓存层不是我们负责,服务端直接根据查询条件或者ID就把之前放的缓存数据拿下来。前端搭建的 服务器的是不需要重新做保存的,只是数据层保存。其它的数据交互量其实没有多大。在我们这边主要是指的详情页、列表页。
我讲这个是开发模 式,整个过程中没有太多涉及到浏览器,是服务端的东西。Angular更多是交互层的东西,你要做游戏,或者富交互的应用可以用,但是我这个是开发模式变 了。根据不同的路由规则,一个FE小组要负责80-120个模版,不同的分类,加上不同参数城市匹配过来到不同的模版,最后需要的模块希望 是能够更复杂,东西更多的模块,这样才能更满足需求。
提问:公司内部对于这种问题,前端也是需要版本的控制,这个怎么突破的?
刘 浩源:解耦之后FE要把自己视为真正的RD,就要开始适应做纯粹的程序员,要适应一些新的工作的规则。刚才说的这个开发模式有另外一个附带的好处,这个是 有争议的,在大部分情况下,用这个开发模式开发的内容可以分开上线的,RD和FE可以分开上线的,RD可以是旧的数据结构。全部转成新的模式之后,我FE 要上模版了,如果产品这边觉得有一个短时间不匹配就接受,那我可以直接上,如果产品不认可这种不匹配,我们也可以加一些判断,判断一下数据结构有没有新的 对象,有的话我就加上去新的HTML结构。当然,版本控制也是需要的,FE是需要重新学习的,要真正把自己看成是程序员。
提问:您现在开发模式里面前后短联调之后,前端跟后端单独提测,前端是模版提测,这是什么意思?
刘浩源:开发环境调好的模块部署到测试环境。
提问:这不是还是相当于得有后台数据填充到模块里,这不是跟之前开发模式是一样的,发布上去之后,相当于模块跟RD提供的数据在一起。
刘浩源:极端情况下双方提测可以分离的。
提问:我理解您的提测应该还是在一条线,而不是单独提测,您意思是测试在前后端联调之后,只提测前端,只提测后端,两者是分开。
刘 浩源:两个原因,首先RD提测不是必须的,这个需求如果没有涉及到数据结构的变化,我是不需要RD提测的,可以改。另外一个极端情况下RD如果时间安排或 者工期赶不上,这边模版可以先上。这样有个好处不会因为RD上线,因为java服务器上线相对要复杂一点,不会因为等他那边东西上线很晚,我今天晚上也等 到两三点。