前端开发与后台开发如何协作?

如何减少空档期。团队不在同一层楼。
关注者
1541
被浏览
85197

30 个回答

首先, @learnshare给出的两种开发模式基本上就是我们所有的选择了,后端提供api,完全前端渲染的开发模式如果可行的话,angular或者react都是不错的选择。但如果网站业务决定了必须seo友好而必须进行服务端渲染的话,如同@learnshare所述,现有的开发模式下,前端和后端人员的协作是很困难的。下面我来谈谈我对这个困境的看法以及我们正在实践的一个非常棒的模型(最近我在好几个问题下面都回答了类似的内容,会不会被当成spam。。。)。

首先,服务端渲染的前后端分离之所以困难,根本的原因不在于模板技术的复杂性上,而在于MVC模式本身是有问题的。本质上讲,MVC是面向业务过程的,对于企业应用开发,MVC模式的确是无上利器,可以清晰的分离业务逻辑层次,让程序员将精力集中在业务逻辑的整合上(其实,我觉得即使对于这一点,传统的MVC模式也没有做得足够好,这里重点不讨论MVC,就不展开了,有兴趣的可以看看这个:Asta4D Framework User Guide,算是我对传统MVC模式的一些思考),但是,MVC模式本身的重点在于M和C,而V只是一个附属品,一个用来展现业务流程的可视化界面而已,因此,通常对前端工作的要求是很低的,能够展示数据,能够将业务流程向前推进,这就足够了。

回过头来,对于重点是展示内容并帮助用户获得有效信息为主的互联网网站来说,MVC本身就是不合时宜的,常见的例子就是,比如淘宝的首页,model是什么?再进一步的,淘宝的宝贝页面,也许可以把当前宝贝作为model,问题是,边栏之类的周边信息怎么整合到这个model中去?当然,不是做不到,但就此带来的复杂性,实际上已经宣告了MVC的软弱。

我上知乎的时间不太长,很奇怪在知乎没有看到过任何讨论view first模式的帖子,这个模式是由lift(Lift :: Home)最先提出并实践的,可以说,view first模式从根本上解决了内容展示型的网站的MVC困境,可以极大的提高开发效率。

(说到这里,还没有说到前后端如何分离。。。我都有点着急了。。。)

view first的基本理念来说,就是视图,view,才是整个系统的第一优先对象,所有的代码结构,所有的逻辑,都要围绕view来展开,传统的MVC模型,一个url,要先映射到一个controller,然后controller构建model,最后导向一个view,但在view first下,一个url,就对应一个view,服务端接受到request,view就开始渲染了,在渲染过程中,不断的取得需要的数据,并完成整个页面,这个过程中不需要controller来控制,也就更不需要model来沟通controller和view,一切都是以视图为基础进行的。说到这里,其实很多人应该已经明白过来了,这不就是传统的PHP开发模式嘛,先写html,然后把php的动态代码嵌进去,OK,搞定啦,是快呀,可这代码没法维护呀,前后端也没法分离呀。。。别急,快了。。。

可以说,view first这个模型,其实就是传统的php开发模式,lift的贡献在于,首先明确并命名了这样一个开发模式,从理论上解决了开发效率的问题并且将开发人员从MVC的迷思中解放出来,然后,lift更重要的贡献是,从实践上解决了view first模式下代码不可维护与前后端分离的问题,提供了一个前后端完全分离的模板模型。这里多说两句,lift本身是基于scala的,我们公司在用了两年lift之后鉴于对scala的种种不爽,决定还是退回到java上,虽然lift本身也支持用java进行开发,但我们觉得一个pure java的方案会更舒服,而且lift本身也有一些细节我们觉得是有改进必要的,因此我们开发了自己的框架Asta4D(astamuse/asta4d · GitHub),虽然我们提供了很多不同于lift的功能,但单就view first和前后端分离的模板来说,基本上是一个95%拷贝加5%改良的lift,下面我就用我们自己的框架来举例,相信java代码大家看起来也会更舒服一点。

首先,无论是lift,还是我们的山寨Asta4D,前端工程师面对的都是pure html的模板,如下:

<section>
    <article>
        <div>
            <p id="name">name:<span>dummy name</span></p>
            <p id="age">age:<span>0</span></p>
    </article>
</section>

前端工程师可以自由的填入stub数据来调试他们想要的效果或者交互逻辑,在他们完成工作后,这个模板交给后端工程师的时候,后端工程师会在模板中嵌入一点代码:

<section>
    <article afd:render="SomeSnippet:showProfile">
        <p id="name">name:<span>dummy name</span></p>
        <p id="age">age:<span>0</span></p>
    </article>
</section>


好吧,这个时候想像一下,前端工程师发现了一点bug需要进一步修正,我们可以相信的一点是,在99%的情况下,后端工程师加入的那一行“afd:render”的代码应该不会给前端工程师造成干扰,因此,这个时候,我们的前端和后端就已经可以开始同时工作了,先把前端的工作放在一边,看看后端怎么填入真实数据:

 public Renderer showProfile() {
        Renderer render = Renderer.create();
        render.add("p#name span", "asta4d");
        render.add("p#age span", 20);
        return render;
    }

