xufei / blog

my personal blog

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

未来Web应用的前端技术选型畅想

xufei opened this issue · comments

commented

上半年,我写过一篇《2015前端组件化框架之路》,现在大半年过去了,这段时间一直在思考,未来的东西是怎样的。

目前我主导着苏宁的云计算相关的所有前端项目,这些项目以控制台为主,几乎都广泛使用了Angular 1.x,一方面因为个人技能之前有积累,一方面因为产品的开发人员基本都是Java方向转岗,对Angular的接受度较高,上手非常快,开发效率也非常高。

但2015年,前端的世界发生了很多变化,这些变化快得超出我想象。在这个巨变的时代,产品的技术选型是个麻烦的事情,具体来说,有几个方面:

  • 如果2-3年后新开始一个业务项目,可能会有什么样的技术方案?
  • 如果现在立刻开始一个新业务项目,可能会有什么样的技术方案?
  • 如果持续维护老的项目,后面可能会对它们有怎样的迁移方案?是逐步迁移,还是推倒重做?
  • 在PC端项目为主体的业务体系里,如果将来某个时机出现了移动端项目,该如何去选型,并且利用之前的业务代码?

我之前没有预料到的,是ES6的普及之快。在此之前,对于新的语言特性,人们一般会等到支持的浏览器普及之后,才会大量使用,比如ES5,但由于Babel这样转译的工具出现,我们可以渐进使用,所以,开发过程中可以完全使用ES6甚至ES7的特性编写代码,然后通过构建去达到兼容的结果。

有鉴于此,在未来的项目中,使用ES语言新特性进行开发,是一个必然要做的事情。但,这并不能算是整体方案。整体的方案应当包括但不限于:

  • 使用什么基础框架
  • 业务代码如何规划
  • UI组件如何规划
  • 样式和主题如何规划
  • 构建方案怎样
  • 人员如何协作
  • ……

所以,我们面临的,还是基础框架选型这么一个重大问题。照理说,使用Angular 1.x,后续应当选择往2.0版本过渡,但现在这个阶段,乱花迷人眼,谁也不知道未来的事情怎样,在这一层上,我个人觉得还是要再看看。

于是就卡在这里了,这个选不了,后面的事情都没法考虑做了吗?

也不尽然,我考虑了一段时间,觉得虽然每个层面都比较麻烦,但至少可以分层隔离一下。比如说,我们选了某个UI层的组件化框架,并不意味着对下层的数据模型和业务逻辑就有很强约束,至少说,这层还是有很多可选方案。

通常我们在前端,可以对一个Web应用这样分层:

UI层(View) -- 业务逻辑层(ViewModel / Controller) -- 数据层(Model)

比如说,数据层,有Relay,有GraphQL,有Falcor,但我们还可以继续使用原先的RESTful API啊。我们可以不使用某框架自带的请求库,比如$.get,比如$http,但我们还可以使用super agent这样独立的,框架无关的辅助请求库啊。甚至说,我们不想使用XHR了,还可以使用Fetch啊。

所以,把Web应用的前端先分层一看,发现每个层里面,都有很多独立的可选方案,而这些方案是可以组合的,比如说:

上层用React,下层用Falcor或者RESTful,然后把上层换成Vue,好像也没有什么不对啊?下层完全可以不动,也不需要就把每层代码都改一遍啊?

这样一来,我们可以先不管UI层,直接先把下面两层全部构建出来,这个部分不对DOM产生任何依赖,所以,跟上层框架没有关系,也无需按照上层框架的约定。

我们引入一个框架,对整个系统来说,最大的影响是会产生一些约定。有时候我们需要这些约定来帮我们规范代码,但在现在这种形势下,会尽可能希望框架本身不要产生约定,由我们自己,按照ES自身的一些机制来形成代码规范。

比如说,我们使用module,class之类的语法特性,基于传统的OO方法论进行一些规划,利用各种设计模式。或者,我们也可以基于函数式的理念,进行另外一个方向的规划。总之,这个层面的东西是纯业务的,可测试的,可独立运行的。

