Server-generated JavaScript Responses https://37signals.com/svn/posts/3697-server-generated-javascript-responses
这绝对是最强音的逆袭。你如果没点耐性,就直接接受吧。
Revised: 经过吕哥提醒,是 SJR,不是 RJS。但我怎么觉得这是一回事啊。。。。
我也非常赞同应该 RJS
Benefit #2: Less computational power needed on the client
说实话看到这条我觉得 DHH 是在逆历史潮流而动
最近在用 Angular ,感觉 Rails 里面大部分组件已经不需要了(View/Helper 等),只需要提供 JSON 就行 当然这会出现 DHH 第一点里说的那个问题,JS & JSON 加载未完成时首页出不来,但是为此做一个特殊的优化并不是难事
这跟气度和潮流无关,Rails 本质上还是 server side 的解决方案,走的还是服务端处理大部分逻辑的路子。这种情况下某些 ajax 请求返回 html + js partial 确实比直接返回 json 更好。不需要一套 template 分别用 erb 和 js 实现两份(尤其是 template 中含有数据格式化的,更蛋疼)。可以最大程度的重用代码,易于维护,也方便打 cache。
照 SJR 的思路发展,turbolink 的出现就是一种必然了。对不是非常重客户端交互的应用来说,Rails 提供的这种解决方案还是挺好的。提供一定的动态交互的同时最大限度的利用 Rails 在 view 那一层优势。
但如果用前端 MVC 的话,因为 template 的逻辑都在客户端,这时候 json 就是最好的选择。因为 server 已经完全没有必要关心数据怎么展示的问题了。
总之,一件事情,要么前端做,要么后端做。两边都做 50% 的话非常夹生…… 如果两边都做 100% 那就蛋疼了。
#14 楼 @larryzhao 对于 Web 来说,这么多年的发展难道不是将计算能力从 Server 推向 Client ? 否则 Chrome 和 V8 为什么要出现?胖客户端的趋势是很明显的啊
我觉得这玩意儿还是不能一概而论, 看你做的是啥和当时的情况.
对于一个 Single Page Application, 如果要我不要用 Backbone/Spine/Ember, 而是用 SRJ, 我觉得会做起来困难很多。相应的比如简书的阅读文章的页面, 我不可能用前端 MVC 来实现, 人家到了这个页面就是想要立刻看到内容的, 不现实先载入整个 MVC 框架, 完成初始化, 再等待 json 回来做内容填充, 你就算相应时间再快, 用户刷页面还是会看到 html 填充的过程, 所以这部分肯定是一个正常的 page render, 相应的所涉及的一些 Ajax, 就很自然的会用到 SRJ.
虽然我用很多 SRJ, 我感情上还是略微倾向于 json + 前端 render, 也可能是因为一些'洁癖'的关系吧, 当然 SRJ 可以复用 partial 的好处是极大的, 但用下来一直以来的感觉我觉得还是有一些不好的地方的:
文中举例的 create.js.erb
这种写法,我以前也试过,但总觉得不舒服。原因是,本来我的 callback 全部写在 CoffeeScript 中,现在要将 call back 分离写在一个 erb/js 文件中。代码就不集中在一齐了,请问你感觉是如何的?
后面的两种解决方法,感觉都不大好,但能用。
返回 JSON,然后用 JS 的 Template render 就是 DHH 反对的方案 (这个上面已有讨论)
直接在后端 render partial template,然后 JS 获取后在依业务改变页面数据。
还用,如果用 create.js.erb
,部分 JS 就不能 precompile 了,也不能用 CoffeeScript。总觉得 JS, erb 混写很恶心,我的 vim 都认不出来。
#22 楼 @larryzhao 我大概明白你的意思,但是单就 Web 或者叫 B/S 来看,计算能力确实是从 Server 慢慢流向 Client,或者说,B/S 越来越像 C/S
根据不同的团队,不同的需求,可以选择不同的解决方案 大公司大厂,各种人才齐备,api service 一个团队,网站后端一个团队,前端一个团队,完全可以用复杂的方案 对于小团队小项目,人少又要快速开发,可能这种方案比较方便快速。
Basecamp 的绝大多数 Ajax 操作是由服务器端生成并返回 JavaScript (SJR),它的工作流程如下:
这个简单的模式有许多重要的好处。
可以在第一次渲染 model 和后续更新中重复使用同一模板。例如在 Rails 项目中, 你可以在这两种情况下都使用 messages/message 这个局部模板。
如果你只返回 JSON, 为了显示 messages 你需要执行两次动作 (一次是服务器端的 first-response, 一次是客户端的 subsequent-updates) —— 除非你正在做一个单页面的 JavaScript 应用程序,甚至第一次响应也是 JSON/client-side 生成并完成的。
使用第 2 种方法可能非常缓慢, 因为你不能显示任何东西, 直到你的整个 JavaScript 库都加载完毕, 然后客户端生成的模板。(这是 Twitter 最初使用的方法,后来抛弃了)。但至少在某些情况这是一个合理的选择, 且不需要重复模板。
嵌入 HTML 模板的 JavaScript 的响应时间可能略大于纯 JSON 的响应时间 (当你使用 gzip 压缩时, 这种差距通常可以忽略不计)。它不需要客户端的运算来更新。
考虑到这些模板的复杂性和客户端的计算能力, 且服务器端生成模板通常可以缓存并在多用户间共享 (参考 Russian Doll caching)。这意味着,从 end-to-end 的角度发送 JavaScript+HTML 有可能比 JSON 配合客户端生成模板更快。
SJR 是一个很容易遵循的执行流程。请求机制是一个标准化的 helper 逻辑, 例如 form_for @post, remote: true
。 没有必要为每一个请求定义 action 逻辑。控制器执行逻辑,然后渲染部分视图生成响应,以完全相同的方式呈现一个完整的视图, 只是模板变成 JavaScript, 而不是 HTML。
完整的例子
0) First-use of the message template.
<h1>All messages:</h1>
<%# renders messages/_message.html.erb %>
<%= render @messages %>
1) Form submitting via Ajax.
<% form_for @project.messages.new, remote: true do |form| %>
...
<%= form.submit "Send message" %>
<% end %>
2) Server creates the model object.
class MessagesController < ActionController::Base
def create
@message = @project.messages.create!(message_params)
respond_to do |format|
format.html { redirect_to @message } # no js fallback
format.js # just renders messages/create.js.erb
end
end
end
3) Server generates a JavaScript response with the HTML embedded.
<%# renders messages/_message.html.erb %>
$('#messages').prepend('<%=j render @message %>');
$('#<%= dom_id @message %>').highlight();
最后一步反应是由 form_for 生成的 XMLHttpRequest-powered 自动处理, 视图更新新消息, 新消息通过 JS / CSS 动画实现高亮效果。
当我们第一次开始使用 SJR 时, 我们使用一种叫 RJS 的编译器, 它允许你用 Ruby 编写模板,并转换成 JavaScript。 RJS 是一个屌丝版本的 CoffeeScript 你也可以了解一下 Opalr, RJS 让许多人离开了 SJR 模式。
现在我们不再使用 RJS, 但是我们仍然坚定的推荐 SJR。
这并不意味着由服务器端生成 JSON 然后由客户端生成 views 的情况不再需要。如下场景我们会用到这个模式:UI 保真度非常高并且大量的视图状态需要维护。例如日历。当你遇到这个情况时,我们建议使用 Eco template system(think ERB for CoffeeScript)
如果你的 web application 全都是 high-fidelity UI, 它完全合适这种路线。当然自己选择的路跪着也要走完!爷们不哭,站起来撸。但是如果你的程序更像 Basecamp 或者 Github 或者绝大多数基于文档的 web 站点, 那么你真的应该使用 SJR。
最后,DHH 大大认为 Russian Doll-caching, Turbolinks 和 SJR。绝对是一个牛 X 的技术组合,能够让你高大强的为 web 应用写出漂亮的代码。颤抖吧凡人。
简单翻译了一下,同学们有发现翻错漏的麻烦 @ 我一下
create.js.erb 仅仅会是很简单的东西
# 找到对应 DOM xxx
# Render partial 准备好需要渲染的内容
# xxx.append(content) 或 xxx.replace(content)
比如 Ruby China 的 app/views/replies/create.js.erb
:
<% if @reply.errors.blank? %>
<% @page = 1 %>
$("#replies .items").append('<%= j(render("reply", :reply => @reply, :reply_counter => @topic.replies_count - 1, :display_edit => true)) %>')
.find(".total b").text('<%= @topic.replies_count %>');
$("#replies .reply a.edit:last").css("display","inline-block");
Topics.replyCallback(1,'<%= j(@msg) %>');
<% else %>
Topics.replyCallback(0,'<%= j(@msg) %>');
<% end %>
以前我也向往过 client side,看过 angular 和 ember 的文档,但是真正动手的时候就怂了,将渲染移到 client side,意味着原本每个小查询都要写成 API,例如分类列表。而要减少远程调用,加快第一页速度,就要写 init 逻辑,把要用的数据塞到页面 js。最后为了 seo,还要单独写一份简化版的服务端渲染(discourse.org)。一般的 CRUD 用 client side 方案真是麻烦很多。
客户端的计算能力提升,可以用去需要客户端做的事上,例如拍照、定位、动态图表、编辑器等等,而要给移动应用写 API,可以单独写 API 接口,不跟网站混在一起。
所以,我赞成 DHH 的方案。
其实嘛,我觉得,网站用 dhh 这种,web app 用 js mvc 就好,但是问题就是,一般都是先有个站,最后才衍生出 app,所以就蛋疼的最后加个 api 目录搞 app.... 哎。。。
目前越来越多的项目都是 restful 后台,然后 web 端弱化成一个普通的 client 端. 这样做规模越大越得力, 数据和逻辑都是后台, 前台负责本地化展现, api 级别是 restful 的,利于扩展.
对于 native app,这么做貌似阻力不大,对于 web 的话, 能熟练使用 js 并做测试的人实在太少了,所以接受的难度肯定要大不少. 而且 SEO 之类的问题也比较头疼.
我个人是赞同将计算力向客户端迁移的, 当然前提是应用要足够成功, 越成功的应用, 越多计算放在客户端,则需要的服务器维护费用会越少, 当用户量超过 10w 以上的时候, 差距应该会越来越明显. 善用 browser cache, server side cache 等等也是减少 cpu,io 量的一个办法,但是都不如仅仅拿 JSON 那么节省 bandwidth 和计算力.
@xds2000 RJS 是很早以前(Rails 3 以前)Rails 推出的用 Ruby 写 JavaScript 的方法,完全是 Ruby 代码
举个栗子:
# some_view.rjs
page['time_updated'].replace_html Time.now
page.visual_effect :highlight, 'the_list'
这段代码是从 这篇 ITEye blog 上摘抄的,看看日期你就知道有多古老了。
Rails 会把这一段代码渲染成 JavaScript,当时还是使用的 Prototype。年代太久远记不清楚了,大概是这个样子:
$('time_update').replace('some time');
$('the_list').visualEffect('highlight');
这种技术非常依赖前端的 JavaScript 库,而且也不够直观。只不过算是一种 “用 Ruby 写任何东西” 的理念的衍生物。
后来在 Yehuda 把 RJS 移除了(貌似是 Rails 3 时代?),换成了 js.erb 的方式,直接在 view 里面写 JavaScript。这就是 DHH 文章里面说的 SJR(Server generated JavaScript Responses)。
#58 楼 @darkbaby123
原来是 Yehuda 把 RJS 移除的,估计他也是觉得现在的 SJR 还是太蹩脚,然后直接投身到 Ember 的开发了
how about this https://github.com/rails-api/rails-api
如果是一个本身就是前端出身的人肯定给出的是完全不一样的看法。
sjr 在我看来无法前后端分工开发,两种语言混写出错率高。
普通网站带上少量 ajax 可以用 sjr,真正的 web app 还是得看 angular,ember,knockout 之类的
@small_fish__ 反正我现在是没用的,就是 Rails + Ember 。因为我还是用的 Devise 的 session 验证。虽然有办法去跟 Rails API 结合,但我还需要 sprocket ,懒得折腾了。 其实 Rails API 就是个减去了不必要 middleware 的 Rails,其他地方没什么区别。
@small_fish__ BTW 作为 Rails API 依赖的一部分,active_model_serializers 貌似也是有 Yehuda 参与的。它默认生成的 json 格式和 Ember Data 是吻合的,不过现在有点不一样了。
好处 #1: 重用模板且不牺牲性能 Angular 可以重用 Rails 的 局部模板,任何后缀都可以
不能显示任何东西, 直到你的整个 JavaScript 库都加载完毕 Angular 可以先显示 html 模板,局部用 loading,让用户先看到页面。
你需要执行两次动作 (一次是服务器端的 first-response, 一次是客户端的 subsequent-updates) 如果是简单页面,一次执行即可,ng-init 可以接受初始化数据 如果是复杂的,有重用模板的页面,可以用传统 Rails 的方式重用模板。
服务器端生成模板通常可以缓存并在多用户间共享 DHH 说的是页面片断缓存,而 模板缓存 + 数据缓存 一样解决问题,而且不混乱。
从 end-to-end 的角度发送 JavaScript+HTML 有可能比 JSON 配合客户端生成模板更快。 客户端生成模块?有时候根本不需要重新生成模板,DHH 真的应该看看 ng-repeat 如果是调用新模板, 两次 Ajax 请求 + 客户端计算 VS 一次 Ajax 请求 + 服务器端计算 前者服务器性能要求更低,后者少一次请求数,但是这是在用户交互时,相差约等于 0
浅显易懂的执行流程 老式 Form action 是否满足 多功能按钮执行的需求?