Rails 3.1'i kullanarak, “sayfaya özgü” JavaScript kodunuzu nereye koyarsınız?


388

Anladığım kadarıyla, tüm JavaScript'iniz 1 dosyada birleştirilir. Rails, manifest dosyanızın //= require_tree .altına eklendiğinde bunu varsayılan olarak yapar application.js.

Bu gerçek bir hayat kurtarıcıya benziyor, ancak sayfaya özgü JavaScript kodu hakkında biraz endişeliyim. Bu kod her sayfada yürütülüyor mu? İstediğim son şey, tüm nesnelerimin yalnızca 1 sayfada ihtiyaç duyulduğunda her sayfa için somutlaştırılması.

Ayrıca, çakışan kod için de bir potansiyel yok mu?

Yoksa scriptsayfanın altına, sayfanın javascript kodunu yürüten bir yöntemi çağıran küçük bir etiket mi ekliyorsunuz?

O zaman artık requir.js'ye ihtiyacınız yok mu?

Teşekkürler

DÜZENLEME : Tüm cevapları takdir ediyorum ... ve gerçekten sorun yaşadıklarını sanmıyorum. Bazıları stil hakkındadır ve birbirleriyle ilişkili görünmüyor ... ve diğerleri sadece bahsettiğini javascript_include_tag... var olduğunu biliyorum (açıkçası ...) ama ileride Rails 3.1 yolunun hepsini sarmak olduğu anlaşılıyor her sayfanın altına tek tek JavaScript yüklemek yerine JavaScript'inizi 1 dosyaya yerleştirin.

Gelebileceğim en iyi çözüm, divetiketlerde belirli özellikleri ids veya classes ile sarmaktır. JavaScript kodunda, yalnızca idveya classöğesinin sayfada olup olmadığını kontrol edersiniz ve öyleyse, onunla ilişkili JavaScript kodunu çalıştırırsınız. Bu şekilde, dinamik öğe sayfada değilse, JavaScript kodu çalışmaz - application.jsSprockets tarafından paketlenen büyük dosyaya dahil edilmiş olsa bile .

Yukarıdaki çözümüm, 100 sayfanın 8'ine bir arama kutusu eklenirse, yalnızca bu 8 sayfada çalışacağı avantajına sahiptir. Aynı kodu sitedeki 8 sayfaya da eklemeniz gerekmez. Aslında, sitenize bir daha asla manuel komut dosyası etiketleri eklemenize gerek yoktur.

Sanırım sorumun asıl cevabı bu.


11
"Rails 3.1 yolu ileriye her sayfanın altındaki tek tek Javascript yüklemek yerine, tüm Javascript 1 dosyaya sarmak için." - Rails sadece Rails çekirdek ekibi olduğunu ve her zaman, gerçekten kötü olduğunu bilmek JavaScript'i yönetmek için. Küçük dosyalar genellikle daha iyidir (başka yerlerdeki yorumlarıma bakın). JavaScript söz konusu olduğunda, Rails yolu nadiren doğru yoldur (eşek vuruşunu yapan varlık boru hattı ve CoffeeScript'in teşvik edilmesi hariç).
Marnen Laibow-Koser

Her sayfaya sayfaya özel js dosyalarınızı ekleyecek misiniz? Bence bu bir israf, ClosureCowboy'un cevabına daha çok katılıyorum.
gerky

1
Bu soru için kabul edilen cevaba baktınız mı? stackoverflow.com/questions/6571753/…
rassom

1
@DutGRIFF Başka bir deyişle: hayır, bu durumda Rails yollarını yapmak en iyisi değildir (veya en azından her şeyi koymayın application.js) ve aslında sağladığınız referans bunun neden böyle olduğunu gösterir: JS yürütme sürecinin en yavaş kısmı. Birçok küçük dosya bir büyük dosyadan daha önbelleğe alınabilir. O zaman The Unholy Rails halkı, önerilerinin uymaya çalıştıkları ilkelere aykırı olduğunu ve bu nedenle tavsiyelerinin ciddiye alınmaması gerektiğini anlamıyor gibi görünüyor.
Marnen Laibow-Koser

1
@DutGRIFF Hayır, büyük bir JS dosyası önbelleğe alındığında bile normalde iyi bir şey olmaz. Bu sayfadaki başka yerlerdeki yorumlarıma bakın: küçük dosyalar belirli sayfaları daha iyi hedefleyebilir ve daha ayrıntılı bir ayrıntıda önbelleğe alınabilir. Hiçbir sayfaya özel kodu olmadığı sürece tek bir büyük dosya için iyi bir kullanım örneğini görmüyorum hiç .
Marnen Laibow-Koser

Yanıtlar:


157

Asset Pipeline belgeleri, denetleyiciye özgü JS'nin nasıl yapılacağını önerir:

Örneğin, a ProjectsControlleroluşturulursa, adresinde yeni bir dosya ve adresinde app/assets/javascripts/projects.js.coffeebaşka bir dosya olacaktır app/assets/stylesheets/projects.css.scss. Bu dosyalar daha sonra sadece gibi hatları ile bu denetleyicileri için yüklenebilir gibi, kendi varlık dosyaları içinde bir kontrolöre JavaScript veya CSS Benzersiz herhangi koymalıyız <%= javascript_include_tag params[:controller] %>ya <%= stylesheet_link_tag params[:controller] %>.

