React 11/1在上海会有一个workshop,看到 Ember.js 节点最近没啥人气,本着凑热闹的精神,准备去看一下 React,预习了点暴走的 React 介绍视频,还有霹雳渔的,感觉 React 上手好容易的样子(天哪,这贴其实我是准备黑 React 的啊。。),心有所动,于是就又放狗搜了下这两对冤家的近况。
最终发现Alex Matchneer的React vs Ember 的比较真心不错,终于又将 Ember 的道心稳住了。。
其实同时懂 React 和 Ember 的人很少,能够客观看的就更少了,还能顺便黑 Rails的,就。。
React 的问题:
Ember 的问题:
我觉得 ember 现在最大的问题就是还不是 routable component,所以现在写起 ember 来很是不爽。不知道 ember 什么时候才能把这个 feature 加上。我现在快从 ember 粉转黑了。最近用了一下 vue.js,发现非常好用啊,写起来十分爽快,推荐 vue.js。
@nong 没用过这货,我觉得没有必要纠结选什么框架吧,选合适的就好,这也要看你项目的需求,还有你使用起来到底舒不舒服。反正得记住,是你在用框架,不是框架来玩你,如果你在用一个框架的时候有这种感觉了,就趁早换一个吧,前端框架何其多,何必为难自己呢?
再过一段时间,我预期大概两三个星期后能得到一段空闲的时间,然后我要谈谈复杂组件的设计(因为我最近做了一个很复杂的组件)和 DDAU 在其中的实践,接着还想谈谈 Data Store。
React 的确很好,这一点无需否认;然而如果某项技术你只能听到一片叫好却听不到有人说它的缺点,那就一定有什么问题了;而且我看到很多盲目跟风的人甚至都不知道 React 只是一个 view engine 而不是 frameworks,这就有点让人无语了。这说明支持和称赞 React 的人群里有很大的水分。
而 Ember 自进入视野开始就不断的备受质疑,从来没有被交口称赞的时候。可正是这种“特质”一直吸引着我,让我觉得它很真实,即使在我的工作环境不允许使用它的过去几年,我也没有放弃对它的兴趣。现在我终于可以用它做点真实的事情了,于是我对它的认识越来越深。就我所看到的(国内的)对 Ember 的绝大多数评价,我认为几乎都在瞎编乱造,要么就是在炒一两年前的冷饭。有些属于 Ember 的缺点早已不复存在却被反复用来吐槽(比如说渲染性能,Glimmer 实际表现比 React 还好那么一丢丢,这有多少人知道?);而那些 Ember 的闪光点却甚少有人知道,比如说 React 带起的 DDAU,大家可知道 DDAU 得以实现或存在最基本的前提是什么吗?是 Data Store:the only source of truth。而目前最好的 Data Store 实现之一就是 Ember Data,它的出现远比 DDAU 这个概念还要来得早,如果你真的能玩儿转 Ember Data,然后再回头看看 Flux/Redux 等解决方案所“提供”的 Data Store,就知道简直是小儿科。(提供打引号,是因为像 Redux 那样的轻量级实现,不会去做一个“超级数据仓库”的,他们只会强调所谓 Data Store 就是一个 Single and maybe huge Plain JavaScript Object。
我不是说 React/Flux/Redux 它们不好,但是它们主攻的方向和目标是与 Ember 这样的框架很不同的,根本就不适合拿来相互比较。我几乎尝试过所有新鲜出炉的框架,那些标榜轻量级/简单易用的框架就的确只适合做轻量级的应用程序,反之亦然——你当然可以拿它们来做复杂的应用,但前提是你有那个能力去拼装组合;比方说 Ember Data 也是可以配合 Redux 来用的,这样由 Redux 控制单向数据流,Ember Data 提供一个高级数据仓库也是很不错的事情(但是 Ember 没必要这样做,因为 Redux 能做到的 Ember 只会做到更多,只需要使用者改变一些思路和理念即可)
我也不是说 Ember 就已经很好了,实际上它现存的问题也不少,不过在你发表评论之前是否应该先对其有个足够的了解呢?比如说 @aidewoode 提到的“最大的问题是还不是 routable component”,的确,routable component 是下一个 big thing for ember,但就目前而言还真算不上最大的问题。因为目前(假设你一直在用最新的 stable 版本)controller 的唯二作用就是:1)接收 model hook 返回的 model 并暴露给路由入口的 template 使用;2)query params 的声明和部分操作;除此二者之外(特别是第一点还是隐式完成的,不需要你写什么)剩下的事情都可以交给 routes 或 normal components 各负其责。我最近的项目里,controller 的代码少得可怜,而 template 唯一的功能就是做各种 components 的容器,这感觉已经和 react 没有什么差别了。
更甚至,如果你现在就把散落在路由入口模版里的各种 components 用一个大的 component 包起来,那你几乎可以说这个就是 routable component 了,底层的差异不必太在意,就应用程序的结构来说,这就是一两个月后的 ember app,所以这真的不算什么最大的问题,只是看你如何理解这种变化的态势了。
什么是真正的问题?很多。有人提到了文档的事情,和最近半年 Ember 的演变相比,文档的滞后的确很严重。幸运的是 Core Team 已经意识到了,并且对未来一到两个小版本的发布目标进行了调整:完善文档是第一要务,我看到了一些对于官网和 guides/api 的新设计,用不了多久就要改头换面了。
还有就是解决复杂组件结构相互通信,共享状态的事情。DDAU 只是一个纲领性的方向,但它并没有解决很多细节的实现问题,何时/怎样向下或者向上?何时/怎样适合单向或者双向?如果你确实碰到需要数据向上/动作向下的场景却依然想要坚持 DDAU 的原则你该如何去做?框架又提供了哪些机制帮助你这样去做?我用 React 做过一些试验,其中一些场景它能做的也并不很令人满意,然而对于 Ember 这些也是一样的问题。
Contextual Components / Closure Actions / Kebab Actions ... 这些才是 Ember 和它的社区正在面对和交付解决方案的问题,这些东西(或许)才是回答上述问题的确切的具体的答案。在这里就不得不提一下 Ember 社区的 RFCs,正是这样的群策性政策让真正的 Ember 用户得以保持信心。RFC 是一个独立的 repo,它所做的就是让用户提交自己在开发过程中遇到的各种痛点(甚至不需要是 Ember 的用户),然后 Ember 的核心团队以及其他经验丰富的架构师会针对这些问题提出 proposal,阐述问题的根源/解决方案的思路/在 Ember 中的具体实现/以及可能存在的 drawbacks 等等。这些 RFCs 会完全公开它的演进过程,每一个人可以参与其中提出自己的观点和想法,大家一起来完善和修正它们,直到它成为 Ember 正式的一部分。
这是我在其他前端框架社区里没有看到的东西,这也是我会对 Ember 保持信心的根源,从实际问题出发,到提供确切解决方案为止,不回避问题,坦承过去的失误,积极探索未来的可能性,不拒绝来自其他社区的 good ideas,把形而上的理念具体化……我认为这是所有终端开发者都希望看到的框架。
每次涉及到这类话题我就会打好多字,这种现象其实我自己也是够了……然而如果每一个人在发表见解之前能对目标有一定的了解和分析,我也就无需写这些了。如果你想要赞美谁,先想想它还有什么缺陷,以免被反对者立靶子;如果你想要批评谁,先想想它有什么优点是别人做不到/没做到的,以免被支持者点名打脸。如果人人都能这样去想和做,整个社区里会少很多“xxx 完爆所有 n 条街”,或者“xxx 弱爆了”这样了无聊论点——不过追根究底,硬要去比较两者的行为才是最愚蠢的,决定你选择的不是他人的主观评价,而是你所选择的东西正在做什么以及将要去做什么。
去了解,不要去比较。一定要比较的话,也是先了解之后再比较。
看了这个 ppt. 帖子里有一点感觉很好,作者很认真的探究了 react 的优势,而且也会考虑将真正吸引人的地方引入到 ember, https://docs.google.com/presentation/d/1afMLTCpRxhJpurQ97VBHCZkLbR1TEsRnd3yyxuSQ5YY/edit#slide=id.g380053cce_1495, 感觉这种态度很让人放心,更加有信心。
已倾向于 ember.js,
我可以补充一份打鸡血的讨论吗?虽然时间比较早 (2014.2), 但里面的讨论着实很精彩呀!
[Discussion] The future of Ghost's admin UI
然后是一段时间的实现 [Ember.js] admin UI rewrite
成功的案例,挖一挖总是有的。
既然大家都提到了这个 ppt,我就先说几件和这个 ppt 有关的事情。这份 ppt 和最初的演讲已经过去三个月了吧,在这三个月里 Ember 的确践行了吸纳同行菁华的承诺,有一些改变确确实实的发生或正在发生着,当然也有一些原本就不错的地方得以继续保持并进一步改善:
一说 React,必提单向数据流。以至于给人一种错觉:Angular/Ember 等前辈就是只会双向绑定的,是 Evil 的。
这是一个误会。Angular 我不去说它太多,本身它就没有 Data Store 这层设计,Data Model 的主要任务还是在 http requests 这一块,一旦进入了 ViewModel 层,由于每一个 View 都和一个 $scope
耦合着,所以很难轻松的实现单向数据流——除非你不介意把数据统统都丢在 $rootScope
,然而那样的话性能就会很糟糕了。它这种设计就注定了把数据分散至各个独立的 ViewModel,然后分别去双向绑定是最自然的方式,硬要强求单向数据流也是难为人家。
而 Ember 则是另外一回事,Ember Data 提供了天然的优质 Data Store,借由 Router 提供数据向下逐层流动的入口,其整体架构其实一开始就很单向的,这一点我在一开始接触 Ember 的时候几乎完全没有意识到。有熟悉 Angular 的可能会问,像 ui.router 那样善用 resolve
钩子向 ViewModel 提供数据不也是一样的吗——这可是你自己举的例子哦(不久前我刚在这里讲过的例子)!嘿嘿,不一样哦:
// ui.router's resolve:
resolve: {
data(SomeModel) {
return SomeModel.someRequest(withSomeParams) // a promise will be return
}
}
// Ember Route's model hook:
model(params) {
return this.store.query('ModelName', params) // a promise will be return
}
上面的两个例子看起来像极了对吗?然而差别就在于:Angular 的 SomeModel
们是各自独立的,没有一个 single source of truth,如果它返回的数据在应用程序的任意层级被修改然后去 update/delete 等等都是很“正常”的事情——我指的是 Angular 开发者的编写习惯会认为这样做很正常——作为旁观者你永远也无法预知数据会在哪里/何时发生改变,甚至是否有改变?除非,你替 Angular 加一个数据层,帮它集中管理各种 Data Models 以及它们的状态;而 Ember 好就好在它有这么个 this.store
,也就是 Ember Data 了。对于路由来说,它根本不知道有没有/有哪些 Data Models,数据的进入是全权委托给 Ember Data 的,同样数据的状态和各种请求也是全权交给 Ember Data 去负责的。当进入到下层的 ViewModel 之后(以后可以认为没有 VM 了,全是 Components),没有任何“人”可以随意改变这些数据,也不应该随意发起 update/delete 等请求,只需要借由 actions 来通知 Ember Data 代理这些事宜即可。
所以哪怕是看起来极像的代码,你仔细一琢磨却发现其中内涵大有不同。Angular 那种的充满了不可预知性,数据层操作和视图层操作很容易耦合在一起(因为每个 Model 自己管理自己的行为,更改其一并不能影响其他);Ember 这种则是有一个很明显的分离,即使将来有一天你不想用 Ember Data 了(换用 POJOs?)你也无需担心大量的代码改动,顶多去实现一个 this.store
这样的接口就好。
这一切只是要说明一件事情:从一个应用的整体来看,其实单向数据流也是 Ember 的天然特质;只是在 React 明确这一概念之前,大家都没有意识到罢了。
不过在那个 ppt 里也提到,尽管很多人认为“双向绑定是罪恶之源”,但也要看到它是有自己的用武之地的。对于 webapp 来说,任何地方坚持单向数据流都好,唯独表单这个东西还真是需要双向绑定。
以下是一个类似 TODO App 在最新版的 React 里的简单实现(不借助双向绑定):https://gist.github.com/nightire/1f29fe5747eca3f07fe9
然后是一个同样的应用,换成了默认双向绑定的 Ember 简单实现(没写成 Components):https://gist.github.com/nightire/7b1be58719446614b907,并且可以在线预览(也是 Ember 社区提供的工具):https://ember-twiddle.com/7b1be58719446614b907
你可以轻易的比较出哪一个写起来更简单一些,即便是把后者再组件化也增加不了几行代码的。非常多的应用,特别是企业内部办公/管理等应用都高度的依赖表单操作,在那些应用场景下双向绑定并不是什么罪恶,只要你理解它并且善加使用。
Ember 社区也同样意识到,滥用双向绑定会造成很多不良后果,所以未来的 Ember 将会默认使用单向数据绑定(我指的是在模版上绑定数据的写法默认是单向的)。当开发者确实知道自己在做什么的时候,他可以选择开启双向绑定。这是一个相应的例子: https://gist.github.com/nightire/e8febb30359c4c2da2fa 和在线预览:https://ember-twiddle.com/e8febb30359c4c2da2fa
后面的例子演示了在 Ember 中封装组件之后的样子,依然很简洁。同时演示了:1)显式声明的 mutable property binding(双向绑定);2)closure action(闭包动作)
第一点是 Ember 的 Observable Object 天然支持的,只是为了避免滥用双向绑定所以在模板语法上“故意”给开发者设了一道“坎”;第二点则应该是向 React 取来的经了,尽管 React 并没有 closure action 这个名词,但是类似于这样的声明:
<ChannelForm addChannel={ this.addChannel.bind(this) } />
和这样的调用:
this.props.addChannel(this.state.channelName)
几乎就是 closure action 的模仿对象,Ember 借助了 HTMLBars helper,更进一步的省去了 .bind(this)
这个尾巴。再下一步,当 angle-brackets components 正式登场后,仅从写法而言两者之间还有多大区别?更不要说 Ember 是模版与脚本分离写的,React 解释 JSX 的时候反复强调是为了对 designer 友好,可终究不如分离的模版来的痛快不是?
当然 React 也是可以双向绑定的,不过你得启用它的 addon 包,本质上它是一个 mixin,但在 Ember 里则是底层的 Object System 所提供的 Observable 特性——一旦浏览器支持了原生的 Object.observe
,Ember 在其中扮演的“二传手”的角色就可以彻底跑龙套去了(同时会减少代码实现的依赖,变得更轻量)。一个做加法,一个做减法,殊途同归,只是到那时 React 是否会大大方方的接纳双向绑定特性呢?
说到 Observable,Ember 里现在必要的 get()ter/set()ter
本质上就是为了这个,这玩意儿我也烦,只是我知道它们会“死去”的,时间问题。ppt 里也提到了,开发者们似乎更喜欢 React 里显式的 setState()
,其实这就是为了通知受状态变化影响的视图去更新自己(无论是 Virtual DOM 还是 Glimmer 都是如此),原生环境的现状就是如此,大家其实都是半斤八两。Angular 倒是看起来干净了,可一代目里 $scope
的连带问题被吐槽也不见得就少了,忍忍,再忍忍,至少这几年的进步还是让人看得到希望的。Yehuda Katz 是 TC39 的常委之一,这也是让大家信任 Ember 的未来的重要原因。
以上,仅仅是最近一段时间 Ember 的许多进步之中的一小部分而已,更多的东西我自己也还在消化和实践之中。总之组件化的大势对于 UI 编程来说是不可避免的,大家其实都在往这个方向努力着。然而大规模的应用程序开发又不仅仅只是 UI 编程而已,所以不必因为一时一处的井喷式进步就觉得“一药治百病”了,要成为更好的开发者,我们就必须看得更远、更广。
Object.observe 提案被取消了 https://mail.mozilla.org/pipermail/es-discuss/2015-November/044684.html
不能拿 Ember Data 和 Redux 比吧,感觉现在用 Redux 写写轻量的应用还是不错的。谁来谈谈 GraphQL 和 Relay 啊?
ember 之前丑陋的模版是实作的时候的大坑,导致之前一直对 ember 不感冒。
react 最近两个月跟的比较紧,可以大致说一下我所知道的:
的确大部分人的理解是 react 就是 View engine,不过从使用的角度来说
子状态的共享在 redux 里面可以采用监听 action 来存储数据到自己的 store 里面,这个不是非常别扭。但是的确如果整个页面就是一个大的 react component 会方便很多。
在 dataStore 方面 redux 的实现方式并不能和 ember 相比。store 在 redux 里面是非常非常简单的,也没有和后端通信的接口,需要自己去拼接。但是很显然,redux 并不是 react 官方配合的数据层解决方案,react 官方吸收了 reducer 到 react component 里面,作为 state 到 render 的中间数据处理层。而 Relay 则是 facebook 官方配合 GraphQL 的前端实现,整体上就是一个前端的超级 DB。
手工传递 props 到 sub sub sub component 下面是比较累人的,react 目前提供了context的方式传递,但是 context 本身不作为 component 是否 re-render 的依据,需要的话只能自己写。
react 的确没有自己的 RFC,不过标准性的东西有一些: react-future
模板和 component 之间是区分还是合并,还是取决于团队分工的。如果设计师不懂 js,需要前端插一道手,那 jsx 和模版就没有太多区别。而使用 js 原生语法,并且能直接采用 js 自己定义的内容,那么 jsx 就比模版更好一点。
但是如果需要设计师去动,且设计师懂得 html 不懂 js,那当然还是模板好一点。
咨询下,使用 ember 为客户端 app 的话,ruby 系的话,用来做 api,哪个比较合适呢?我目前只用过 rails + grape, 就是不管 view
另外实际上 rails 使用量依旧很大,考虑在 rails 的 view 上增加组件支持的话,也就是尽量去改善 rails 的 view 的写法,你觉着 polymer, react, ember 等这些哪个更好呢?
View Engine 的说法是 React 自己强调的,在历届 React Conf 里都反复的被强调。你提到的那几点:
所以没必要觉得怪怪的吧,当然内行相互谈话说起 React 一般都是隐式的指代 React 及其生态圈相关,所以我们当然知道 data store 该怎么搞,知道怎么把 flux/redux/relay 这一系列串到一起,只不过这么一来其实和 Angular/Ember 等等也是半斤八两的,并不总是人们以为的那么轻量。
诚然,你可以说这样更灵活,选择更多,每一个独立的组件其质量都更有保障等等等等,然而无非就是另外一种选择罢了。就好像 JSX 到底好不好,最终只不过就是一种选择而已。
综上,尽管 Ember 没有 React/Angular 等等那么引人注目,但我这些年大家都用下来的感觉是,Ember 没有那么那么不好,其他的也没有那么那么好,现在对待它们的心态很平和,不管用哪个都能用到和用对它们的优势和特色,这才是最重要的吧。
#24 楼 @iwege "你可以说 xxx”……这是一种行文手法而已,类似于英文里的 "You can say...",这里的“你”或者“You“都不是特指你,而是一种泛指。所以我说的并不是你……
关于 model,部分取代也不行,不是说你把数据接收到了然后可以在每一个组件的 local 使用,那么这个数据就是 model 或者部分是 model 了。仔细对比一下 Ember 接受数据也是靠 components 的 attrs,也是通过 yield 来做像 React 提供的 context 这样的事情,但这些都和 model 无关。Ember data 才是和 model 有关的部分,而这部分在 React 里是没有的,需要其他组件加入来支持,所以说 React 是 View Engine 或者 Components Layer 一点没错。