【问题标题】:Why is my rails nested simple form rendering so slow?为什么我的 rails 嵌套简单表单渲染这么慢?
【发布时间】:2018-03-14 04:23:23
【问题描述】:

使用带有以下相关 gem 的 Rails 4.2: 'jquery-turbolinks' 'turbolinks', '~> 2.4.0' 'simple_form', '~> 3.0.2' “select2-rails”,“3.5.9.1” '茧', '~> 1.2.6'

这是我在本地运行的相关页面/表单的控制台(请注意查看速度太慢的问题):

Started GET "/cases/2936/edit_case_contacts" for ::1 at 2018-03-06 18:39:13 -0800
Processing by CasesController#edit_case_contacts as HTML
  Parameters: {"id"=>"2936"}
  User Load (1.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1  ORDER BY "users"."id" ASC LIMIT 1  [["id", 90]]
  Case Load (1.2ms)  SELECT  "cases".* FROM "cases" WHERE "cases"."id" = $1 LIMIT 1  [["id", 2936]]
  Firm Load (1.0ms)  SELECT  "firms".* FROM "firms" WHERE "firms"."id" = $1 LIMIT 1  [["id", 16]]
  Contact Load (1.4ms)  SELECT "contacts"."first_name", "contacts"."last_name", "contacts"."email", "contacts"."company_name", "contacts"."id", "contacts"."type", "contacts"."prefix", "contacts"."suffix" FROM "contacts" WHERE "contacts"."firm_id" = $1  [["firm_id", 16]]
  Incident Load (1.3ms)  SELECT  "incidents".* FROM "incidents" WHERE "incidents"."case_id" = $1 LIMIT 1  [["case_id", 2936]]
  Medical Load (1.3ms)  SELECT  "medicals".* FROM "medicals" WHERE "medicals"."case_id" = $1 LIMIT 1  [["case_id", 2936]]
  Resolution Load (1.4ms)  SELECT  "resolutions".* FROM "resolutions" WHERE "resolutions"."case_id" = $1 LIMIT 1  [["case_id", 2936]]
  Rendered shared/_javascript_case_tabs.html.erb (0.6ms)
  Rendered shared/_case_tabs.html.erb (24.1ms)
  Rendered cases/_case_contact_fields.html.erb (5.0ms)
  CaseContact Load (1.3ms)  SELECT "case_contacts".* FROM "case_contacts" WHERE "case_contacts"."case_id" = $1  [["case_id", 2936]]
  Contact Load (1.9ms)  SELECT "contacts".* FROM "contacts" WHERE "contacts"."id" IN (2132, 2130, 1922, 1918, 1935, 1931, 1921, 2066, 2129, 2158, 2118)
  Rendered cases/_case_contact_fields.html.erb (6.0ms)
  Rendered cases/_case_contact_fields.html.erb (4.9ms)
  Rendered cases/_case_contact_fields.html.erb (4.9ms)
  Rendered cases/_case_contact_fields.html.erb (6.0ms)
  Rendered cases/_case_contact_fields.html.erb (5.3ms)
  Rendered cases/_case_contact_fields.html.erb (5.2ms)
  Rendered cases/_case_contact_fields.html.erb (5.4ms)
  Rendered cases/_case_contact_fields.html.erb (5.0ms)
  Rendered cases/_case_contact_fields.html.erb (5.4ms)
  Rendered cases/_case_contact_fields.html.erb (5.5ms)
  Rendered cases/_case_contact_fields.html.erb (5.3ms)
  Rendered cases/_case_contact_fields.html.erb (5.1ms)
  Rendered cases/_case_contact_fields.html.erb (6.1ms)
  Rendered cases/_case_contact_fields.html.erb (4.9ms)
  Rendered cases/_case_contact_fields.html.erb (4.8ms)
  **Rendered cases/edit_case_contacts.html.erb within layouts/application (197.9ms)**
  **Rendered layouts/_head.html.erb (311.1ms)**
  Rendered layouts/_navigation_links.html.erb (0.7ms)
  Rendered layouts/_navigation.html.erb (5.4ms)
   (1.7ms)  SELECT COUNT(*) FROM "notifications" WHERE (user_id = 90 and is_read = 'f')
  Case Load (2.5ms)  SELECT "cases".* FROM "cases" WHERE "cases"."firm_id" = $1  [["firm_id", 16]]
  Rendered time_entries/_form.html.erb (30.6ms)
  Rendered layouts/_side_navigation_javascript.html.erb (36.9ms)
  Rendered layouts/_side_navigation.html.erb (49.9ms)
  Rendered layouts/_messages.html.erb (0.5ms)
  Rendered layouts/_javascript_application.html.erb (0.5ms)
Completed 200 OK in 618ms (Views: 586.0ms | ActiveRecord: 16.0ms)
source=rack-timeout id=b6ee1bed757a24fb10c794c3e80d80ed timeout=15000ms service=646ms state=completed
source=rack-timeout id=f0cc00f68250d81864e108b8690fa9d4 timeout=15000ms state=ready
source=rack-timeout id=f0cc00f68250d81864e108b8690fa9d4 timeout=15000ms service=0ms state=active

这是我渲染茧部分的表单:

<%= simple_form_for @case do |f| %>
  <div class="case-header">
    <div class="case-title">
      <div style="font-size: 24px;">
        <strong><%= @case.name %></strong><br>
      </div>
      <div id="case-buttons">
        <ul>
          <li><%= link_to 'Cancel', show_case_contacts_path(@case), :class => 'dark' %></li>
          <li><%= f.submit :class => 'sub2-small dark-right', :value => 'Save' %></li>
        </ul>
      </div>
    </div>
    <strong>Case #: </strong><%= @case.case_number %>

    <div class="container-fluid" style="padding: 0;">
      <div class="row">
        <div class="col-md-12">
          <%= render 'shared/case_tabs' %>
        </div>
      </div>
    </div>
  </div>

  <div class="case_contacts-edit-container">
    <%= link_to_add_association 'ADD CONTACT', f, :case_contacts, :"data-association-insertion-node" => "#header_row", :"data-association-insertion-method" => "after", class: 'action-button insure pull-right' %>
     <table id="info-table3">
      <tbody id="case_contacts_">
      <tr id="header_row">
        <td class="gray-back">
          <strong>Name</strong>
        </td>
        <td class="gray-back">
          <strong>Case Role</strong>
        </td>
        <td class="gray-back" colspan="2">
          <strong>Note</strong>
        </td>
      </tr>
        <%= f.simple_fields_for :case_contacts, f.object.case_contacts.includes(:contact).sort_by { |a| [a.name_of_contact_for_sorting] } do |case_contact| %>
          <%= render 'case_contact_fields', :f => case_contact, cache: true %>
        <% end %>
      </tbody>
    </table>
  </div>
<% end %>

<script type="text/javascript">
  $(document).ready(function() {
      $('select').select2();
  });
</script>

这里是部分内容:

<tr class="nested-fields insurance-field">
    <td>
        <%= f.association :contact, :collection => @firm_contacts, :prompt => "Add Contact", :class => 'table-input', :label => false, hint: false, input_html: { id: 'contact_prompt'} %>
    </td>
    <td>
        <div id="popup_case"></div>
        <%= f.input :role, collection: Contact::ROLES, :prompt => "Choose Role", :class => 'table-input', :label => false, hint: false, input_html: { id: 'role_prompt'} %>
    </td>
    <td colspan="2">
        <%= f.text_field :note, :prompt => "Add Note", :class => 'table-input-75', :label => false, hint: false %>

        <%= f.hidden_field :firm_id, :value => @firm.id, hint: false %>

        <%= link_to_remove_association f, class: 'dark-small remove_case_contacts' do %><span id="remove" class="glyphicon glyphicon-remove pull-right" original-title="Remove Contact", hint: false ></span><% end %>
    </td>
</tr>

<script type="text/javascript">
$(document).ready(function() {

      $('#case_contacts_').on('cocoon:before-insert', function() {

      })
      .on('cocoon:after-insert', function(e, insertedItem) {
             $('#contact_prompt').select2();
             $('#role_prompt').select2();
      })
      .on("cocoon:before-remove", function() {

      })
      .on("cocoon:after-remove", function() {
        /* e.g. recalculate order of child items */
      });

});
</script>

从上面的控制台日志中可以看出,它使 _head 部分的渲染速度也很慢,这里是:

<head>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title><%= content_for?(:title) ? yield(:title) : "Litigo | Legal Software" %></title>
  <meta name="description" content="<%= content_for?(:description) ? yield(:description) : "Litigo | Legal Software" %>">
  <link href="<%= Rails.env.production? ? 'https' : 'http' %>://fonts.googleapis.com/css?family=Arimo:400,700|Changa+One" rel='stylesheet' type='text/css'>
  <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
  <%= stylesheet_link_tag 'application', "#{Rails.env.production? ? 'https' : 'http'}://fonts.googleapis.com/css?family=Arimo:400,700|Changa+One" %>
  <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
  <script type="text/javascript" src="https://js.stripe.com/v2/"></script>
  <%= csrf_meta_tags %>
  <% tag :meta, :name => "stripe-key", :content => STRIPE_PUBLIC_KEY %>
  <%= favicon_link_tag 'favicon.png' %>
</head>

我不确定如何减少渲染时间。我什至没有看到任何低垂的果实...帮助!

【问题讨论】:

  • 尝试从 layouts/_head.html.erb 中删除单行,看看哪一行花费的时间最多。
  • 我做到了,但我意识到 _head 在生产模式下不会出现。请注意下面的 nathanvda 评论。不过谢谢!

标签: ruby-on-rails ruby-on-rails-4 simple-form cocoon-gem


【解决方案1】:

这是开发还是生产?因为如果是开发,那就可以解释为什么_head 比较慢(因为没有预编译的资产)。

我注意到的事情:

  • edit_case_contacts中你调用了sort_by从数据库中获取后(为什么不让数据库立即排序数据?)
  • 在部分中,您有一段 javascript 似乎对于所有部分都相同,因此请提取它
  • 另外:最好将所有 javascript 代码放在一个单独的 js 中,因为这样它就可以在生产环境中预编译并包含在 application.js 中

[编辑:如何按“联系人姓名”排序]

您现在正在编写以下内容:

f.object.case_contacts.includes(:contact).sort_by { |a| [a.name_of_contact_for_sorting] }

这将获取包括联系人在内的案例联系人,返回错误,然后使用sort_by 对该数组进行排序。恕我直言,让数据库使用order 对其进行排序会更有效。所以写一些像

f.object.case_contacts.includes(:contact).order("contacts.name")

假设您有联系人的属性名称。更多选择:

  • .order("contacts.last_name, contacts.first_name") 对多个字段进行排序
  • .order("contacts.name asc").order("contacts.name desc") 选择方向

【讨论】:

  • 1.由于我正在使用 f.simple_fields_for :case_contacts (获取当前表单上下文的对象),我真的不知道数据库如何进行排序。实际上,您的意思是让数据库对其进行排序?那会发生在哪里?坚持不懈?
  • 2.提取javascript是什么意思?如何提取它?
  • 3.我将尝试将 js 移动到一个单独的文件(我假设在 assets 文件夹下?)
  • sort_by 在数组上工作,所以它现在首先获取所有数据,然后 ruby​​ 进行排序。如果你改用order,它将被转换为一个sql查询。我编辑了我的答案来解释这一点。是的:将 js 移动到 assets 下的单独文件将证明效率更高,尤其是在构建资产时的生产模式下,因为这样浏览器将能够缓存 js 文件(因为它们不再更改)。
猜你喜欢
  • 1970-01-01
  • 2012-08-08
  • 1970-01-01
  • 2018-03-01
  • 2014-08-16
  • 2012-06-13
  • 1970-01-01
  • 2020-09-06
  • 1970-01-01
相关资源
最近更新 更多