Bağlantı: asset_pipeline


50
Bunu yapmanın en zarif yolu bu. Ancak, // = requir_tree satırını da kaldırmanız gerekir. from the application.js.coffee
zsljulius

2
Bu yönteme tamamen katılıyorum. Diğer yöntemler çok hantal görünüyor ve yine de dev bir js dosyası yüklemeye devam ediyor. Üzerinde çalıştığım proje, birleştirildikten / küçültüldükten sonra neredeyse 2mb değerinde JS dosyası / eklentisi vb.
Bill Garrison

2
Rails için oldukça yeniyim, ama bana göre bu varsayılan davranış olmalı.
Ross Hambrick

12
Eyleme özgü kontrol için bunu mizanpajımda görüyorum, çünkü her denetleyicinin her eyleminin kendine özgü JS'si yoktur. page_specific_js = "#{params[:controller]}_#{params[:action]}"ve sonra; javascript_include_tag page_specific_js if Rails.application.assets.find_asset page_specific_js
Sujimichi

2
Denetleyiciye özgü eylemler hala küçültülüyor mu? Zincir dişlileri tarafından oluşturulan tek js dosyasına mı eklenirler, yoksa bu, varlık dosyaları için birden fazla talebe yol açar mı?
Jason

77

Sayfaya özgü js için Garber-Irish çözümünü kullanabilirsiniz .

Bu nedenle Rails javascripts klasörünüz iki denetleyici - otomobil ve kullanıcılar için şöyle görünebilir:

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

Ve javascripts şöyle görünecek:

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

ve markup_based_js_execution, UTIL nesnesi ve DOM için hazır UTIL.init yürütmesinde kod içerir.

Ve bunu düzen dosyanıza koymayı unutmayın:

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

Daha data-*iyi sayfaya özgü css için öznitelikler yerine sınıfları kullanmak daha iyi olduğunu düşünüyorum . Jason Garber'in belirttiği gibi: sayfaya özgü CSS seçicileri gerçekten garipleşebilir ( data-*nitelikleri kullandığınızda )

Umarım bu sana yardımcı olmuştur.


4
Kullanıcı denetleyicisindeki tüm eylemler için kullanılabilir, ancak diğer denetleyicilerde bulunmayan bir değişkene ihtiyacınız varsa ne olur? Bu yöntemde bazı kapsam belirleme sorunları yok mu?
tybro0103

@ tybro0103, ben window.varForOneController='val'bu denetleyici init işlevi gibi bir şey yazmak istediğiniz bu davranışı uygulamak düşünüyorum . Ayrıca gon gem burada yardımcı olabilir ( github.com/gazay/gon ). Başka geçici çözümler olabilir.
welldan97

1
@ welldan97 Açıklamanız için değil - mükemmel - ama Garber-İrlandalı yapı kötüdür. Tüm JS'nizi her sayfaya yükler ve işleri halletmek için <body> öğesindeki sınıflara ve kimliklere bağlıdır. Bu DOM ile savaşmanın kesin bir işaretidir: normal şartlar altında <body> öğesinin bir sınıfa veya kimliğe ihtiyacı yoktur, çünkü bir belgede yalnızca bir tane vardır. Bunu yapmanın doğru yolu, //= require_tree .sayfaya özgü JavaScript'i kaldırmak ve kullanmaktır. Aktif olarak bunu yapmamaya çalışıyorsanız, o zaman kötü uygulama için çaba gösteriyorsunuz.
Marnen Laibow-Koser

2
@ MarnenLaibow-Koser Şahsen ben tüm js tek bir dosyada birleştirip en aza indirmek her sayfada tüm js yükleme çoğu proje için iyi olduğuna inanıyorum. Genel olarak kullanıcı için daha hızlı çalıştığına inanıyorum. En azından bir js dosyası vs birçok çatışma gibi (yani stackoverflow.com/questions/555696/… bakın ). Ayrıca, kodu basitleştirir ve sizin için çalışırsa, vücutta sınıfları ve kimlikleri kullanmanın kötü bir yanı yoktur. Modernizr ( modernizr.com ) bunu ve diğer bazı kütüphaneleri de yapar.
welldan97

2
@ MarnenLaibow-Koser raylar varlık boru hattı, bana göre, derleme ile karşılaştırmak için iyi bir aday gibi görünüyor. Bir programcı, javascriptlerini güzel ayrılmış modüllere yazar ve daha sonra birlikte toplanır, küçültülür ve servis edilir. Derlenmiş dillerde olduğu gibi, derleyiciden bir adım önde olduklarını düşünen programcılar her zaman olacaktır ... ama bence bu nadiren doğrudur.
Ziggy

65

Kendi sorunuzu cevapladığınızı görüyorum, ancak işte başka bir seçenek:

Temel olarak,

//= require_tree .

gerekli. Değil. Kaldırmaktan çekinmeyin. Mevcut uygulamamda, 3.1.x ile dürüstçe ilk yaptığım, üç farklı üst düzey JS dosyası yaptım. Dosyamda application.jsyalnızca

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