后端用css selector来标定数据锚点并将真实数据填入,因此,在最简单的情况下,只要数据锚点不变,无论前端工程师如何重构模板代码,都不再需要后端工程师的介入了。当然,这里有一个显而易见的问题,前端工程的重构并不能保证数据锚点不变,因此,我们在实践中,引入了一个所谓的“X约定”,简单的讲,我们的后端工程师会在模板中再多加一点东西:

<section>
    <article>
        <div afd:render="SomeSnippet:showProfile">
            <p id="name">name:<span class="x-name">dummy name</span></p>
            <p id="age">age:<span class="x-age">0</span></p>
    </article>
</section>

大家可以注意到,后端工程师在数据锚点上加入了以x开头的伪类,这样,后端的渲染代码就变成下面这个样子:

 public Renderer showProfile() {
        Renderer render = Renderer.create();
        render.add(".x-name", "asta4d");
        render.add(".x-age", 20);
        return render;
    }

仍然是用css selector,只不过不再用tag而是用class来锚定数据,我们可以看到,class中加入的“x-”一方面不会对前端工程师的工作造成任何干扰,另一方面也起到hint的作用,前端工程师只要能够将“x-”标记的数据锚点保持不变就可以放心大胆的重构代码而不需要后端工程师的介入。

在我们的实践中,一般的开发流程是前端先完成页面,然后后端接手填入数据,这个中间通常不会进行交流,因为我们的前端和后端甚至是分开在两个部门的,大家的交互就是redmine的ticket的转交而已。当然,在某些时候,后端工程师无法理解前端的模板不知道应该将数据填在哪儿的时候,还是会有必要的交流,但这种交流真的很少发生,至少不需要他们非得坐在一起工作:)

更进一步的,某些时间很紧的开发任务,前后端甚至是同时开始工作的,后端会开一个debug页面,在里面只用div和x-来标记数据锚点并完成后端的渲染逻辑,而同时前端会完成正式的html页面代码,最后,由后端将渲染逻辑合并到正式页面即可。当然,这种情况下,前后端的交流会多一些,我们的前端mm摆脱后端猥琐大叔们纠缠的办法就是尽可能快的先完成基本的html骨架push上去,然后告诉他们,你们自己玩去吧,别来烦我了^_^

更为具体的一个我们的实践的例子是,一个耗费前端两个人月的页面大规模重新设计和重构,在前端完成工作后30分钟,我们的后端就完成了所有必要的修改并将代码合并到主干准备进入release流程了。嗯,因为我们能做到这个,所以我们的前端和后端就一直是两个部门没人提合并的事情。

最后,题外话,最近react.js突然吸引了很多人的目光,对于客户端渲染真的是非常不错的东西,而我们的框架Asta4D,同样的提供了服务端渲染下的虚拟DOM组件模型。哦,这里就不赘述了,有兴趣的可以自己去看我们的user guide。

大家看几个我们的页面吧

Detailed information of toushiba corp.

Publication number 225663) a nonvolatile memory device

Technology and business trends of Glass melting and manufacturing

我们的网站其实是日文的,这个英文网站是做给华尔街的大爷们看的一个简装版,相对内容要简单些,但大家仍然可以看到,我们在前后端完全分离的模式下可以做得很漂亮。


============感谢 @贺师俊的评论,评论回复里面没法回答太多,我把回复贴到这里=====

贺师俊
你的 showProfile 其实就是变相的 model,x-name 和 x-age 就是 model 的属性。

这取决于你如何定义model。

从简单的ORMAP的角度来说,我们必然有一个entity,对我们的这个架构来说,这个entity是一定存在的,从这个角度上说,这里的确有一个model,就是ormap中的entity。

但是从MVC模式的model来说,MVC的model并不是简单的entity,而是一个包含了所有前端必须数据的container,从这个意义上讲,我们没有model。

最后,我上面的回答跟你指出的事实,其实有点不搭界,你的意思是,render的方法本身就是一个逻辑上的model,而x-就是model的属性,老实说,这个观点真的很有趣。

你指出的事实让我陷入了长考,这里究竟有没有model,从逻辑上讲,你是对的,我思考了很长时间,这种逻辑上的model跟MVC的model的区别是什么?

我的理解是,首先,我们不需要在代码中构建一个大杂烩的数据容器,而是在一个极小的范围类定义了一个局部适用的小数据结构,从这一点说,跟传统MVC比起来,我们做到了更细粒度的解耦。其次,从实现的角度讲,我们鼓励开发人员尽可能简单的取得数据,我们近乎变态的尽可能的执行以一行为单位的无join查询(性能依靠缓存保证, astamuse.github.io/asta),这绝对比传统的MVC模式的开发效率和可维护性都高得多。进一步的,我在原文中已经强调过了,这里的“x-”约定,只是一个hint,对前端没有任何强制的约束能力,前端不会因为破坏了这个约定而导致页面崩溃出错,反过来,前端在有必要的时候可以完全无视这个约定,数据的整合是由后端完成的,但页面的逻辑,是前端主导的,开玩笑的说,“x-”约定是我们后端人员对前端大爷的哀求:“大爷啊,不要乱搞我好吗。。。”。