在构建模型层和业务逻辑层的过程中,我们可以使用ES6,也可以使用TypeScript。之前我曾经有个断言:如果ES6普及得快,TS的形势就会不太好。这主要是因为考虑到如果一个开发者已经在使用ES6,他去使用TS的可能性并不会很大,而如果他到ES6流行的时候尚未接触TS,后面接触TS的可能性就比较小,直接用ES6的可能性比较多。

不过,当业务逻辑比较复杂的时候,使用TS会有一些优势。即使不使用TS,我也建议把数据模型预先定义出来,在实体类里面做一些事情,尽可能使用实体类来构建数据,而不是直接用字面量来定义。当应用规模变大的时候,“严谨性”变得更加重要。

另外一个角度,如果我们要尽可能构建框架中立的业务逻辑层代码,最好是脱离上层框架的绑定监控机制,自己通过比如getter,setter这样的方式,实现数据模型的内部联动,所以从这个角度,预定义数据模型也是必要的。

在这个基础上,再回到我们的现实来,在文章开头,我提出了几个要考虑的可能,现在可以逐一回答了:

1. 如果2-3年后新开始一个业务项目,可能会有什么样的技术方案?

底层如上所述,上层根据当时情况判断选择

2. 如果现在立刻开始一个新业务项目,可能会有什么样的技术方案?

底层如上所述,上层使用Angular 1.4或者Vue之类的成熟框架,同时,使用ES6开发

3. 如果持续维护老的项目,后面可能会对它们有怎样的迁移方案?是逐步迁移,还是推倒重做?

先逐步重构,维持UI层框架不变,把底层重构成上述那样,然后引入ES6,先搞成方案2这样,后续再考虑迁移上层。

4. 在PC端项目为主体的业务体系里,如果将来某个时机出现了移动端项目,该如何去选型,并且利用之前的业务代码?

先把PC端重构如方案3,然后,PC端可继续使用Angular,移动端上层选用Vue之类性能较好的轻量库,PC端与移动端共用业务逻辑层。

以前有一段时间,我一直觉得Angular的all in one是一种挺好的策略,但最近考虑了很多事情之后,觉得将来这种方案的优势会逐渐削弱,所以,现在我也觉得纯粹做上层视图框架的Vue之类有不少好处。在未来,约束越强的框架很可能越不受欢迎,基于ES自身的语言特性做业务代码约束才是王道。

这篇主要是比较笼统地谈一些想法,后面会写两篇具体细节策略的考虑。

叔,还记得大明湖畔的李凤姐吗

民工叔棒棒哒

好文!

还是从技术角度来分析的,技术选型,在于前端整体的发展形式,ES6的定稿以及已经可以以ES6来进行开发,所以目前的选择会有些难以明确的地方。但是,即便如此,我们依然可以根据功能或者职能的不同,在合理的范围内做出符合当前利益的选择。
有没有发现,all in one和web应用分层选型,在某种形式上很像IOS和Android?也就是封闭还是开放,当然,前端领域纯粹的封闭只能是死路一条。我们是用一个框架把整个web应用整个都覆盖掉,还是每个部分是独立的,保持各部分的纯粹性,由开发人员根据各自的情况来进行整合?

在我看来,通常,对个有能力的大公司,每个部分都拆开来,选择最好的配件,以此来达到最优的收益,这就有点像定制的东西,虽然成本高,但是很适合。
对于实力或者资金不是那么雄厚的公司来说,选择all in one是比较有优势的,首先,它有一套完善的解决方案,不需要人力去研究各部分细节,虽然不是特别定制的那么顺手,但拿起来就能用。其次,一整套的方案比较容易出成果,它做出来东西快。这会使得普通开发者普遍喜爱,比较容易拥有广泛的群众基础,广泛的群众基础又会促进其生态圈的完善,如此循环影响。

所以,通常目前国内的大公司内部的技术选型,都不太会被广泛使用,主要在于它只适合相对于自己业务或者同样体量的公司。

