Rails 关于 Rails 里的 render 方法

lingceng · 2018年03月23日 · 最后由 ruby_sky 回复于 2018年03月24日 · 6102 次阅读

官方文档里有两个地方提到 render 的用法 在 Controller 里,http://guides.rubyonrails.org/layouts_and_rendering.html#using-render,例如

render :edit
render plain: "OK"
render json: @product

在 View 里,http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials,例如

<%= render "shared/menu" %>
<%= render partial: "form", locals: {zone: @zone} %>

看到这里,render 的日常使用就差不多了。 但我们还想找找 render 更多细致的用法,于是去查 API,找到很多 render 的定义。 有点混乱,也没有一个说明了render json: @product这样的用法。

于是有了些疑问:

  1. Controller 里的 render 在哪儿定义的?具体有哪些参数?
  2. render json: @product这样的用法在哪儿实现的?
  3. View 的 render 在哪儿定义的?

带着这样疑问,通过代码调试,结合源码,找到了些答案。

Controller 里的 render 定义应该在 AbstractController::Rendering#render。 这里"定义"有点不准确,因为往往有多次重载,姑且理解为"主要方法"的意思。

这里的 render 其实是通过 render_to_body 这个方式实现的,render_to_body 是个 API 方法,主要实现在 ActionController::Renderers。 在 ActionController::Renderers 里有 add 的类方法,可以添加 Mine 类型的支持,文档里的例子是定制"render csv: @csvable"。 ActionController::Renders 源码里就有 json, js, xml 的 Mine 类型的支持的实现。这里摘抄一段 js 的:

add :js do |js, options|
  self.content_type ||= Mime::JS
  js.respond_to?(:to_js) ? js.to_js(options) : js
end

这样就回答问题 2:"render json: @product" 是在 ActionController::Renderers 里实现的。

但 ActionController::Renderers 里并没有render "index"这样的用法实现。 跟踪发现,ActionCtroller::Base included ActionView::Layouts which included ActionView::Rendering。 ActionView::Rendering#render_to_body uses ActionView::Renderer#render。 ActionView::Renderer#render 的文档里有说明,它会根据条件判断使用 TemplateRender 或者 PartialRenderer 渲染。 PartialRenderer 类有比较详细的文档,TemplateRender 的类没什么文档,它的说明主要在下面提到的 ActionView::Helpers::RenderingHelper#render。

这里是很奇妙的一点,Controller 的模版渲染实现其实是通过 View 里面的 Renderer 实现的。 不过想来也可以理解,View 层本来就负责对模版文件做渲染。 那么我们可以推测,在 controller 里我们其实也可以这样使用render partial: 'shared/menu'。我测试了下,的确可以。

至于问题 3,相对简单些:在 ActionView::Helpers::RenderingHelper#render 定义的。 这个 Helper 方法还顺便提供了两个很有意思的功能。

一是 partial 可以当作 layout 来使用,

<%= render layout: 'shared/menu' do %>
  <p>Content</p>
<% end %>

二是在 View 层可以简化 partial 的用法

<%= render partial: "form", locals: {zone: @zone} %>

<%# 可简化为 %>
<%= render "form", zone: @zone %>

那么简单总结下:

  • 查 View 的 render 的用法,主要查 ActionView::Helpers::RenderingHelper#render 和 PartialRenderer 的文档。
  • 查 Controller 的 render 的用法,比 View 里的多了 ActionController::Renderers 的功能。
lingceng 关于 Rails 官方文档 中提及了此贴 03月23日 08:39

我一般是在:

  • 通过 Dash 来找文档, 顺便就能找到源码.
  • show-source 或者是 show-doc
  • 瞎猜, 打个 debugger.
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册