首页 文章

使用Rails 3.1,您在哪里放置“特定于页面”的JavaScript代码?

提问于
浏览
382

据我所知,您的所有JavaScript都合并为1个文件 . 当Rails将 //= require_tree . 添加到 application.js 清单文件的底部时,默认情况下会执行此操作 .

这听起来像是一个真正的生命保护程序,但我有点担心特定于页面的JavaScript代码 . 此代码是否在每个页面上执行?我想要的最后一件事是,只需要在1页上需要为每个页面实例化我的所有对象 .

此外,代码是否也存在冲突的可能性?

或者你是否在页面底部放了一个小的 script 标签,它只调用一个执行页面javascript代码的方法?

那你不再需要require.js了吗?

谢谢

EDIT :我很欣赏所有的答案......而且我似乎并没有提及...而其他人只是提到 javascript_include_tag ......我知道存在(显然......)但是看起来Rails 3.1的方式前进了是将所有JavaScript包装到1个文件中,而不是在每个页面底部加载单独的JavaScript .

我能想出的最佳解决方案是使用 id s或 class es将 div 标签中的某些功能包装起来 . 在JavaScript代码中,您只需检查 idclass 是否在页面上,如果是,则运行与其关联的JavaScript代码 . 这样,如果动态元素不在页面上,则JavaScript代码不会包含在由Sprockets打包的大量 application.js 文件中 .

我的上述解决方案的好处是,如果在100个页面中的8个页面中包含搜索框,则它将仅在这8个页面上运行 . 您也不必在网站的8个页面上包含相同的代码 . 实际上,您永远不必在任何地方再次在您的网站上包含手动脚本标记 .

我认为这是我问题的实际答案 .