但并不是说大公司出的东西就不会有广泛的应用,非常有代表性的例子比如:AngularJS、ReactJS,一个是出自Google,一个出自Facdbook。之所以它们的框架会非常流行,我认为是由于其设计高度,也就是站的层次的问题,其不仅仅是出于公司自身业务。

一点拙见,还有点跑题,欢迎拍砖。

human readable data tree => react component tree => dom tree
human readable data tree => vue component tree => dom tree

react 的 virtuail-dom 本质是 json 数据,有了这层隔离,映射到 dom \ native \ canvas 都行。

human readable data tree 是新的隔离层,不管 view 层是什么,都可以桥接过去。

这是我对前端编程模型的的大概思路。

commented

+1回家看。

前排占坑

commented

围观

commented

react 果真影响了前端界 😄

Angular 1.4或者Vue之类的成熟框架....

虽然我也在用 vue,但是不敢这么说啊

commented

@sinoon 支持这种说法,得分场景

前排占坑

滋慈,不过关于typescript这里想说一下,其实TS和ES6并不冲突,TS本身定义的就是JS的超集,最新版已经引入了ES6的绝大部分特性,而且完好的支持TSX。它最主要的目的是解决动态弱类型语言的在开发过程中的缺陷,力求达到C#/Java开发体验,而JS再怎么发展发展出花来也不可能引入静态类型,于是强类型静态语言才能有的开发优势也就享受不到。

上个月去听了Anders来北京关于TS的讲座,虽然没讲完,但是很好的暴露出JS开发的坑并如何用TS去解决这些问题,所以我个人推荐有精力的人还是去学习一下,官方文档2个小时就能搞定,况且Angular 2也要用这个

commented

膜拜之。。

commented

厉害。。
好文要常读

组合各个层次,还真需要点能力

ES6普及真是好快,react 项目基本都是ES6。

commented

前排围观

膜拜……

对于前端工程化,MVC之类的分层设计,确实很有必要,否则牵一发动全身,说到底,都是软件,需要设计!

不过感觉楼主对React的态度比较冷淡。
之前我也很不感冒,不过最近一周看了下React和Redux的官方相关文档,态度有所转变。
再加上我在公司项目使用MVVM框架(RactiveJS)编写组件的时候遇到的一些问题:大部分由于项目变大以后组件化带来的复杂性和双向绑定所引起。发展到这个阶段,这种“传统的”MVVM框架的优势显得不再那么明显,而由此引发的新问题却非常难以解决。
React入门不难,但是真要开发实际项目要学习的东西还是挺多的,开发效率在初始阶段对比传统MVVM框架也没有任何优势。但是他的组件化以及单向数据流的设计**确实先进,在项目膨胀以后不管是发现问题还是解决问题都比传统的MVVM框架要方便的多。

commented

我发现angular的公用指令很容易写的很庞大,但是写组件的人对业务不可能100%覆盖,加上API和设定有会繁琐(因为支持的功能多),业务开发自己写自己用的又是一个分分钟的事,导致不太受欢迎
感觉组件想通用很难,在业务逻辑差不多的情况还行

commented

@sutaking 这个问题,我觉得是这样的,我们写一个公用组件,往往还会用第一代前端框架的思维去考虑,无数的配置项,无数的内部解析,不庞大是不可能的,而且还会出现互斥需求。

但在Angular这样的体系里,没有必要这样去做,我一直强调一个观点:模板本身也是配置项。在用Angular之类东西做组件的时候,应当把模板作为配置项来使用,分离到组件主体功能的外部去,组件自身只做基础布局的增强用。

#22

我这篇里面有个DataGrid的例子,就是这个意思。

commented

@simongfxu 我确实对React的态度比较冷淡,其实不仅是我,MVVM流派的不少人都不太接受React,并不一定要那样的方式才是组件化,并不一定要那样的方式才是单向数据流,才能导致可控,部分观点我也谈过:http://www.zhihu.com/question/31613336/answer/62814977

