javascript - require_self - ror assets




使用Rails 3.1,你把你的“頁面特定”JavaScript代碼放在哪裡? (19)

據我了解,你所有的JavaScript被合併成1個文件。 Rails在添加//= require_tree .時會默認執行此操作//= require_tree . 到您的application.js清單文件的底部。

這聽起來像是一個真正的生活節省,但我有點關注頁面特定的JavaScript代碼。 此代碼是否在每個頁面上執行? 我想要的最後一件事是,只有在1頁上需要它們時,才能為每個頁面實例化所有對象。

另外,這些代碼是否存在衝突的可能性?

或者,您是否在頁面底部放置了一個小script標記,只需調用一個執行該頁面的JavaScript代碼的方法?

那麼你不再需要require.js了嗎?

謝謝

編輯 :我感謝所有的答案......我不認為他們真的在解決問題。 其中一些是關於樣式,似乎不涉及...和其他人只是提到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頁上包含相同的代碼。 實際上,您再也不必在網站上添加手動腳本標記。

我認為這是我的問題的實際答案。


LoadJS寶石是另一種選擇:

LoadJS提供了一種在Rails應用程序中加載頁面特定的Javascript代碼的方法,而不會失去Sprockets提供的魔法。 您的所有Javascript代碼將通過一個Javascript文件縮小,但其中的某些部分只會針對某些頁面執行。

LoadJS


Paloma project offers interesting approach to manage page specific javascript code.

Usage example from their docs:

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

// Executes when Rails User#new is executed.
UsersController.prototype.new = function(){
   alert('Hello Sexy User!' );
};

Here's how to do it especially if you don't have to execute tons of libraries for your specific page, but only to run a few hundreds lines of JS more or less.

由於將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?


I combined some answers into:

Application helper:

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

layouts/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  

I have another solution, which although primitive works fine for me and doesn't need any fancy selective loading strategies. Put in your nornal document ready function, but then test the current windows location to see if it is the page your javascript is intended for:

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

This still allows all the js to be loaded by rails 3.x in one small package, but does not generate much overhead or any conflicts with pages for which the js isn't intended.


I haven't tried this out, but it looks like the following is true:

  • if you have a content_for that is javascript (eg with real javascript within it), sprockets would not know about it and thus this would work the same way as it does now.

  • if you want to exclude a file from the big bundle of javascript, you would go into config/sprockets.yml file and modify the source_files accordingly. Then, you would just include any of the files that you excluded where needed.


Though you have several answers here, I think your edit is probably the best bet. A design pattern that we use in our team that we got from Gitlab is the Dispatcher pattern. It does something similar to what you're talking about, however the page name is set in the body tag by rails. For example, in your layout file, just include something like (in HAML):

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

Then only have one closure and a switch statement in your dispatcher.js.coffee file in your javascripts folder like so:

$ ->
  new Dispatcher()

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

All you need to do in the individual files (say products.js.coffee or login.js.coffee for example) is enclose them in a class and then globalize that class symbol so you can access it in the dispatcher:

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

Gitlab has several examples of this that you might want to poke around with in case you're curious :)


ryguy's answer is a good answer, even though its been downvoted into negative points land.

Especially if you're using something like Backbone JS - each page has its own Backbone view. Then the erb file just has a single line of inline javascript that fires up the right backbone view class. I consider it a single line of 'glue code' and therefore the fact that its inline is OK. The advantage is that you can keep your "require_tree" which lets the browser cache all the javascript.

in show.html.erb, you'll have something like:

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

and in your layout file, you'll need:

<%= yield :javascript %>

另一種選擇:創建頁面或模型特定的文件,您可以在assets/javascripts/文件夾中創建目錄。

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

您的主application.js清單文件可以配置為從global/加載它的文件。 特定的頁面或頁面組可以有它們自己的清單,它們從它們自己的特定目錄加載文件。 鏈輪會自動將由application.js加載的文件與特定於頁面的文件結合起來,從而使此解決方案可以正常工作。

這種技術也可以用於style_sheets/


只有在您告訴Rails(鏈接,而不是)將它們合併時,JavaScript才會合併。


很久以前,這已經得到了回答和接受,但是基於這些答案以及我對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佈局文件中,將您的新幫助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>

瞧,現在你可以很容易地創建視圖特定的JavaScript使用相同的文件結構,你在軌道上的其他地方使用。 只需將你的文件粘貼到app/assets/:namespace/:controller/action.js.erb

希望能幫助別人!


您可以在佈局文件(例如application.html.erb)中添加此行以自動加載控制器特定的JavaScript文件(在生成控制器時創建的文件):

<%= javascript_include_tag params[:controller] %>

您還可以添加一行來以每個操作為基礎自動加載腳本文件。

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

只需將頁面腳本放入以控制器名稱命名的子目錄中即可。 在這些文件中,您可以使用= require來包含其他腳本。 創建一個幫助程序來包含文件,如果它存在,這將是很好的,以避免瀏覽器發生404錯誤。


我同意你的回答,檢查選擇器是否在那裡,使用:

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

(沒有看到任何人添加實際的解決方案)


我很欣賞所有的答案......我不認為他們真的在解決問題。 其中一些是關於樣式,似乎不涉及...和其他人只是提到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頁上包含相同的代碼。 實際上,您再也不必在網站上再添加手動腳本標記 - 除了可能預先加載數據。

我認為這是我的問題的實際答案。


我沒有看到一個真正把所有東西放在一起的答案,並為你提供了答案。 因此,我會嘗試把簡單的sujal (一個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!"


簡要描述;簡介

  • 刪除//= require_tree .application.js中只列出每個頁面共享的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] %>做類似的事情,但這超出了問題的範圍。

如果我在這裡錯過了一個明顯的最佳練習,請給我一張便條,然後我會調整conisder。 Rails is fairly new to me and, honestly, I'm not terribly impressed so far with the chaos it brings by default to enterprise development and all the traffic the average Rails program generates.


我看到你已經回答了你自己的問題,但這裡有另一個選擇:

基本上,你正在做這個假設

//= require_tree .

是必須的。 不是。 隨意刪除它。 在我目前的應用程序中,第一個我正在使用3.1.x,我做了三個不同的頂級JS文件。 我的application.js文件只有

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

這樣,我可以創建具有自己的頂級JS文件的子目錄,其中只包含我需要的東西。

關鍵是:

  1. 你可以刪除require_tree - Rails讓你改變它的假設
  2. 名稱application.js沒有什麼特別之處 - assets/javascript子目錄中的任何文件都可以包含預處理器指令,其中//=

希望能幫助ClosureCowboy的答案並增加一些細節。

Sujal


菲利普的回答非常好。 以下是使其工作的代碼:

在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'

資產管道文檔建議如何執行控制器特定的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] %>

鏈接到:asset_pipeline


<%= javascript_include_tag params[:controller] %>




sprockets