29 回答

  • 2

    Asset Pipeline文档建议如何执行特定于控制器的JS:

    例如,如果生成了ProjectsController,则app / assets / javascripts / projects.js.coffee上会有一个新文件,app / assets / stylesheets / projects.css.scss中会有另一个文件 . 您应该将任何JavaScript或CSS唯一的控件放在各自的资产文件中,因为这些文件可以仅为这些控制器加载,例如<%= javascript_include_tag params [:controller]%>或<%= stylesheet_link_tag params [ :controller]%> .

    Link to: asset_pipeline

  • 3

    对于特定于页面的js,您可以使用Garber-Irish solution .

    因此,对于两个控制器 - 汽车和用户,您的Rails javascripts文件夹可能看起来像这样:

    javascripts/
    ├── application.js
    ├── init.js
    ├── markup_based_js_execution
    ├── cars
    │   ├── init .js
    │   ├── index.js
    │   └── ...
    └── users
        └── ...
    

    javascripts将如下所示:

    // application.js
    
    //= 
    //= require init.js
    //= require_tree cars
    //= require_tree users
    

    // init.js
    
    SITENAME = new Object();
    SITENAME.cars = new Object;
    SITENAME.users = new Object;
    
    SITENAME.common.init = function (){
      // Your js code for all pages here
    }
    

    // cars/init.js
    
    SITENAME.cars.init = function (){
      // Your js code for the cars controller here
    }
    

    // cars/index.js
    
    SITENAME.cars.index = function (){
      // Your js code for the index method of the cars controller
    }
    

    和markup_based_js_execution将包含UTIL对象的代码,以及支持DOM的UTIL.init执行 .

    并且不要忘记将它放到您的布局文件中:

    <body data-controller="<%= controller_name %>" data-action="<%= action_name %>">
    

    我还认为最好使用类而不是 data-* 属性,以获得更好的页面特定css . 正如Jason Garber所提到的:特定于页面的CSS选择器可能变得非常笨拙(当您使用 data-* 属性时)

    我希望这能帮到您 .

  • -2

    我看到你已经回答了自己的问题,但这是另一种选择:

    基本上,你正在做出这样的假设

    //= require_tree .
    

    是必须的 . 它诚实地使用3.1.x,我已经制作了三个不同的顶级JS文件 . 我的 application.js 文件只有

    //= require jquery
    //= require jquery_ujs
    //= require_directory .
    //= require_directory ./api
    //= require_directory ./admin
    

    这样,我可以使用自己的顶级JS文件创建子目录,只包含我需要的内容 .

    关键是:

    • 您可以删除 require_tree - Rails允许您更改它所做的假设

    • 名称 application.js 没什么特别的 - assets/javascript 子目录中的任何文件都可以包含带有 //= 的预处理器指令

    希望能帮助并为ClosureCowboy的答案添加一些细节 .

    Sujal

  • 2

    另一种选择:创建特定于页面或模型的文件,您可以在 assets/javascripts/ 文件夹中创建目录 .

    assets/javascripts/global/
    assets/javascripts/cupcakes
    assets/javascripts/something_else_specific
    

    您的主 application.js 清单文件可以配置为从 global/ 加载其文件 . 特定页面或页面组可以有自己的清单,从其自己的特定目录加载文件 . Sprockets会自动将 application.js 加载的文件与特定于页面的文件组合在一起,从而使该解决方案能够正常工作 .

    此技术也可用于 style_sheets/ .

  • 2

    我很欣赏所有答案......而且我似乎并没有......和其他人有关只提一下 javascript_include_tag ...我知道存在(显然......)但是看起来Rails 3.1的方法是将你所有的Javascript包装成1个文件,而不是在每个页面的底部加载单独的Javascript .

    我能想出的最佳解决方案是使用 idclass es将 div 标签中的某些功能包装起来 . 在javascript代码中 . 然后你只需检查 idclass 是否在页面上,如果是,则运行与之关联的javascript代码 . 这样,如果动态元素不在页面上,则javascript代码不会包含在由Sprockets打包的大量 application.js 文件中 .

    我的上述解决方案的好处是,如果在100个页面中的8个页面中包含搜索框,则它将仅在这8个页面上运行 . 您也不必在网站的8个页面上包含相同的代码 . 实际上,您永远不必在任何地方再次在您的网站上包含手动脚本标记 - 除了可能预加载数据 .

    我认为这是我问题的实际答案 .

  • 1

    我意识到我迟到了这个派对,但我想提出一个我最近一直在使用的解决方案 . 但是,我先提一下......

    The Rails 3.1/3.2 Way (No, sir. I don't like it.)

    见:http://guides.rubyonrails.org/asset_pipeline.html#how-to-use-the-asset-pipeline

    为了这个答案的完整性,我包括以下内容,因为它不是一个不可行的解决方案......虽然我并不关心它 .

    “Rails Way”是面向控制器的解决方案,而不是像这个问题的原始作者那样以视图为导向 . 有各自的控制器命名的控制器特定的JS文件 . 所有这些文件都放在一个文件夹树中,默认情况下不包含在任何application.js require指令中 .

    要包含特定于控制器的代码,以下内容将添加到视图中 .

    <%= javascript_include_tag params[:controller] %>
    

    我厌恶这个解决方案,但它很快 . 据推测,您可以调用这些文件,如"people-index.js"和"people-show.js",然后使用类似 "#{params[:controller]}-index" 的内容来获得面向视图的解决方案 . 再次,快速修复,但它并不适合我 .

    My Data Attribute Way

    叫我疯了,但是当我部署时,我希望我的所有JS编译并缩小为application.js . 我不想记得在整个地方都包含这些小的straggler文件 .

    我将所有的JS加载到一个紧凑的,即将成为浏览器的缓存文件中 . 如果我的application.js的某一部分需要在页面上触发,我让HTML告诉我,而不是Rails .

    我使用名为 data-jstags 的自定义数据属性,而不是将我的JS锁定到特定的元素ID或者使用标记类乱丢我的HTML .

    <input name="search" data-jstag="auto-suggest hint" />
    

    在每个页面上,我使用 - 在这里插入首选的JS库方法 - 在DOM加载完成后运行代码 . 此引导代码执行以下操作:

    • 迭代标记为 data-jstag 的DOM中的所有元素

    • 对于每个元素,在空间上拆分属性值,创建标记字符串数组 .

    • 对于每个标记字符串,在哈希中为该标记执行查找 .

    • 如果找到匹配的键,则运行与其关联的函数,并将该元素作为参数传递 .

    所以说我在application.js中的某处定义了以下内容:

    function my_autosuggest_init(element) {
      /* Add events to watch input and make suggestions... */
    }
    
    function my_hint_init(element) {
      /* Add events to show a hint on change/blur when blank... */
      /* Yes, I know HTML 5 can do this natively with attributes. */
    }
    
    var JSTags = {
      'auto-suggest': my_autosuggest_init,
      'hint': my_hint_init
    };
    

    bootstrapping事件将对搜索输入应用 my_autosuggest_initmy_hint_init 函数,将其转换为输入,在用户键入时显示建议列表,并在输入空白且未聚焦时提供某种输入提示 .

    除非某些元素标记为 data-jstag="auto-suggest" ,否则自动建议代码永远不会触发 . 但是,它始终存在,缩小并最终缓存在我的application.js中,以便我在页面上需要它 .

    如果您需要将其他参数传递给标记的JS函数,则必须应用一些创造力 . 添加数据参数属性,提出某种参数语法,甚至使用混合方法 .

    即使我有一些看似特定于控制器的复杂工作流程,我也只是在我的lib文件夹中为它创建一个文件,将其打包到application.js中,并用“new-thing-wizard”标记它 . 当我的引导程序命中该标记时,我的漂亮,花哨的向导将被实例化并运行 . 它在需要时运行该控制器的视图,但不以其他方式耦合到控制器 . 事实上,如果我对我的向导进行编码,我可能能够在视图中提供所有配置数据,因此可以在以后为任何其他需要的控制器重用我的向导它 .

    无论如何,这就是我现在一直在实现特定于页面的JS的方式,它对于简单的站点设计和更复杂/丰富的应用程序都很有帮助 . 希望我在这里提出的两种解决方案之一,我的方式或Rails方式,对将来遇到这个问题的任何人都有帮助 .

  • 1

    这已经很久以前得到了回答和接受,但我基于其中一些答案和我对Rails 3的经验提出了自己的解决方案 .

    资产管道很好 . 用它 .

    首先,在 application.js 文件中,删除 //= require_tree.

    然后在你的 application_controller.rb 中创建一个帮助方法:

    helper_method :javascript_include_view_js //Or something similar
    
    def javascript_include_view_js
        if FileTest.exists? "app/assets/javascripts/"+params[:controller]+"/"+params[:action]+".js.erb"
            return '<script src="/assets/'+params[:controller]+'/'+params[:action]+'.js.erb" type="text/javascript"></script>'
        end
    end
    

    然后在您的 application.html.erb 布局文件中,在现有的javascript包含中添加新的帮助程序,前缀为 raw 帮助程序:

    <head>
        <title>Your Application</title>
        <%= stylesheet_link_tag "application", :media => "all" %>
        <%= javascript_include_tag "application" %>
        <%= raw javascript_include_view_js %>
    </head>
    

    瞧,现在你可以使用你在rails中的其他地方使用的相同文件结构轻松创建特定于视图的javascript . 只需将文件粘贴在 app/assets/:namespace/:controller/action.js.erb 中!

    希望能帮助别人!

  • 6

    您可以在布局文件中添加此行(例如application.html.erb)以自动加载控制器特定的javascript文件(生成控制器时创建的文件):

    <%= javascript_include_tag params[:controller] %>
    

    您还可以添加一行以自动加载脚本文件 .

    <%= javascript_include_tag params[:controller] + "/" + params[:action] %>
    

    只需将页面脚本放入以控制器名称命名的子目录中即可 . 在这些文件中,您可以使用= require包含其他脚本 . 如果文件存在,创建一个帮助程序来包含文件会很好,以避免浏览器中的404失败 .

  • 0
    <%= javascript_include_tag params[:controller] %>
    
  • 6

    也许你会发现pluggable_js gem是合适的解决方案 .

  • 1

    LoadJS gem是另一种选择:

    LoadJS提供了一种在Rails应用程序中加载特定于页面的Javascript代码的方法,而不会失去Sprockets提供的魔力 . 您的所有Javascript代码将在一个Javascript文件中继续缩小,但其中某些部分仅针对某些页面执行 . https://github.com/guidomb/loadjs

  • 7

    菲利普的答案非常好 . 以下是使其工作的代码:

    在application.html.erb中:

    <body class="<%=params[:controller].parameterize%>">

    假设您的控制器名为Projects,那将生成:

    <body class="projects">

    然后在projects.js.coffee中:

    jQuery ->
      if $('body.projects').length > 0  
         $('h1').click ->
           alert 'you clicked on an h1 in Projects'
    
  • 0

    只有在告诉Rails(Sprockets,而不是)合并它们时,才会合并JavaScripts .

  • 1

    这就是我解决造型问题的方法: (excuse the Haml)

    %div{:id => "#{params[:controller].parameterize} #{params[:view]}"}
        = yield
    

    这样我启动所有页面特定的 .css.sass 文件:

    #post
      /* Controller specific code here */
      &#index
        /* View specific code here */
      &#new
      &#edit
      &#show
    

    这样你就可以轻松避免任何冲突 . 说到 .js.coffee 文件,您可以初始化元素,如;

    $('#post > #edit') ->
      $('form > h1').css('float', 'right')
    

    希望这有助于一些人 .

  • 77

    您还可以将js分组到文件夹中,并根据页面继续使用资产管道load your javascript selectively .

  • 0

    我同意你的回答,检查选择器是否在那里,使用:

    if ($(selector).length) {
        // Put the function that does not need to be executed every page
    }
    

    (没有看到有人添加实际的解决方案)

  • -3

    我不会试图把meleyal,sujal(一个la ClosureCowboy),Ryan的答案的第一部分,甚至是Gal关于Backbone.js的大胆声明......所有这些都以简短明了的方式结合在一起 . 而且,谁知道,我甚至可能会满足Marnen Laibow-Koser的要求 .

    示例编辑

    assets / javascripts / application.js

    //= require jquery
    //= require jquery_ujs
    //= require lodash.underscore.min
    ...
    

    views / layouts / application.html.erb

    ...
      </footer>
    
      <!-- Javascripts ================================================== -->
      <!-- Placed at the end of the document so the pages load faster -->
      <%= javascript_include_tag "application" %>
      <%= yield :javascript %>
    
    </body>
    </html>
    

    views / foo / index.html.erb

    ...
    <% content_for :javascript do %>
      <%= javascript_include_tag params[:controller] %>
    <% end %>
    

    assets / javascripts / foo.js

    //= require moment
    //= require_tree ./foostuff
    

    assets / javascripts / foostuff / foothis.js.coffee

    alert "Hello world!"
    

    简要说明

    • application.js 删除 //= require_tree . 并仅列出每个页面共享的JS .

    • 上面显示的两行 application.html.erb 告诉页面包含application.js和特定于页面的JS的位置 .

    • 上面显示的 index.html.erb 中的三行告诉您的视图寻找一些特定于页面的JS并将其包含在名为":javascript"(或您想要命名的任何名称)的命名yield区域中 . 在此示例中,控制器为"foo",因此Rails将尝试在应用程序布局中的:javascript yield区域中包含"foo.js" .

    • foo.js (或控制器的名称)中列出特定于页面的JS . 列出公共库,树,目录等等 .

    • 保持您的自定义页面特定的JS在某个地方,您可以轻松地从其他自定义JS中引用它 . 在这个例子中,foo.js需要foostuff树,所以将自定义JS放在那里,例如 foothis.js.coffee .

    • 这里没有硬规则 . 随意移动东西,甚至可能在各种布局中创建多个不同名称的屈服区域需要 . 这只是向前迈出的第一步 . (考虑到我们使用Backbone.js,我不会完全这样做 . 我也可以选择将foo.js放到一个名为foo而不是foostuff的文件夹中,但还没有决定 . )

    注意事项

    您可以使用CSS和 <%= stylesheet_link_tag params[:controller] %> 执行类似的操作,但这超出了问题的范围 .

    如果我在这里错过了一个明显的最佳实践,请给我一个便条,我会适应 . Rails对我来说还是一个新手,老实说,到目前为止,我对它在企业开发中带来的混乱以及平均Rails程序产生的所有流量都没有给人留下深刻的印象 .

  • 5

    我有另一种解决方案,虽然原始对我来说很好,并且不需要任何花哨的选择性加载策略 . 放入您的nornal文档就绪函数,然后测试当前的Windows位置,看看它是否是您的javascript所针对的页面:

    $(document).ready(function() {
       if(window.location.pathname.indexOf('/yourpage') != -1) {
              // the javascript you want to execute
       }
    }
    

    这仍然允许所有js由rails 3.x在一个小包中加载,但不会产生很多开销或与js不打算的页面有任何冲突 .

  • 65

    ryguy的回答是一个很好的答案,尽管它已经被归入负面土地 .

    特别是如果你使用像Backbone JS这样的东西 - 每个页面都有自己的Backbone视图 . 然后erb文件只有一行内联javascript,它会激活正确的主干视图类 . 我认为它只是一行'胶水代码',因此它的内联是可以的 . 优点是你可以保留你的“require_tree”,让浏览器缓存所有的javascript .

    在show.html.erb中,你会有类似的东西:

    <% provide :javascript do %>
      <%= javascript_include_tag do %>
        (new app.views.ProjectsView({el: 'body'})).render();
      <% end %>
    <% end do %>
    

    在你的布局文件中,你需要:

    <%= yield :javascript %>
    
  • 6

    将所有commom JS文件移动到'app/assets/javascript/global'之类的子文件夹,然后在application.js中,将 //= require_tree . 行修改为 //= require_tree ./global .

    现在您可以自由地将特定于控制器的JS放在'app/assets/javascript/'根上,并且它们将不会包含在已编译的JS中,仅在您通过控制器/视图上的 = javascript_include_tag 调用它们时使用 .

  • 23

    虽然你在这里有几个答案,但我认为你的编辑可能是最好的选择 . 我们从Gitlab获得的团队设计模式是Dispatcher模式 . 它的功能类似于您所说的内容,但页面名称由rails标记在body标签中 . 例如,在您的布局文件中,只包含类似(在HAML中)的内容:

    %body{'data-page' => "#{controller}:#{action}" }
    

    然后在javascripts文件夹中的 dispatcher.js.coffee 文件中只有一个闭包和一个switch语句,如下所示:

    $ ->
      new Dispatcher()
    
    class Dispatcher
      constructor: ->
        page = $('body').attr('data-page')
        switch page
          when 'products:index'
            new Products() 
          when 'users:login'
            new Login()
    

    您需要在单个文件中执行的操作(例如 products.js.coffeelogin.js.coffee )将它们包含在类中,然后将该类符号全局化,以便您可以在调度程序中访问它:

    class Products
      constructor: ->
        #do stuff
    @Products = Products
    

    Gitlab有几个这方面的例子,如果你好奇的话,你可能想要解决这个问题:)

  • 155

    Paloma项目提供了管理页面特定javascript代码的有趣方法 .

    他们的文档中的用法示例:

    var UsersController = Paloma.controller('Users');

    //执行Rails User#new时执行 .
    UsersController.prototype.new = function(){
    警报('你好性感的用户!');
    };

  • 2

    步骤1 . 删除require_tree . 在你的application.js和application.css中 .

    第2步 . 在布局文件夹中编辑application.html.erb(通过rails默认值) . 在以下标签中添加“params [:controller]” .

    <%= stylesheet_link_tag    'application', params[:controller], media: 'all', 'data-turbolinks-track' => true %>
    
    <%= javascript_include_tag 'application', params[:controller], 'data-turbolinks-track' => true %>
    

    第三步 . 在config / initializers / assets.rb中添加文件

    %w( controller_one controller_two controller_three ).each do |controller|
      Rails.application.config.assets.precompile += ["#{controller}.js", "#{controller}.js.coffee", "#{controller}.css", "#{controller}.scss"]
    end
    

    参考:http://theflyingdeveloper.com/controller-specific-assets-with-rails-4/

  • 1

    我没有试过这个,但看起来如下:

    • 如果你有一个javascript的content_for(例如,其中包含真正的javascript),sprockets就不会知道它,因此这将与现在的方式相同 .

    • 如果你想从大包的javascript中排除一个文件,你会进入config / sprockets.yml文件并相应地修改source_files . 然后,您只需包含您在需要时排除的任何文件 .

  • 2

    我以前使用这种方法做过:http://theflyingdeveloper.com/controller-specific-assets-with-rails-4/ . 超级简单,依靠控制器来选择要加载的正确js .

  • 16

    我将一些答案合并到:

    申请助手:

    module ApplicationHelper
      def js_page_specific_include
        page_specific_js = params[:controller] + '_' + params[:action]
        if Rails.application.assets.find_asset(page_specific_js).nil?
          javascript_include_tag 'application', 'data-turbolinks-track' => true
        else
          javascript_include_tag 'application', page_specific_js, 'data-turbolinks-track' => true
        end
      end
    end
    

    布局/ application.html.haml:

    <!DOCTYPE html>
    %html{lang: 'uk'}
      %head   
        = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
       bla-bla-bla
        = js_page_specific_include   
       bla-bla-bla
    
  • 1

    首先:从application.js中删除 \\=require_tree 第二:所有的JS代码都必须在 /app/assets/javascritpt 处找到,所有的CSS代码都必须在 /app/assets/stylesheets 找到

  • 40

    在Ryan的带领下,这就是我所做的 -

    application.js.coffee

    $ ->
        view_method_name = $("body").data("view") + "_onload"
        eval("#{view_method_name}()") if eval("typeof #{view_method_name} == 'function'")
        view_action_method_name = $("body").data("view") + "_"+$("body").data("action")+"_onload"
        eval("#{view_action_method_name}()") if eval("typeof #{view_action_method_name} == 'function'")
    

    users.js.coffee(控制器特定的coffeescript,例如controller:users,action:dashboard)

    window.users_dashboard_onload = () ->
        alert("controller action called")
    window.users_onload = () ->
        alert("controller called")
    

    application.html.haml

    %body{:data=>{:view=>controller.controller_name, :action=>controller.action_name}}
    
  • 0

    以下是如何执行此操作,尤其是如果您不必为特定页面执行大量库,而只需要或多或少地运行几百行JS .

    由于将Javascript代码嵌入到HTML中非常好,只需在app / views shared.js目录下创建并将页面/页面特定代码放在 my_cool_partial.html.erb

    <script type="text/javascript"> 
    <!--
      var your_code_goes_here = 0;
      function etc() {
         ...
      }
    -->
    </script>
    

    所以现在从你想要的任何地方你只需:

    = render :partial => 'shared.js/my_cool_partial'
    

    就是这样,k?

相关问题