Bu şekilde, kendi üst düzey JS dosyaları ile yalnızca ihtiyacım olanı içeren alt dizinler oluşturabilirim.

Anahtarlar:

  1. Sen kaldırabilirsiniz require_treeRaylar bunu yapar varsayımları değiştirmenizi sağlar -
  2. Adla ilgili özel bir şey yok application.js- assets/javascriptalt dizindeki herhangi bir dosya,//=

Bu yardımcı olur ve ClosureCowboy'un cevabına bazı ayrıntılar ekler.

Sujal


8
+1 Bu benim gibi bir acemi için bilmek harika. Yapabilirsem +2 verirdim.
jrhorn424 17:12

5
@sujal Kesinlikle. Rails çekirdek ekibi, uçsuz bucaksız JavaScript yönetimi konusunda kötü üne sahiptir. Onların önerilerini göz ardı etmekten çekinmeyin ve sadece varlık boru hattının iyi kısımlarını kullanın . :)
Marnen Laibow-Koser

1
Bu tavsiye için çok teşekkürler. Uygulamamın modülüne bağlı olarak birkaç "üst düzey" JS dosyam yok. İyi çalışıyor.
elsurudo

1
+1 Burada benim için önemli olan, mevcut tüm dosyaları oldukları yerde tutabilmeniz ve sayfaya özgü dosyalar için yeni dizinler oluşturabilmeniz için //= require_tree .bununla değiştirebilmenizdir //= require_directory ..
zelanix

41

Başka bir seçenek: sayfaya veya modele özgü dosyalar oluşturmak için assets/javascripts/klasörünüzün içinde dizinler oluşturabilirsiniz .

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

Ana application.jsbildirim dosyanız, dosyalarını yükleyecek şekilde yapılandırılmış olabilir global/. Belirli sayfaların veya sayfa gruplarının kendi dizinlerinden dosya yükleyen kendi bildirimleri olabilir. Sprockets, yüklenen dosyaları otomatik olarak application.jssayfaya özgü dosyalarınızla birleştirerek bu çözümün çalışmasını sağlar.

Bu teknik de kullanılabilir style_sheets/.


13
Beni şimdi cupcakes istemiştin .. Dangit!
Chuck Bergeron

Bu çözümü gerçekten çok seviyorum. Bununla ilgili tek sorun, bu ekstra tezahürlerin sıkıştırılmış / çirkin olmamasıdır. Gerçi uygun şekilde derlenmişlerdir. Bir çözüm var mı yoksa bir şey mi kaçırıyorum?
clst

1
Bu, tarayıcının bir js dosyası yüklediği, yani global + sayfaya özgü dosyanın bir kombinasyonu olduğu anlamına mı geliyor?
lulalala

Varsa soruma bir göz atabilir misiniz? stackoverflow.com/questions/17055213/…
Maximus S

1
@clst Aradığınız yanıtın bu olduğuna inanıyorum: guides.rubyonrails.org/asset_pipeline.html#precompiling-assets
FrontierPsycho

23

Tüm cevapları takdir ediyorum ... ve gerçekten sorun yaşadıklarını sanmıyorum. Bazıları stil hakkındadır ve birbirleriyle ilişkili görünmüyor ... ve diğerleri sadece bahsettiğini javascript_include_tag... var olduğunu biliyorum (açıkçası ...) ama ileride Rails 3.1 yolunun hepsini sarmak olduğu anlaşılıyor her sayfanın alt kısmına tek tek Javascript yüklemek yerine Javascript'inizi 1 dosyaya yükleyin.

Gelebileceğim en iyi çözüm, divetiketlerde belirli özellikleri ids veya classes ile sarmaktır. Javascript kodunda. Sonra sadece idya classda sayfada olup olmadığını kontrol edin ve varsa, onunla ilişkili javascript kodunu çalıştırın. Bu şekilde, dinamik öğe sayfada değilse, JavaScript kodu, application.jsSprockets tarafından paketlenen büyük dosyaya dahil edilmiş olsa bile çalışmaz .

Yukarıdaki çözümüm, 100 sayfanın 8'ine bir arama kutusu eklenirse, yalnızca bu 8 sayfada çalışacağı avantajına sahiptir. Aynı kodu sitedeki 8 sayfaya da eklemeniz gerekmez. Aslında, belki önceden önceden yüklenmiş veriler dışında, sitenize hiçbir zaman manuel komut dosyası etiketleri eklemenize gerek yoktur.

Sanırım sorumun asıl cevabı bu.


Ama aslında bu manuel <script>etiketleri istiyorsunuz . Evet, sınıflar ve kimlikler yanıtın bir parçasıdır, ancak kullanıcının belirli bir sayfanın gerektirmediği JavaScript'i yüklemesi mantıklı değildir.
Marnen Laibow-Koser

