将partial转换为method / block以获得速度

时间:2014-06-16 07:25:54

标签: ruby-on-rails ruby performance ruby-on-rails-4 partial

我有一个渲染部分

的循环
1000.times do |i|
  render partial: 'test', locals: {i: i}
end

这真的很慢,foreach渲染调用最多0.1毫秒,即使部分只打印出i

my_partial = render_to_method(partial: 'test')
1000.times do |i|
  my_partial(locals: {i: i})
end

现在这应该以更快的方式做同样的事情吧?但我不知道该怎么做。

更新:

我试过这样做:

Haml::Engine.new(File.read(File.expand_path File.dirname(FILE)+"/../table/#{column.macro}/_#{column.display_type}.haml"))
.‌​render(OpenStruct.new({column_value: column_value, object: object})) 

两个主要缺点:

  • 视图的路径不会像使用渲染时那样观察回退(parital:' partial'将在当前视图目录中寻找parital,在某些宝石中以及在视图/应用程序。
  • Rails视图帮助程序不再可用

更新2:

许多答案试图通过使用其他技术来解决问题。我的实际应用这种技术无法应用。 https://github.com/antpaw/bhf/blob/master/app/views/bhf/pages/_platform.haml#L54主要是因为这一行是一个动态字符串,有时会链接到甚至不存在于gem中并且在main_app中定义的部分。我想知道为什么做这么基本的事情是如此困难:1。抓住观点。 2.渲染它。 (3.再渲染一次。)

更新3:

@Flash Gordon建议这个https://gist.github.com/antpaw/d6670c23d6f08e35812e#file-gistfile1-haml-L54

template = lookup_context.find_template "#{column.macro}/#{column.display_type}", ['bhf/table'], true
template.render(self, {column_value: column_value, object: object})

它几乎可以解决locals的问题。但它已经感觉就像你抓住模板的部分,渲染部分很好分开。

6 个答案:

答案 0 :(得分:5)

我喜欢这个问题,但它可能是一个错误的工具"事

基本上:

  1. 如果部分是简单的 - 考虑不使用部分,开销是不值得的
  2. 如果部分是复杂的 - 那么渲染1000件事就会花费时间,而部分开销只是其中的一小部分,你需要一个计划B
  3. 计划B:

    1. 首先渲染25-50-100 ....并在用户滚动时从服务器获取其他记录 - http://railscasts.com/episodes/114-endless-page
    2. 从服务器发送一个JSON数组,让客户端JS遍历集合并呈现HTML - 这样部分模板仍然可以在rails中呈现,客户端JS可以gsub
    3. 尽可能缓存所有内容 - 这不包括不幸的表单,这些表单通常是最慢的呈现组件
    4. ...
    5. PS你试过细胞吗? https://github.com/apotonick/cells

答案 1 :(得分:2)

以下是我以前的一个答案的例子。它是从PartialRenderer来源中提取的。

- local_names = [:i]
- partials = {}
- 1000.times do |i|
  - name = 'name_%s' % (i % 10)
  - partials[name] ||= lookup_context.find_template(name, lookup_context.prefixes, true, local_names)
  = partials[name].render(self, i: i)

我建议你用辅助方法包装它。请记住,当地人'名称出现在这里两次:首先在local_names中作为数组,第二个在哈希的键中作为#render方法的第二个参数传递。

答案 2 :(得分:0)

这是一个有趣的命题,似乎比我想象的更难找到它的信息。但是从response object的实现中我可以看出,我认为应该可以将输出(<<)附加到stream对象的response对象上。这将在必要时以response#body形式呈现为字符串。

棘手的部分是找到一个定义my_partial方法的好地方。虽然在View中执行此操作并不常见,但仍然应该可以,我认为您应该可以直接访问response对象。否则,您可以在帮助器中定义它,尽管您可能不希望在控制器中使用此代码。

很抱歉,如果这是一个想法而不是答案,但我没有时间对其进行正确测试。

答案 3 :(得分:0)

您可以添加辅助方法并使用content_tag。这实现了让方法作为部分工作的目标。

def my_partial(i)
  content_tag(:div, i, class: 'iterator')
end

如果你的部分内容很多,那么使用content_tag辅助方法可能不是一个好的解决方案,为了便于阅读。

另一种建议:您可以渲染包含循环的单个部分,而不是在循环内多次渲染相同的部分。如果需要仅为一个对象重用partial,则可以将对象作为数组传递。

这是一个更真实的例子:

# index.html.erb
<%= render partial: 'dogs', locals: {dogs: @dogs} %>

# show.html.erb
<%= render partial: 'dogs', locals: {dogs: [@dog]} %>

# _dogs.html.erb
<% dogs.each do |dog| %>
  <%= dog.name %>
<% end %>

答案 4 :(得分:0)

答案 5 :(得分:0)

如果您需要多次渲染并且速度很慢,我建议您多次调用render。我知道,我是队长很明显。

如果locals包含简单的字符串或数字,请尝试渲染一次视图,然后只使用实际变量替换部分。

text = render(partial: 'test', locals: {i: '$'})

1000.times do |i|
  output[i] = text.gsub('$', i)
end