Convert a partial to method/block for speed

后端 未结 6 572
暗喜
暗喜 2021-02-06 13:46

I have a loop that renders a partial

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

this is really slow, up to 0.1 ms for for

相关标签:
6条回答
  • 2021-02-06 14:19

    If you need render many times and it is slow, I suggest you do not call render many times. I know, I am captain obvious.

    If locals contains simple strings or numbers, try to render your view once, and then just replace parts with actual variables.

    text = render(partial: 'test', locals: {i: '$'})
    
    1000.times do |i|
      output[i] = text.gsub('$', i)
    end
    
    0 讨论(0)
  • 2021-02-06 14:21

    Here is an example from one of my previous answers. It's extracted from PartialRenderer sources.

    - 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)
    

    I'd recommend you to wrap it with a helper method. Keep in mind that locals' names appear here twice: first in local_names as an array and second in hash's keys passed as the second argument of #render method.

    0 讨论(0)
  • 2021-02-06 14:32

    This is an interesting proposition and it seems harder than I thought to find information about it. However from what I can tell from the implementation of the response object I would think it should be possible to append (<<) your output to the stream object of the response object. This will be rendered as a string into the response#body as necessary.

    The tricky part is to find a good place to define your my_partial method. While it is not commonplace to do this in a View it should still be possible and I think you should have access to the response object directly. Otherwise you might define it in a helper, though you probably do not want to use this code in a controller.

    Sorry if this is rather an idea than an answer, but I lack the time to test it properly.

    0 讨论(0)
  • 2021-02-06 14:35

    I love this question, but it may be a "wrong tool for the job" thing

    Basically:

    1. if the partial is simple - consider not using a partial at all, the overhead isn't worth it
    2. if the partial is complex - then rendering 1000s of things will take TIME out of which partial overhead is just a fraction of, you'll need a plan B

    Plan Bs:

    1. render 25-50-100.... thingies at first and fetch additional records from the server as the user scrolls - http://railscasts.com/episodes/114-endless-page
    2. send a JSON array from the server and let client side JS iterate through the collection and render HTML - this way the partial template can still be rendered in rails and client side JS can do gsub
    3. cache everything you possibly can - this doesn't include forms unfortunately which are typically the slowest rendering components
    4. ...

    PS have you tried out cells? https://github.com/apotonick/cells

    0 讨论(0)
  • 2021-02-06 14:36

    You could add a helper method and use content_tag. This accomplishes your goal of having a method work as a partial.

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

    Using helper methods with content_tag probably isn't a good solution if you have a lot of content in your partial, for readability's sake.

    An alternative recommendation: Instead of rendering the same partial multiple times within a loop, you could render a single partial that includes a loop. If you needed to reuse the partial for only one object, you could just pass the object as an array.

    Here's a more real-world example:

    # 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 %>
    
    0 讨论(0)
  • 2021-02-06 14:38

    Use :collection option:

    = render partial: 'test', collection: 10.times.to_a, as: :i
    

    Docs: http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials

    Source: https://github.com/rails/rails/blob/master/actionview/lib/action_view/renderer/partial_renderer.rb#L284

    0 讨论(0)
提交回复
热议问题