4
@ MarnenLaibow-Koser, her benzersiz sayfaya manuel komut dosyası etiketi eklememenin nedeni, bu komut dosyası içeriğini her sayfa görünümünde indirmeniz gerektiğidir. Varlık boru hattını kullanarak tüm javascript'i application.js içine paketleyebiliyorsanız, kullanıcı bu komut dosyalarını yalnızca bir kez indirir ve sonraki tüm sayfa yüklemelerinde önbellekten application.js dosyasını çeker
jakeonrails 9:13

@jakeonrails "her benzersiz sayfaya manuel komut dosyası etiketi eklememenin nedeni, bu komut dosyası içeriğini her sayfa görünümünde indirmeniz gerektiğidir" - oldukça yanlış. Komut dosyası bir kez indirilecek, daha sonra başka isteklerde tarayıcının önbelleğinden alınacaktır. "Tüm javascript'i varlık pipeline'ı kullanarak application.js içine paketleyebiliyorsanız, kullanıcı bu komut dosyalarını yalnızca bir kez indirir" - doğru, ancak çok sayıda gereksiz kod pahasına. JS'nizi büyük bir dosya yerine çok sayıda küçük dosya halinde yapılandırabilirseniz, gereksiz kodlar olmadan önbellekleme avantajları elde edersiniz.
Marnen Laibow-Koser

1
@ MarnenLaibow-Koser Her şeyi tek bir komut dosyasına paketlerseniz, kullanıcılarınızın sitenizin herhangi bir sayfası için yalnızca 1 komut dosyası indirmesi gerektiğini söylemenin daha iyi olacağını düşünüyorum. Uygulamanızın farklı bölümleri için birden fazla komut dosyanız varsa, açıkçası kullanıcının birden fazla komut dosyası indirmesi gerekir. Her iki yöntem de elbette önbelleğe alınacak, ancak çoğu durumda (küçük-orta ölçekli uygulamalar), bir kez bir application.js'ye hizmet vermek indirme için daha verimli olacaktır. Hizmet verdiğinize bağlı olarak JS'nin ayrıştırılması başka bir hikaye olabilir.
jakeonrails

1
@Ziggy Ayrıca, küçük dosya yalnızca 100 sayfadan 8'inde kullanılıyorsa, kod neden kullanıcının önbelleğinde sürekli dursun? Aslında gerekli olmayan şeyleri bırakmak daha iyi.
Marnen Laibow-Koser

16

Bu partiye biraz geç geldiğimin farkındayım, ama son zamanlarda kullandığım bir çözümü atmak istedim. Ancak, önce bahsetmeme izin verin ...

Rails 3.1 / 3.2 Yollu (Hayır, efendim. Sevmiyorum.)

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

Bu cevabın bütünlüğü uğruna aşağıdakileri ekliyorum ve bunun kaçınılmaz bir çözüm olmadığı için ... bununla pek ilgilenmiyorum.

"Rails Way", bu sorunun asıl yazarı olarak görünüme yönelik olmaktan ziyade denetleyici odaklı bir çözümdür. İlgili denetleyicilerinden sonra denetleyiciye özgü JS dosyaları vardır. Bu dosyaların tümü, application.js gerektiren yönergelerin hiçbirine varsayılan olarak dahil OLMAYAN bir klasör ağacına yerleştirilir.

Denetleyiciye özel kod eklemek için bir görünüme aşağıdakiler eklenir.

<%= javascript_include_tag params[:controller] %>

Bu çözümden nefret ediyorum, ama orada ve hızlı. Muhtemelen, bu dosyaları "people-index.js" ve "people-show.js" gibi "#{params[:controller]}-index"bir şey olarak adlandırabilir ve sonra görünüm odaklı bir çözüm elde etmek için benzer bir şey kullanabilirsiniz . Yine, hızlı düzeltme, ama benimle iyi oturmuyor.

Veri Öznitelik Yolu

Beni deli olarak ara, ama konuştuğumda TÜM JS'mın application.js içine derlenmesini ve küçültülmesini istiyorum. Bu küçük straggler dosyalarını her yere dahil etmeyi hatırlamak istemiyorum.

Tüm JS'mi kompakt, yakında tarayıcı önbelleğe alınmış bir dosyaya yüklüyorum. Application.js dosyamın belirli bir parçasının bir sayfada tetiklenmesi gerekiyorsa, HTML’nin bana Rails’i söylemesine izin verdim.

JS'imi belirli öğe kimliklerine kilitlemek veya HTML'yi işaretleyici sınıflarıyla doldurmak yerine, adlı özel bir veri özniteliği kullanıyorum data-jstags.

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

Her sayfada, DOM yüklemesi bittiğinde kodu çalıştırmak için - burada tercih edilen JS kitaplığı yöntemini ekle - kullanıyorum . Bu önyükleme kodu aşağıdaki eylemleri gerçekleştirir:

  1. İle işaretlenen DOM'daki tüm öğeler üzerinde yineleme yapın data-jstag
  2. Her öğe için, nitelik değerini boşlukta bölün ve bir dizi etiket dizesi oluşturun.
  3. Her bir etiket dizesi için, o etiket için bir Kare'de bir arama yapın.
  4. Eşleşen bir anahtar bulunursa, öğeyi parametre olarak ileterek kendisiyle ilişkilendirilmiş işlevi çalıştırın.