在这个主题下面,尤雨溪和刘骥的答案是我深为认同的。

面对大型Web应用,我们要的是两个东西:

  • 可控,组件化是一种策略,我们要的是两种东西可控,一种是组件之间的关系,一种是组件内部的数据,这两者,不同框架有不同策略去做,并无高下之分,主要还是取决于使用者的水平
  • 高效,从开发效率讲,带双向绑定的框架有其不可阻挡的优势。

哈哈,感觉最近都在反思这些,上周末也写了一个类似的: https://github.com/phodal/repractise/blob/gh-pages/chapters/frontend.md

引用部份原文:

前端的演进在这一年特别快,Ruby On Rails也在一个合适的年代里出现,在那个年代里也流行得特别快。RoR开发效率高的优势已然不再突显,语法灵活性的副作用就是运行效率降低,同时后期维护难——每个人元编程了自己。

如果不能把Controller、Model Mapper变成ViewModel,又或者是Micro Services来解耦,那么ES6 + React只是在现在带来更高的开发效率。而所谓的高效率,只是相比较而意淫出来的,因为他只是一层View层。将Model和Controller再加回View层,以后再拆分出来?

现有的结构只是将View层做了View层应该做的事。

首先,你应该考虑的是一种可以让View层解耦于Domain或者Service层。今天,桌面、平板、手机并不是唯一用户设备,虽然你可能在明年统一了这三个平台,现在新的设备的出现又将设备分成两种类型——桌面版和手机版。一开始桌面版和手机版是不同的版本,后来你又需要合并这两个设备。

其次,你可以考虑用混合Micro Services优势的Monolithic Service来分解业务。如果可以举一个成功的例子,那么就是Linux,一个混合内核的“Service”。

最后,Keep Learning。我们总需要在适当的时候做出改变,尽管我们觉得一个Web应用代码库中含桌面版和移动版代码会很不错,但是在那个时候需要做出改变。

对于复杂的应用来说,其架构肯定不是只有纯MVP或者纯MVVM这么简单的。如果一个应用混合了MVVM、MVP和MVC,那么他也变成了MVC——因为他直接访问了Model层。但是如果细分来看,只有访问了Model层的那一部分才是MVC模式。

模式,是人们对于某个解决方案的描述。在一段代码中可能有各种各样的设计模式,更何况是架构。

commented

Mark好文

现在前端选型确实是个纠结的时间,angularjs 1 or 2 or react.js?
感觉Angularjs适合大规模的后台项目,团队整体前端技术一般的场景;
React.js更适合C端项目,对团队整体前端技术能力要求相对高一些

如果现在有一个全新的webapp项目,我应该会选择用 webpack+npm 来做模块化。然后用react或者vue.js来做UI层的MVVM和组件化。应该还会结合webpack的babel-loader来使用ES6和jsx,或许还会永css-modules来处理css的复用和模块化。

commented

强力插入,也许快速开启一个项目也很重要,推荐下基于 LeanCloud 的 Web 方案。
https://github.com/leancloud/LeanEngine-Full-Stack

@yutingzhao1991 手机访问uhouzz.com 我们团队开发的webApp webpack+vue.js 实现的欢迎交流。 @xufei 好文,我们团队没有历史负担,PC用angular 手机端用Vue.js

vue+ webpack + vue-loader + babel-loader + window.fetch + npm + less 做项目中

2015-11-17 11:12 GMT+08:00 寇云 notifications@github.com:

@yutingzhao1991 https://github.com/yutingzhao1991 手机访问uhouzz.com
我们团队开发的webApp webpack+vue.js 实现的欢迎交流。 @xufei https://github.com/xufei
好文,我们团队没有历史负担,PC用angular 手机端用Vue.js


Reply to this email directly or view it on GitHub
#24 (comment).

sfsdf

@chaoren1641 在Vue论坛看到你身影,请问是哪个团队的?

再多说几句。@xufei 个人现在不太喜欢angular就是像你说的它all in one。而不是像vuejs和react这样只关注UI。你上面有说