所以,我们对前后端分离的开发模式的理解是,最重要的一点是,前端具有主导权,由前端决定开发的走向而不是后端,后端的职责是满足并提供前端需要的数据,反过来,前端没有义务和必要为了后端的各种技术上的理由而去学习或者说导入各种跟前端无关的技术(有任何前端开发人员会喜欢velocity之流的模板的吗?),前端需要最大限度的自由度去完成创造性的工作,后端的职责是配合他们。进一步的,后端的技术意义在于,以更合理的后台架构,更方便,快捷的提供前端需要的数据,在这个层次上,我们需要缓存的设计,需要合理的api设计,需要后台存储架构的各种变化,但是,在最终向前端提供数据这一个逻辑层次上,后端可以比前端开发人员写出更有效率的利用现有API取得必要数据的逻辑,但后端不可能比前端知道如何更有效率的将数据展现给用户,因此,我们对view这一层的分工的理解就是,前端负责数据如何展现,后端负责取得数据并提供给前端。

最后,无论那种模式,最终总有一个标记数据位置的参数,或者是MVC中model的属性,或者是我们这里的css selector hint,或者是一个嵌入的变量名,从这个意义上讲, 任何模式都有个model,都有个属性,所以,即便我很同意你说的逻辑上showProfile就是个model,但我仍然认为,我们从本质上是anti-MVC的。
首先建议要前后端分离开来做,这样在后期维护更新的时候能节约很多时间,很多框架思想或者组件什么的都是基于分离思想上提出来的。

然后考虑具体采用哪种分离方式,目前比较流行的有两种,一种是传统MVC模式,一种是纯接口类似于SPA模式。

其实MVC虽然做到了view层的部分分离,也就是前端负责模板,后端填充模板的方式虽然做到了工作上的分离,但实际上前端和后端的代码还是交织在一起的,同时整个页面模板的控制权在后端手里,这样有时候也容易暴露出弊端:
  1. BUG谁负责?MVC模式的站点运营一段时间后出现BUG,想要分清楚是业务逻辑上的问题还是前端模板的问题并不是那么简单的,有时候还需要反复的进行定位,如果经验不丰富常常要在BUG的定位上浪费很多时间。
  2. 维护成本。如果页面加以改动和BUG修复,前端修改页面时必须要建立后端的环境,除非你把HTML抽出来改,当然很少有人会做这种吃力不讨好的事。同时这些改动是前后端一起进行的,有时候可能还会返工到前端再改,在反复的维护中也带来了一定的沟通成本。
  3. 页面逻辑体现不完美。只要接触过前端或者后端的人都能很清楚的意识到整个页面的逻辑或者是方向大多数时候是掌握在前端手里的,当一个页面足够复杂时后端要理解前端的页面设计逻辑和控制页面是非常浪费时间的一件事。而前端拿不到页面控制权也很容易使页面逻辑脱离自己的方向。
  4. 开发力度不均等。前端如果只是切图+效果就完成工作,剩下的一切交给后端,会让整个项目的后端工作繁重。

在这些问题的基础上,有人开始使用类似于SPA模式的纯接口开发模式,简单的说就是后端只提供接口,具体的页面实现和逻辑都交给前端,相比之下这解决了不少MVC模式带来的问题:
  1. 均衡开发力度。前端掌握view层的控制权之后工作当然也会更多一点,后端则可以更加专注业务逻辑工作,整体平衡了项目的前后端工作重量。
  2. 易于管理和维护。相比之下代码的清晰度,逻辑的清晰度更高,不管是后期维护还是更新都有不少的帮助。

当然啦,虽然看起来很美,但是SPA模式很难解决SEO问题,通过接口拿来的数据必然是填写在空白的html模板中的,而蜘蛛访问时不会考虑js,在蜘蛛频繁的抓取的空页面时会认为这是一个垃圾页面,从而影响网站PR等评分。当然你也可以识别蜘蛛来引导一个假页面欺骗,但是实际效果很差而且浪费工作量。
除此之外SPA模式前端传值的安全性也是备受考验的一点。

后来淘宝UED提出了一个nodejs的中间层办法,但是搜索一下会发现实际真的这样做的人并不多,可能是部署相对复杂而且可供参考的资料较少,还不够成熟。原理是提供一个nodejs中间层来获得前端控制器的权利,也就是前端更方便的操作路由,当然最重要的是渲染模式多变,解决SEO问题的同时还保证了页面的即时交互性。

除此之外也有一些MVVM的做法,和SPA模式最大的区别在于vm不操控html代码,只对值进行修改。

我的经验是如果传统的PC页面还是建议用MVC模式来搞定最好,如果涉及移动端或者复杂且高度的交互时,可以考虑纯接口模式,如果公司希望能够探索更多的方式则可以试试新方法。
其实这些模式并没有说哪种最好,只是提供了一个目前WEB开发模式的解决方案,以后随着技术的成熟肯定会有越来越多的开发方式引进,前后端的协作也会变得更直白一些。