Yani diyelim ki application.js dosyamda bir yerde aşağıdakiler tanımlanmış:

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 olayı, my_autosuggest_initve my_hint_initgirişlerini arama girişine uygulayacak ve kullanıcı yazarken bir öneri listesi görüntüleyen bir giriş haline getirecek ve giriş boş bırakıldığında ve odaklanmadığında bir tür giriş ipucu sağlayacaktır.

Bazı öğeler etiketlenmedikçe data-jstag="auto-suggest", otomatik öneri kodu hiçbir zaman tetiklenmez. Ancak, her zaman orada, küçültülmüş ve sonunda bir sayfada ihtiyaç duyduğum zamanlar için application.js dosyasında önbelleğe alınmış.

Etiketli JS işlevlerinize ek parametreler iletmeniz gerekiyorsa, biraz yaratıcılık uygulamanız gerekir. Ya veri parametresi nitelikleri ekleyin, bir tür parametre sözdizimi oluşturun, hatta karma bir yaklaşım kullanın.

Denetleyiciye özgü görünen karmaşık bir iş akışım olsa bile, sadece lib klasörümde bir dosya oluşturacağım, application.js içine paketleyeceğim ve 'yeni bir şey sihirbazı' gibi bir şeyle etiketleyeceğim. Bootstrap'ım bu etikete çarptığında, güzel, süslü sihirbazım örneklenecek ve çalıştırılacak. Gerektiğinde o denetleyicinin görünümleri için çalışır, ancak denetleyiciye başka şekilde bağlanmamıştır. Aslında, sihirbazımı doğru kodlarsam, görünümlerde tüm yapılandırma verilerini sağlayabilir ve bu nedenle sihirbazımı daha sonra ihtiyacı olan diğer denetleyiciler için yeniden kullanabilirim.

Her neyse, bir süredir sayfaya özel JS uyguluyorum ve hem basit site tasarımları hem de daha karmaşık / zengin uygulamalar için bana iyi hizmet etti. Umarım burada sunduğum iki çözümden biri, benim yolum ya da Rails yolu, gelecekte bu soruya rastlayan herkes için yararlıdır.


6
Küçük bir detay: Cevabınızda js tarayıcı önbelleğe alındığında, bunun bir etkisi olmadığını düşünüyoruz. Bu tam olarak doğru değil. Tarayıcı, js dosyası düzgün bir şekilde önbelleğe alınırsa, indirmeyi gerçekten engellemez, ancak yine de her sayfadaki kodu derler . Yani, dengeleri dengelemek zorundasınız. Toplamda çok fazla JS varsa, ancak sayfa başına yalnızca bir kısmı kullanılıyorsa, JS'yi parçalayarak sayfa sürelerini iyileştirebilirsiniz.
sujal

Bahsettiğim o derleme aşamasında pratik etkileri hakkında daha fazla bilgi için Basecamp Sonraki pjax etkiledi nasıl 37 İşaret açıklamasını bakın: 37signals.com/svn/posts/...
Sujal

Bu adil bir nokta. Makaleyi okuduktan ve yukarıdaki çözümü kullandığım projelere baktıktan sonra, makalede bahsettikleri aynı "değiştirilmiş HTML'yi gönder" çözümünü yazdığımı fark ettim. JS'nin sık sık yeniden derlenmesi bu yüzden projelerimde bir sorun olmamıştı. Derleme adımı, daha az "masaüstü uygulaması" odaklı sitelerde çalışırken aklımda tutacağım bir şeydir.
Ryan

2
"Beni deli et, ama konuştuğumda TÜM JS'mın application.js'de derlenmesini ve küçültülmesini istiyorum." Bunu gerçekten istemezsiniz, çünkü kullanıcı ihtiyaç duymadığı JavaScript'i yükler ve işleyicilerinizin orada olmayacak nitelikleri aramasını sağlar. App.js'de her şeye sahip olmak caziptir ve Rails kesinlikle bunu kolaylaştırır, ancak yapılacak doğru şey JavaScript'i daha iyi modüle etmektir.
Marnen Laibow-Koser

Farklı bir fikre hak kazanıyorsunuz ... ve teknik olarak bir fikir farklılığını azaltma hakkına sahipsiniz. Ancak, büyük ve önbelleğe alınmış bir dosyanın modülerize JS'yi almak için birden çok HTTP isteğini neden zorlamakla ilgili bazı gerekçeler görmek güzel olurdu. Ayrıca, işleyici arama prosedürü ile ilgili yanılıyorsunuz. Etiket değerleri aranmaz. Şimdiye kadar yalnızca bir arama yapılır ve data-jstag özniteliğine sahip tüm öğeleri çeker. Etiket adına göre arama yapmaz, yalnızca etiketleri olan tüm öğeleri bulur ve yalnızca gerekli nesneleri başlatır.
Ryan

7

Bu uzun zaman önce cevaplandı ve kabul edildi, ancak bu cevapların bazılarına ve Rails 3+ ile yaşadığım deneyime dayanarak kendi çözümümü buldum.

Varlık boru hattı tatlı. Kullanın.

İlk olarak, application.jsdosyanızda kaldırın//= require_tree.

