Rails 3.1 资产管道:如何加载特定于控制器的脚本?

Posted

技术标签:

【中文标题】Rails 3.1 资产管道:如何加载特定于控制器的脚本?【英文标题】:Rails 3.1 asset pipeline: how to load controller-specific scripts? 【发布时间】:2011-09-28 03:25:48 【问题描述】:

如果我在 Rails 3.1 中生成一个新控制器,也会自动添加一个带有控制器名称的 javascript 文件。首先,我认为只有在调用相关控制器时才会使用这个 javascript 文件。

默认情况下,application.js 文件中的指令 //= require_tree . 包含其树上的每个 javascript 文件。

我怎样才能只加载控制器特定的脚本?

【问题讨论】:

这样做可能不是一个好主意。请查看此相关问题的答案:***.com/questions/8250951/… 编写您的 javascript,使其特定于页面,然后不必担心所有内容都混合在一起的事实。如果这是编译后的代码,你会这样做,对吧? 查看具体:***.com/questions/6167805/… 【参考方案1】:

仅加载必要的 name_of_the_js_file.js 文件:

    application.js中删除//=require_tree

    将您的 js 文件(在加载特定页面时要加载的文件)保留在资产管道中

    application_helper.rb中添加一个助手

    def javascript(*files)
      content_for(:head)  javascript_include_tag(*files) 
    end
    

    屈服于你的布局:

    <%= yield(:head) %>
    

    将此添加到您的视图文件中:

    <% javascript 'name_of_the_js_file' %>
    

那应该没问题

【讨论】:

值得注意的是,这种方法在生产中运行良好。例如。如果您查看生产中的源代码,您会看到单个控制器 javascript 文件获得了适当的缓存清除名称,就像主 application.js 文件一样: 是的,因为文件本身在资产管道中。我们只是不希望在 application.js 中需要它。 你能说得更具体点吗?也许我可以帮忙。 确保在您的config/application.rb 中添加config.assets.precompile += %w(name_of_js_file.js) 之类的行,否则您可能会像我一样遇到预编译问题。另见jalada.co.uk/2012/01/23/… 在 rails 3.2.3 中为我工作(需要上面 ZMorek 指定的 config.assets.precompile 选项)【参考方案2】:

一个优雅的解决方案是在你的javascript_include_tag中要求controller_name

见http://apidock.com/rails/ActionController/Metal/controller_name/class

<%= javascript_include_tag "application", controller_name %>

controller_name.js 将被加载并且也在资产中,因此您可以从此处获取其他文件。

例如,渲染汽车#index 会给出

<%= javascript_include_tag "application", "cars" %>

cars.js 可以包含的地方

//= require wheel
//= require tyre

享受吧!

【讨论】:

虽然这在阅读后很明显,但我并没有立即想到解决方案。 如果您没有每个controller_name.js 的文件,您可能会看到一些预编译问题和缓存未命中,尤其是如果您没有显式预编译所有这些问题。 预编译问题由 config.assets.precompile 设置解决 - 有关此问题的另一个答案,请参阅 ZMorek 的评论。 更好: @Pencilcheck 您的解决方案不起作用。 asset_path 总是返回一个路径,即使文件不存在【参考方案3】:

您的问题可以通过不同的方式解决。

动态添加资产

请考虑这对于生产模式不是一个好的解决方案,因为您的控制器细节不会被预编译!

    将以下方法添加到我们的应用程序助手中:

    module ApplicationHelper
        def include_related_asset(asset)
        #          v-----Change this
            if !YourApp::Application.assets.find_asset(asset).nil?
                case asset.split('.')[-1]
                    when 'js'
                        javascript_include_tag asset
                    when 'css'
                        stylesheet_link_tag asset
                end
            end
        end
    end
    

    layout-file 中调用辅助方法:

    <%= include_related_asset(params[:controller].to_param + '_' + params[:action].to_param . 'js') %>
    

    为您的控制器操作创建特定资产。例如。 controller_action.js

请不要忘记将YourApp 更改为您的应用名称。

使用yield

    &lt;%= yield :head%&gt; 添加到您的布局头部

    在您的操作视图中包含您的资产:

    <% content_for :head do %>
    <%= javascript_include_tag 'controller_action' %>
    <% end %>
    

请参阅Rails guides 了解更多信息。

【讨论】:

【参考方案4】:

我总是将它包含在我的布局文件中。它可以将你的 js 限定为行动

<%= javascript_include_tag params[:controller] if AppName::Application.assets.find_asset("#params[:controller].js") %>
<%= javascript_include_tag "#params[:controller]_#params[:action]"  if AppName::Application.assets.find_asset("#params[:controller]_#params[:action].js") %>

【讨论】:

虽然我真的很喜欢这个,但在生产中似乎不起作用。 我想最好的解决方案。 @kitsched - 您可能需要通过 ***.com/a/18992685/94668 之类的方式将所有资产添加到 config.assets.precompile 谢谢,试试看。 这对我来说是最好的解决方案。它可以在生产环境中使用。【参考方案5】:

我最近发现了一种为特定控制器使用生成脚本的简单方法。我使用该解决方案gem gon。添加控制器:

class HomesController < ApplicationController
  before_filter :remember_controller

  private

  def remember_controller
    gon.controller = params[:controller]
  end
end

然后打开您的homes.js.cofee 并在文件开头添加:

jQuery ->
  if gon.controller == "sermons"
    # Place all functions here...

就是这样。

【讨论】:

【参考方案6】:

我喜欢albandiguer's solution。我发现 javascript/coffeescript 资产不是单独预编译的。这会导致尝试使用javascript_path 时出现各种错误。在我解决了一些人在他的 cmets 中提到的问题后,我将分享我对该问题的解决方案。主要只处理部分控​​制器名为 JavaScript 文件的集合。

所以我构建了一个应用程序助手来检测文件是否存在于 javascript 目录中,而不管扩展名为 .coffee/.js:

module ApplicationHelper
  def javascript_asset_path(basename)
    Sprockets::Rails::Helper.assets.paths.select|i|
      i =~ /javascript/ and i =~ /#Rails.root/
    .each do |directory|
      if Dir.entries(directory).map |i| i.split('.')[0].compact.
          include? basename
        return File.join(directory, basename)
      end
    end
    nil
  end
end

此方法将返回 javascript 文件的完整路径(如果存在)。否则返回零。因此,按照 Pencilcheck 的评论,您可以将此方法添加为条件包括:

<%= javascript_include_tag(controller_name) if javascript_asset_path(controller_name) %>

现在你有了一个合适的条件包含。现在讨论预编译资产的问题。通常用于单独优化you don't want assets precompiled。但是,如果您必须这样做,您可以这样做:

# Live Compilation
config.assets.compile = true

你可以添加这个做你的环境配置文件。首先在您的开发环境文件中对其进行测试。这又是不可取的。 Rails 资产管道使用 Sprocket 优化一切:

Sprockets 加载指定的文件,必要时处理它们, 将它们连接成一个文件,然后压缩它们(如果 Rails.application.config.assets.compress 为真)。通过提供一个文件 而不是很多,页面的加载时间可以大大减少 因为浏览器发出的请求更少。压缩也减少 文件大小,使浏览器能够更快地下载它们。

请阅读文档以了解有关 Sprockets (Asset Pipeline) http://guides.rubyonrails.org/asset_pipeline.html 机制的更多详细信息

资产不是单独预编译的。例如当我尝试时:

<%= javascript_include_tag 'event' %>

我明白了:

Sprockets::Rails::Helper::AssetFilteredError: 资产被过滤掉并且 将不提供服务:将 Rails.application.config.assets.precompile += %w( event.js ) 添加到 config/initializers/assets.rb 并重新启动您的 服务器

因此您可以包含要单独预编译的资产。我们只需要在我们的资产初始化程序中添加名为 javascript 文件的相关控制器。好吧,我们可以通过编程来完成。

要获取控制器名称列表,我将使用ecoologic's example:

all_controllers =  Dir[
    Rails.root.join('app/controllers/*_controller.rb')
  ].map  |path|
    path.match(/(\w+)_controller.rb/); $1
  .compact

现在要获取与控制器名称的基本名称匹配的所有 javascript 文件的名称,您可以使用以下命令:

javascripts_of_controllers = Sprockets::Rails::Helper.assets.paths.select|a_path|
    a_path =~ /javascript/ and a_path =~ /#Rails.root/
  .map |a_path|
    Dir.entries(a_path)
  .flatten.delete_if |the_file|
    !the_file['.js']
  .collect |the_file|
    the_file if all_controllers.any? |a_controller| the_file[a_controller]
  

那你可以试试:

# config/initializers/assets.rb
Rails.application.config.assets.precompile += javascripts_of_controllers

这将为您提供与您的控制器名称匹配的所有 javascript 文件的列表,不包括目录路径。请注意,如果您的控制器名称是复数,则 javascript 名称也应该是复数。另请注意,如果控制器是单数且 javascript 文件是复数,这仍将包含它,因为 the_file[a_controller] 将在部分匹配时成功。

随时在您的Rails.application.config.assets.precompile 设置中尝试一下。我知道这可以正确地为您提供文件列表。但我会让你去测试它。让我知道以这种方式预编译是否有任何细微差别,因为我很好奇。

有关资产如何预编译的详细说明,请参阅此博客:http://www.sitepoint.com/asset-precompile-works-part/

【讨论】:

我应该把 all_controllers 和 javascripts_of_controllers 语句放在哪里? 好吧。我将两者都放入我的资产初始化程序(assets.rb)但 Sprockets::Rails::Helper.assets.paths 在这里为空,所以我不得不将它更改为 Rails.application.config.assets.paths 其余的都很好。非常好的解决方案

以上是关于Rails 3.1 资产管道:如何加载特定于控制器的脚本?的主要内容,如果未能解决你的问题,请参考以下文章

rails 3.1资产管道路由错误

如何将dojo工具包与rails 3.1资产管道和coffeescript一起使用?

如何在 Ruby on Rails 3.1 中禁用资产管道(链轮)消息的日志记录?

>=Rails 3.1 如何在资产管道中包含 IE 特定的 YAML-CSS 文件

Rails 3.1 资产管道和缓存

Rails 3.1 资产管道供应商/资产文件夹组织