通常我们在前端,可以对一个Web应用这样分层:

UI层(View) -- 业务逻辑层(ViewModel / Controller) -- 数据层(Model)

比如说,数据层,有Relay,有GraphQL,有Falcor,但我们还可以继续使用原先的RESTful API啊。我们可以不使用某框架自带的请求库,比如$.get,比如$http,但我们还可以使用super agent这样独立的,框架无关的辅助请求库啊。甚至说,我们不想使用XHR了,还可以使用Fetch啊。

所以,把Web应用的前端先分层一看,发现每个层里面,都有很多独立的可选方案,而这些方案是可以组合的

所以说当UI,Model 这些是可以独立选择使用上面方案的,但是其实在这之上应该还有如何去组织这些模块的问题,有没有研究过使用flux而不是用MVC这样的方式?

commented

@yutingzhao1991 “组织这些模块”是什么意思?如果你说的是模块关系,这是通过import之类去关联的,如果你说的是数据的关系,flux跟mvc也并没有冲突啊?

@xufei 额,组织模块说的不太对。应该是说怎么去做整体的一个架构,如何去组织UI层Model层等,MVC算是一种方案。想知道有没有更好的方案。“flux跟mvc也并没有冲突啊?” 这两个不是应该是只能二选一吗?

commented

flux也可以作为mvc的补充啊,为什么一定是二选一…… @yutingzhao1991

@xufei
我认为这是两种截然不同的模式,一起用会不会怪怪的

https://facebook.github.io/flux/docs/overview.html#content

Flux eschews MVC in favor of a unidirectional data flow. When a user interacts with a React view, the view propagates an action through a central dispatcher, to the various stores that hold the application's data and business logic, which updates all of the views that are affected. This works especially well with React's declarative programming style, which allows the store to send updates without specifying how to transition views between states.

We originally set out to deal correctly with derived data: for example, we wanted to show an unread count for message threads while another view showed a list of threads, with the unread ones highlighted. This was difficult to handle with MVC — marking a single thread as read would update the thread model, and then also need to update the unread count model. These dependencies and cascading updates often occur in a large MVC application, leading to a tangled weave of data flow and unpredictable results.

说polymer的会被打死么

@QActor 力挺Polymer,react一生黑。

想请教一下vue和angular这样的框架如何更好得控制数据的可控性?总不能在数据可能变化的地方拷贝一份吧?

Ember.js 在博主眼里存在感好低,不过看到这段话后就了然了:

我们引入一个框架,对整个系统来说,最大的影响是会产生一些约定。有时候我们需要这些约定来帮我 们规范代码,但在现在这种形势下,会尽可能希望框架本身不要产生约定,由我们自己,按照ES自身的一些机制来形成代码规范。

不过不明白为什么 希望框架本身不要产生约定,可以更进一步解释一下吗?

commented

@ugoa 前面这些年,我们看到很多库或者框架,都包含着各种约定。其中有很多,是因为语言本身提供的能力有限。

比如说,大家都可能都会有模拟Class的机制,又比如说,类似Backbone那样,把数据模型做一层包装。

我们往后再看几年,ES新标准、新特性的使用势不可挡,如果不利用语言本身提供的能力来简化业务开发,是一种很严重的浪费。

比如说,以后我们定义一个模型:

class User {
    set firstName(val) {
        this._firstName = val;
    }

    get firstName() {
        return this._firstName;
    }

    set lastName(val) {
        this._lastName = val;
    }

    get lastName() {
        return this._lastName;
    }

    get fullName() {
        return this.firstName + " " + this.lastName;
    }

    set fullName(val) {
        //
    }
}

对于一个大型业务系统来说,不把模型和业务逻辑、规则定义清楚,后果是灾难性的,这些东西的定义其实是可以脱离语言本身的,也是应当能够跨越多种语言存在。

在这种情况下,数据模型不会是贫血的,会包含自身的一些方法,比如说,一个User对象,它至少能够校验自身是否合法,因此,无论是在表单上使用,还是在服务端做操作抛出异常,都可以使用它自身所存在的约定。