Sonra application_controller.rbbir yardımcı yöntem oluşturun:

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

Ardından, application.html.erbdüzen dosyanızda, yeni yardımcıyı yardımcıyla birlikte gelen mevcut javascript içeriklerinin arasına ekleyin raw:

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

Voila, şimdi raylarda başka her yerde kullandığınız dosya yapısını kullanarak görünüme özgü javascript oluşturabilirsiniz. Sadece dosyalarınızı yapıştırın app/assets/:namespace/:controller/action.js.erb!

Umarım başka birine yardım eder!


1
Varlıklar önceden derlendikten sonra bu soruna neden olmaz ve çalışma zamanında <%= raw ... %>404 döndürür?
Nishant

Varlık boru hattının tatlı olmadığını düşünüyorum, çünkü genellikle kullanılmaması gereken bir grup dosya oluşturuyor. Yani benim için varlık boru hattına güvenmek verimsiz bir sisteme bağımlılık yaratmak.
Deborah

1
@DeborahSpeece Varlık boru hattı, kullanılmaması gereken dosyaları ne zaman oluşturur? Varlık boru hattını (iyi) require_tree /(kötü) ile karıştırıyor musunuz ?
Marnen Laibow-Koser

6

Denetleyiciye özgü javascript dosyasını (denetleyiciyi oluştururken oluşturduğunuz) otomatik olarak yüklemek için bu satırı düzen dosyanıza (örn. Application.html.erb) ekleyebilirsiniz:

<%= javascript_include_tag params[:controller] %>

Ayrıca, komut dosyası dosyasını işlem başına otomatik olarak yüklemek için bir satır da ekleyebilirsiniz.

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

Sayfa komut dosyalarınızı denetleyici adından sonra adlandırılan bir alt dizine koyun. Bu dosyalara = gerektiren kullanarak başka komut dosyaları da ekleyebilirsiniz. Tarayıcıda 404'ün başarısız olmasını önlemek için dosyayı yalnızca varsa eklemek için bir yardımcı oluşturmak iyi olurdu.


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

2
Bu soruya cevap verebilecek gibi görünüyor. Cevabı bulmak için lütfen cevaba daha fazlasını ekler misiniz?


5

LoadJS taş başka bir seçenektir:

LoadJS, Sprockets tarafından sağlanan sihri kaybetmeden Rails uygulamasına sayfaya özgü Javascript kodunu yüklemek için bir yol sağlar. Tüm Javascript kodunuz bir Javascript dosyasında simge durumuna küçültülerek devam eder, ancak bazı bölümleri yalnızca belirli sayfalar için yürütülür.

https://github.com/guidomb/loadjs


3

Philip'in cevabı oldukça iyi. İşte çalışması için kod:

Application.html.erb dosyasında:

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

Kumandanızın Projeler olarak kabul edildiğini varsayarsak, aşağıdakileri üretir:

<body class="projects">

Sonra projelerde. Js.kahve:

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

Downvoting: Herhangi bir çözüm üzerinde koyar bir sınıf olduğunu <body>olduğunu sırf bunun yanlış. Bu sayfada başka bir yerde yorumlarıma bakın.
Marnen Laibow-Koser

Bunu yapma. Buradaki sorun, bunlardan birini her eklediğinizde, sayfa yüklemesinde yürütülmesi gereken başka bir js parçası eklemenizdir. Projeniz büyüdükçe kesinlikle performans düşüşüne neden olabilir.
ifightcrime

2

JavaScripts yalnızca Rails (Sprockets) öğelerini birleştirmelerini söylediğinizde birleştirilir.


Elbette. Sanırım soruyorum çünkü Rails'in varsayılanları klasördeki her şeyi içeriyor ... Bu, David'in bunu yapmayı planladığı anlamına geliyor. Ama @rubyprince'ın diğer yorumunda söylediğim gibi, bu şekilde yapıldığında yürütme konusunda emin değilim. Devre dışı bırakmam gerektiğini //= require_tree .mi düşünüyorum ?
Yangın Amblemi

@FireEmblem Evet. require_tree .genellikle kötü bir fikirdir.
Marnen Laibow-Koser

2

Stil sorununu şu şekilde çözdüm : (Haml'i affedin)

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

Bu şekilde tüm sayfaya özgü .css.sass dosyalarını şu şekilde başlatırım :

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

Bu şekilde herhangi bir çatışmayı kolayca önleyebilirsiniz. .Js.coffee dosyaları söz konusu olduğunda ;

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

Umarım bu yardımcı oldu.


1
Lütfen son biti tekrar okuyun, javascript için, görünüme özgü işlevleri başlatmak için stil sayfaları için kullanılan aynı yapıdan yararlanabilirsiniz.
Zeeraw

Philip, $('#post > #edit') ->geçersiz gibi görünüyor. JQuery'yi bir kapsamda nasıl çalıştırabilirsiniz?
Ramon Tayag

2
Son zamanlarda, bu uygulamaya application.html.haml çağırarak denetleyiciye özgü java komut dosyalarını ve stil sayfalarını yüklemeye başladım; = javascript_include_tag "application"ve = javascript_include_tag params[:controller]bu şekilde dosyanın içinde bir kapsam belirtmek zorunda kalmadan javascript kodu sınırlı tutabilirsiniz.
zeeraw


2

Cevabınızı kabul ediyorum, bu seçicinin orada olup olmadığını kontrol etmek için şunu kullanın:

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

(kimsenin gerçek çözümü eklediğini görmedim)


2

Hepsini bir araya getiren ve sizin için ortaya koyan bir cevap görmüyorum. Böylece, meleyal , sujal (a la ClosureCowboy ), Ryan'ın cevap ve hatta Gal kısa ve net bir şekilde ... backbone.js hakkında tüm buluşmanızı cesur açıklama. Ve kim bilir, Marnen Laibow-Koser'in gereksinimlerini bile karşılayabilirim .

Örnek düzenlemeler

varlıklar / javascripts / application.js

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


görüntüleme / düzenleri / 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>


görüntüleme / foo / index.html.erb

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


varlıklar / javascripts / foo.js

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


varlıklar / javascripts / foostuff / foothis.js.coffee

alert "Hello world!"


Kısa açıklama

  • Kaldır //= require_tree .danApplication.js ve yalnızca her sayfanın paylaştığı JS'yi listeleyin.

  • Yukarıda gösterilen iki çizgi application.html.erb dosyasında sayfaya application.js'yi ve sayfaya özgü JS'nizi nereye ekleyeceğinizi anlatır.

  • Yukarıda gösterilen üç çizgi index.html.erb dosyasında , görünümünüze sayfaya özgü bazı JS'ler aramasını ve bunu ": javascript" (veya adlandırmak istediğiniz herhangi bir adla) adlı adlandırılmış bir verim bölgesine eklemesini söyler. Bu örnekte, denetleyici "foo" olduğundan Raylar uygulama düzeninde: javascript aktarım bölgesine "foo.js" eklemeye çalışacaktır.

  • Sayfanıza özgü JS'yi foo.js'de listeleyin (veya denetleyicinin adı ne olursa olsun . Ortak kütüphaneleri, bir ağacı, dizinleri, her neyse listele.

  • Özel sayfaya özgü JS'nizi, diğer özel JS'niz dışında kolayca başvurabileceğiniz bir yerde tutun. Bu örnekte, foo.js foostuff ağacını gerektirir, bu nedenle foothis.js.coffee gibi özel JS'nizi oraya koyun .

  • Burada zor kurallar yok. Bir şeyleri hareket ettirmekten çekinmeyin ve hatta gerekirse çeşitli düzenlerde çeşitli adlardan oluşan birden fazla verim bölgesi oluşturabilirsiniz. Bu sadece bir olası ilk adımı gösterir. (Backbone.js kullanımımız göz önüne alındığında tam olarak böyle yapmıyorum. Ayrıca foo.js dosyasını foostuff yerine foo adlı bir klasöre bırakmayı da seçebilirim ama henüz karar vermedim.)

notlar

CSS ile benzer şeyler yapabilirsiniz, <%= stylesheet_link_tag params[:controller] %>ancak bu sorunun kapsamı dışındadır.

Burada göze çarpan en iyi uygulamaları kaçırırsam, bana bir not gönderin ve uyarlamayı değiştireceğim. Raylar benim için oldukça yeni ve dürüst olmak gerekirse, kurumsal gelişime ve ortalama Rails programının oluşturduğu tüm trafiğe varsayılan olarak getirdiği kaostan şimdiye kadar çok etkilendim.


Bu, gitme yoluna benziyor, kendi uygulamamda uygulayıp uygulayamayacağımı göreceğim, ayrıntılı cevap için teşekkürler.
martincarlin87

1

İlkel benim için iyi çalışıyor ve herhangi bir fantezi seçici yükleme stratejisine ihtiyaç duymasa da başka bir çözümüm var. Nornal belgenize hazır fonksiyonunuzu koyun, ancak javascriptinizin amaçlandığı sayfa olup olmadığını görmek için geçerli windows konumunu test edin:

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

Bu, tüm j'lerin 3.x raylar tarafından küçük bir pakette yüklenmesine izin verir, ancak çok fazla yük veya js'nin amaçlanmadığı sayfalarla herhangi bir çakışma oluşturmaz.


1

ryguy'un cevabı, olumsuz noktalara indirilmiş olmasına rağmen iyi bir cevaptır.

Özellikle Backbone JS gibi bir şey kullanıyorsanız - her sayfanın kendi Omurga görünümü vardır. Daha sonra erb dosyası sadece sağ omurga görünüm sınıfını başlatan tek bir satır içi javascript satırına sahiptir. Tek bir 'tutkal kodu' satırı ve bu nedenle satır içi iyi olduğunu düşünüyorum. Avantajı, tarayıcının tüm javascript'i önbelleğe almasını sağlayan "requir_tree" nizi saklayabilmenizdir.

show.html.erb'de şöyle bir şey olacak:

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

ve düzen dosyanızda şunlara ihtiyacınız olacak:

<%= yield :javascript %>

Downvoting. Satır içi JavaScript asla iyi bir fikir değildir. Tutkal kodu olsa bile, harici bir dosyada olmalıdır.
Marnen Laibow-Koser

1

Tüm commom JS dosyalarınızı 'app / asset / javascript / global' gibi bir alt klasöre taşıyın ve application.js dosyasında //= require_tree .satırı değiştirin //= require_tree ./global.

Artık denetleyiciye özgü JS'nizi 'app / asset / javascript /' köküne koymakta özgürsünüz ve bunlar sadece = javascript_include_tagdenetleyiciniz / görünümünüzde aradığınızda kullanılan derlenmiş JS'ye dahil edilmeyecektir .


Hiçbir şekilde, bu bir sayfa için yüklenecek bir JavaScript yükü. Önbelleğe alınmış olması bile önemli değil.
jackyalcine

1

Burada birkaç cevabınız olsa da, düzenlemenizin muhtemelen en iyi bahis olduğunu düşünüyorum. Ekibimizde Gitlab'dan aldığımız tasarım deseni Dispatcher deseni. Bahsettiğinize benzer bir şey yapar, ancak sayfa adı gövde etiketinde raylarla ayarlanır. Örneğin, düzen dosyanıza (HAML'de) gibi bir şey ekleyin:

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

Sonra sadece bir kapatma ve dispatcher.js.coffeejavascripts klasörünüzdeki dosyanızda bir switch deyimi var :

$ ->
  new Dispatcher()

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

Tek tek dosyalarda (örneğin products.js.coffeeya da login.js.coffeeörneğin) tek yapmanız gereken, bunları bir sınıfa dahil etmek ve daha sonra dağıtım programında erişebilmek için bu sınıf sembolünü küreselleştirmek:

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

Gitlab'ın merak etmen için etrafta dolaşmak isteyebileceğin birkaç örneği var :)


1

Paloma projesi, sayfaya özgü javascript kodunu yönetmek için ilginç bir yaklaşım sunar.

Dokümanlarından kullanım örneği:

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

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

1

Aşama 1. requir_tree öğesini kaldırın. application.js ve application.css dosyalarınızda.

Adım 2. Düzen klasöründe application.html.erb (varsayılan olarak raylara göre) düzenleyin. Aşağıdaki etiketlere "params [: controller]" ekleyin.

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

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

Aşama 3. Config / initializers / asset.rb dosyasına dosya ekleme

%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

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


Bu teorik olarak soruyu cevaplayabilirken, cevabın temel kısımlarını buraya dahil etmek ve referans için bağlantı sağlamak tercih edilir.
Bhargav Rao

0

Bunu denemedim, ancak aşağıdaki gibi görünüyor:

  • javascript (örneğin içinde gerçek javascript ile) olan bir content_for'unuz varsa, dişliler bunu bilmezler ve bu da şu anki gibi çalışır.

  • bir dosyayı büyük javascript paketinden hariç tutmak istiyorsanız, config / sprockets.yml dosyasına gider ve kaynak_dosyalarını buna göre değiştirirsiniz. Ardından, gerektiğinde hariç tuttuğunuz tüm dosyaları dahil edersiniz.


Dosyaları hariç tutmak veya sayfanın kendisinde özel javascript kullanmak "doğru yol" mu? Davut insanların bunu böyle mi kullanmasını istiyordu?
Yangın Amblemi

@FireEmblem David'in ne istediğini pek umursamıyorum, çünkü David'in JavaScript'i nasıl düzgün bir şekilde organize edeceğini anladığını sanmıyorum.
Marnen Laibow-Koser


0

Bazı cevapları şöyle birleştirdim:

Uygulama yardımcısı:

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

düzenleri / 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  

0

İlk olarak: \\=require_treeapplication.js adresinden kaldırın İkincisi: tüm JS kodunuzun yeri /app/assets/javascritptve tüm CSS kodunuzun yeri/app/assets/stylesheets


-2

Ryan'ın liderliğini takiben, işte yaptığım şey-

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 (denetleyiciye özgü kahve, örneğin denetleyici: kullanıcılar, eylem: gösterge tablosu)

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

Downvoting. Bu, gülünç evalderecede kıvrıktır - HTML'nizin çatlamış bir sunucu veya kötü amaçlı bir kullanıcı metni tarafından ele geçirilmesi durumunda güvensizlikten (nedeniyle ) bahsetmiyorum.
Marnen Laibow-Koser

-3

Özellikle sayfanız için tonlarca kitaplık yürütmeniz gerekmiyorsa, ancak yalnızca birkaç yüzlerce JS satırını az ya da çok çalıştırmak için bunu nasıl yapacağınız aşağıda açıklanmıştır.

Javascript kodunu HTML'ye gömmek gayet iyi olduğundan, app / views shared.js dizini altında oluşturun ve sayfanıza / sayfalarınıza özel kodumu my_cool_partial.html.erb içine yerleştirin

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

Şimdi istediğiniz her yerden yapabilirsiniz:

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

Ve bu kadar, k?


2
Downvoting. Satır içi JavaScript asla önerilmez. HTML yalnızca biçimlendirme içermelidir. JS ve CSS ayrı, yeniden kullanılabilir dosyalarda olmalıdır.
Marnen Laibow-Koser
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.