我们不但会期望这部分的描述是跨框架的,而且是跨语言的,甚至是一种可以用图形方式描述出来,然后再转译到各业务语言,并且,还可能期望这些业务模型和规则之间,存在可视化的关联关系,这样,从更高层来看,这个业务系统才会更清晰。

然后,再回到前端框架这里,如果我们有了这样的纯语言层面的数据模型和规则,可以使用不同的框架对其做适当包装。但是,注意到没有,在这里,有很多框架是不需要再次包装数据模型,而另外一些是需要的,所以,为什么我近期的文章不谈Backbone,Knockout,Ember,而是一直在谈Angular,Vue,React,就是因为我不倾向于对数据模型有额外的包装。

我理解他们之前做包装的意义,是因为语言本身能力不足,但2015这一年,ES语言的推广速度真是难以想象,所以,后面那几种框架/库的优势更大了。

刚才这些只是从一个方面阐述我的观点:为什么尽可能希望框架本身不要产生约定。

commented

@Jokcy 这是个复杂话题,可以单独开几篇谈了

@xufei 嘛~给点提示嘛,因为这块挺麻烦的,之前一直没有什么思路,最近接触了react发现用immutable可以很好地进行控制,这也是官方推荐的做法。但这似乎不太适合用于vue和angular(我并没有验证),你的经验肯定比我丰富,所以想听听你的意见。

angular1.x和vue这种UI层面的处理方案确切的讲应该叫binder吧。它藏在view后面,同时又联系着model。我很同意徐叔关于框架应该尽量让model独立,同时binder层面应该灵活适应不同的model。这方面knockout没有做好,也导致它渐渐的在框架潮流中落伍了。 React的flux单向数据流模型,我觉得给之后技术controller层面提供了很好的思路,它跟踪了每次数据模型变化的流向。这样的设计方式不仅让开发者能够很好的debug,同时也让程序从angular和vue这种小沙箱(sandbox)中解脱出来,整体可控性变得更好。
ES6这块,我觉得它增加的JS块作用域,优化异步的方案倒对框架没啥太大启发。倒是class的提出让Model更好看了,新框架的编写方式也能不再使用一大堆prototype,这确实是个趋势,应该会到来。到时看源码就不会那么眼花缭乱了。@xufei 徐叔怎么看

我们引入一个框架,对整个系统来说,最大的影响是会产生一些约定。有时候我们需要这些约定来帮我们规范代码,但在现在这种形势下,会尽可能希望框架本身不要产生约定,由我们自己,按照ES自身的一些机制来形成代码规范。

*按这个来说, React 才是最符合博主需求的. *

ng / ko 这种带 MVVM 的框架, 都有自己的 template 语法, 修改了原有 js 的写法, (比如一个简单的循环, 需要用 ng-repeat) 而且 ng 还附带一大堆约定, 比如 $resource, $inject, $provider 啥的.

反观 React, JSX 只是简化语法一个写法, 完全可以用直接 js 写, 并且没有黑魔法 (比如一个简单的循环, 直接用内置的 Array.map 即可), 你可以随意选择使用 es5, es6, 甚至是 TypeScript 来编写.

已经采用
底层如上所述,上层使用Angular 1.4或者Vue之类的成熟框架,同时,使用ES6开发

commented

谢谢!

選擇前端框架真是一門學問

es6普及之快真的难以想象 ,刚学完es5就踏上了es6之路……

INTERESTING!!! 👍 ( human readable data-tree

现在看来,双向数据绑定还是没干过单项数据流,从props到minix到hook,也是大势所趋。现在如果构建一个大型的项目scss/less配css-module是比较完善的解决方式,自定义化的webpack也好,vue-cli这种在某方面固定、同时又放开配置的打包工具都是很不错的选择,根据个人接受度了。编写内容还是jsx香,ts的能力也一直在加强,vue3.0官宣支持ts彻底让主流框架全部倒向了ts。楼主有空的话谈谈现在的前端未来嘛?