Menggunakan Rails 3.1, di mana Anda meletakkan kode JavaScript "spesifik halaman" Anda?

388

Setahu saya, semua JavaScript Anda digabung menjadi 1 file. Rails melakukan ini secara default ketika menambahkan //= require_tree .ke bagian bawah application.jsfile manifes Anda .

Ini terdengar seperti penyelamat nyata, tapi saya sedikit khawatir tentang kode JavaScript khusus halaman. Apakah kode ini dijalankan pada setiap halaman? Hal terakhir yang saya inginkan adalah semua objek saya akan dipakai untuk setiap halaman ketika mereka hanya diperlukan pada 1 halaman.

Juga, bukankah ada potensi untuk kode yang berbenturan juga?

Atau apakah Anda meletakkan scripttag kecil di bagian bawah halaman yang hanya memanggil metode yang mengeksekusi kode javascript untuk halaman?

Apakah Anda tidak lagi memerlukan require.js?

Terima kasih

EDIT : Saya menghargai semua jawaban ... dan saya tidak berpikir mereka benar-benar mengerti masalahnya. Beberapa dari mereka adalah tentang gaya dan tampaknya tidak berhubungan ... dan yang lain hanya menyebutkan javascript_include_tag... yang saya tahu ada (jelas ...) tetapi akan terlihat bahwa Rails 3.1 cara maju adalah untuk membungkus semua JavaScript Anda menjadi 1 file daripada memuat individu JavaScript di bagian bawah setiap halaman.

Solusi terbaik yang dapat saya temukan adalah dengan membungkus fitur-fitur tertentu dalam divtag dengan ids atau classes. Dalam kode JavaScript, Anda hanya memeriksa apakah idatau classada di halaman, dan jika itu adalah, Anda menjalankan kode JavaScript yang terkait dengan itu. Dengan cara ini jika elemen dinamis tidak ada di halaman, kode JavaScript tidak berjalan - meskipun sudah termasuk dalam masifapplication.js file dikemas oleh Sprockets.

Solusi saya di atas memiliki manfaat bahwa jika kotak pencarian disertakan pada 8 dari 100 halaman, itu hanya akan berjalan pada 8 halaman tersebut. Anda juga tidak perlu memasukkan kode yang sama pada 8 halaman di situs. Bahkan, Anda tidak perlu memasukkan tag skrip manual di situs Anda di mana pun lagi.

Saya pikir ini adalah jawaban aktual untuk pertanyaan saya.

Emblem Api
sumber
11
"Cara Rails 3.1 maju adalah untuk membungkus semua Javascript Anda menjadi 1 file daripada memuat Javascript individu di bagian bawah setiap halaman." - Hanya karena tim inti Rails adalah, dan selalu, benar-benar buruk mengetahui bagaimana untuk mengelola JavaScript. File kecil umumnya lebih baik (lihat komentar saya di tempat lain). Ketika datang ke JavaScript, cara Rails jarang cara yang benar (kecuali untuk pipa aset, yang menendang pantat, dan dorongan dari CoffeeScript).
Marnen Laibow-Koser
Jadi Anda akan memasukkan file js khusus halaman Anda pada setiap halaman? Saya pikir itu sia-sia, saya lebih setuju dengan jawaban ClosureCowboy.
Gerky
1
Apakah Anda sudah melihat jawaban yang diterima untuk pertanyaan ini? stackoverflow.com/questions/6571753/…
rassom
1
@ DUTGRIFF Dengan kata lain: tidak, itu tidak terbaik untuk melakukan hal-hal dengan cara Rails dalam kasus ini (atau setidaknya, jangan memasukkan semuanya ke dalam application.js), dan pada kenyataannya referensi yang Anda berikan menunjukkan mengapa demikian: mengunduh adalah bagian paling lambat dari proses eksekusi JS. Banyak file kecil lebih dapat di-cache daripada yang besar. Orang-orang Unholy Rails tampaknya tidak menyadari, kemudian, bahwa rekomendasi mereka tidak konsisten dengan prinsip-prinsip yang mereka coba patuhi, dan oleh karena itu rekomendasi mereka tidak boleh dianggap serius.
Marnen Laibow-Koser
1
@ DutGRIFF Tidak, file JS besar biasanya tidak akan menjadi hal yang baik bahkan setelah di-cache. Lihat komentar saya di bagian lain halaman ini: file kecil dapat menargetkan halaman tertentu dengan lebih baik, dan dapat di-cache di granularity yang lebih baik. Saya tidak melihat adanya kasus penggunaan yang baik untuk satu file besar kecuali jika tidak ada kode khusus halaman sama sekali .
Marnen Laibow-Koser

Jawaban:

157

Dokumen Asset Pipeline menyarankan cara melakukan JS khusus pengontrol:

Misalnya, jika a ProjectsControllerdibuat, akan ada file baru di app/assets/javascripts/projects.js.coffeedan yang lain di app/assets/stylesheets/projects.css.scss. Anda harus meletakkan setiap JavaScript atau CSS unik ke pengontrol di dalam file aset masing-masing, karena file-file ini kemudian dapat dimuat hanya untuk pengontrol ini dengan garis-garis seperti <%= javascript_include_tag params[:controller] %>atau <%= stylesheet_link_tag params[:controller] %>.

Tautan ke: asset_pipeline

meleyal
sumber
50
Ini adalah cara paling elegan untuk melakukannya. Tetapi juga, Anda harus menghapus baris // = require_tree. dari application.js.coffee
zsljulius
2
Saya sangat setuju dengan metode ini. Metode lain tampak sangat kikuk dan akhirnya memuat file js raksasa. Proyek yang saya kerjakan memiliki hampir 2mb file JS / plugins dll SETELAH digabungkan / diperkecil.
Bill Garrison
2
Saya cukup baru di Rails, tetapi bagi saya sepertinya ini adalah perilaku default.
Ross Hambrick
12
Untuk kontrol tindakan khusus, saya memiliki ini di tata letak saya, karena tidak setiap tindakan untuk setiap kontroler memiliki JS tertentu. page_specific_js = "#{params[:controller]}_#{params[:action]}"lalu; javascript_include_tag page_specific_js if Rails.application.assets.find_asset page_specific_js
Sujimichi
2
Apakah tindakan spesifik pengontrol masih dapat diperkecil? Apakah mereka ditambahkan ke file js tunggal yang dibuat oleh sprocket, atau ikuti petunjuk ini ke beberapa permintaan file aset?
Jason
77

Untuk js khusus laman, Anda dapat menggunakan solusi Garber-Irish .

Jadi folder javascripts Rails Anda mungkin terlihat seperti ini untuk dua pengontrol - mobil dan pengguna:

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

Dan javascripts akan terlihat seperti ini:

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

dan markup_based_js_execution akan berisi kode untuk objek UTIL, dan pada eksekusi UTIL.init yang siap DOM.

Dan jangan lupa letakkan ini di file layout Anda:

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

Saya juga berpikir bahwa lebih baik menggunakan kelas daripada data-*atribut, untuk css spesifik halaman yang lebih baik. Seperti yang disebutkan Jason Garber: penyeleksi CSS khusus laman dapat menjadi sangat aneh (ketika Anda menggunakan data-*atribut)

Saya harap ini akan membantu Anda.

welldan97
sumber
4
Bagaimana jika Anda memerlukan variabel yang tersedia untuk semua tindakan di pengontrol pengguna, tetapi tidak tersedia di pengontrol lain? Bukankah metode ini memiliki beberapa masalah pelingkupan?
tybro0103
@ tybro0103, saya pikir untuk mengimplementasikan perilaku ini Anda ingin menulis sesuatu seperti window.varForOneController='val'pada fungsi init pengontrol ini. Gon permata juga dapat membantu di sini ( github.com/gazay/gon ). Mungkin ada solusi lain.
welldan97
1
@ welldan97 Downvoting bukan untuk penjelasan Anda - yang sangat bagus - tetapi karena struktur Garber-Irlandia jahat. Ini memuat semua JS Anda di setiap halaman, dan tergantung pada kelas dan ID pada elemen <body> untuk menyelesaikan masalah. Itu pertanda pasti melawan DOM: dalam keadaan normal elemen <body> seharusnya tidak memerlukan kelas atau ID, karena hanya ada satu di dokumen. Cara yang tepat untuk melakukan ini adalah dengan menghapus //= require_tree .dan menggunakan JavaScript khusus halaman. Jika Anda secara aktif berusaha untuk tidak melakukannya, maka Anda berjuang untuk praktik yang buruk.
Marnen Laibow-Koser
2
@ MarnenLaibow-Koser Secara pribadi saya percaya bahwa memuat semua js pada setiap halaman adalah baik untuk sebagian besar proyek ketika Anda menggabungkan semua js menjadi satu file dan menguranginya. Saya percaya ini secara keseluruhan bekerja lebih cepat untuk pengguna. Setidaknya ini lebih seperti konflik satu file js vs banyak (yaitu melihat stackoverflow.com/questions/555696/… ). Juga tidak ada yang buruk dalam menggunakan kelas dan id pada tubuh jika membuat kode lebih sederhana dan berfungsi untuk Anda. Modernizr ( modernizr.com ) melakukan ini, dan beberapa lib lainnya juga.
welldan97
2
@ MarnenLaibow-Koser pipa aset rel, bagi saya, sepertinya kandidat yang baik untuk dibandingkan dengan kompilasi. Seorang programmer menulis javascript mereka dalam modul terpisah yang bagus, dan kemudian disatukan, diperkecil, dan disajikan. Sama seperti dalam kasus bahasa yang dikompilasi, akan selalu ada programmer yang berpikir mereka selangkah lebih maju dari kompiler ... tapi saya pikir ini jarang benar.
Ziggy
65

Saya melihat bahwa Anda telah menjawab pertanyaan Anda sendiri, tetapi inilah pilihan lain:

Pada dasarnya, Anda membuat asumsi itu

//= require_tree .

Dibutuhkan. Ini bukan. Jangan ragu untuk menghapusnya. Dalam aplikasi saya saat ini, yang pertama saya lakukan dengan 3.1.x jujur, saya telah membuat tiga file JS tingkat atas yang berbeda. application.jsFile saya hanya punya

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

Dengan cara ini, saya dapat membuat subdirektori, dengan file JS tingkat atas mereka sendiri, yang hanya menyertakan apa yang saya butuhkan.

Kuncinya adalah:

  1. Anda dapat menghapus require_tree- Rails memungkinkan Anda mengubah asumsi yang dibuatnya
  2. Tidak ada yang istimewa dengan namanya application.js- file apa pun dalam assets/javascriptsubdirektori dapat menyertakan arahan pra-prosesor//=

Harapan yang membantu dan menambahkan beberapa detail pada jawaban ClosureCowboy.

Sujal

sujal
sumber
8
+1 Ini bagus untuk diketahui bagi pemula seperti saya. Saya akan memberi +2 jika saya bisa.
jrhorn424
5
@sujal Persis. Tim inti Rails terkenal karena manajemen JavaScript yang buruk. Jangan ragu untuk mengabaikan saran mereka dan gunakan saja bagian - bagian yang baik dari pipa aset. :)
Marnen Laibow-Koser
1
Terima kasih banyak atas saran ini. Saya tidak memiliki beberapa file JS "tingkat atas", tergantung pada modul aplikasi saya. Bekerja dengan baik.
elsurudo
1
+1 Poin penting di sini bagi saya adalah Anda dapat mengganti //= require_tree .dengan //= require_directory .sehingga Anda dapat menyimpan semua file yang ada di mana mereka berada dan membuat direktori baru untuk file khusus halaman.
zelanix
41

Pilihan lain: untuk membuat file khusus halaman atau model, Anda dapat membuat direktori di dalam assets/javascripts/folder Anda .

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

application.jsFile manifes utama Anda dapat dikonfigurasikan untuk memuat file-file tersebut dari global/. Halaman atau grup halaman tertentu dapat memiliki manifesnya sendiri yang memuat file dari direktori spesifik mereka sendiri. Sprockets akan secara otomatis menggabungkan file yang dimuat application.jsdengan file khusus halaman Anda, yang memungkinkan solusi ini bekerja.

Teknik ini dapat digunakan untuk style_sheets/juga.

ClosureCowboy
sumber
13
Anda telah membuat saya menginginkan kue mangkuk sekarang .. Sialan!
Chuck Bergeron
Saya sangat suka solusi ini. Satu-satunya masalah yang saya miliki dengan itu adalah bahwa manifes tambahan tidak dikompresi / jelek. Mereka dikompilasi dengan benar. Apakah ada solusi atau saya kehilangan sesuatu?
clst
1
apakah ini berarti bahwa browser memuat satu file js, yaitu kombinasi file global + halaman spesifik?
lulalala
Bisakah Anda melihat pertanyaan saya jika tersedia? stackoverflow.com/questions/17055213/…
Maximus S
1
@clst Saya percaya ini adalah jawaban yang Anda cari: guides.rubyonrails.org/asset_pipeline.html#precompiling-assets
FrontierPsycho
23

Saya menghargai semua jawaban ... dan saya pikir mereka tidak benar-benar mengerti masalahnya. Beberapa dari mereka adalah tentang gaya dan tampaknya tidak berhubungan ... dan yang lain hanya menyebutkan javascript_include_tag... yang saya tahu ada (jelas ...) tetapi akan terlihat bahwa Rails 3.1 cara maju adalah untuk membungkus semua Javascript Anda ke dalam 1 file daripada memuat Javascript individual di bagian bawah setiap halaman.

Solusi terbaik yang dapat saya temukan adalah dengan membungkus fitur-fitur tertentu dalam divtag dengan ids atau classes. Dalam kode javascript. Kemudian Anda hanya memeriksa apakah idatau classada di halaman, dan jika ya, Anda menjalankan kode javascript yang terkait dengannya. Dengan cara ini jika elemen dinamis tidak ada di halaman, kode javascript tidak berjalan - meskipun sudah termasuk dalam application.jsfile besar yang dikemas oleh Sprockets.

Solusi saya di atas memiliki manfaat bahwa jika kotak pencarian disertakan pada 8 dari 100 halaman, itu hanya akan berjalan pada 8 halaman tersebut. Anda juga tidak perlu memasukkan kode yang sama pada 8 halaman di situs. Bahkan, Anda tidak akan pernah harus memasukkan tag skrip manual di situs Anda di mana pun lagi - kecuali untuk mungkin memuat ulang data.

Saya pikir ini adalah jawaban aktual untuk pertanyaan saya.

Emblem Api
sumber
Tapi Anda sebenarnya menginginkan <script>tag manual itu . Ya, kelas dan id adalah bagian dari jawabannya, tetapi tidak masuk akal bagi pengguna untuk memuat JavaScript yang tidak dibutuhkan oleh halaman tersebut.
Marnen Laibow-Koser
4
@ MarnenLaibow-Koser alasan untuk tidak menambahkan tag skrip manual ke setiap halaman unik adalah Anda harus mengunduh konten skrip itu pada setiap tampilan halaman. Jika Anda dapat mengemas semua javascript ke application.js menggunakan pipeline aset, maka pengguna mengunduh skrip-skrip itu satu kali saja, dan menarik application.js dari cache pada semua pemuatan halaman berikutnya
jakeonrails
@jakeonrails "alasan untuk tidak menambahkan tag skrip manual ke setiap halaman unik adalah bahwa Anda harus mengunduh konten skrip itu di setiap tampilan halaman" - cukup salah. Script akan diunduh satu kali, lalu akan diambil dari cache browser jika diminta lebih lanjut. "Jika Anda dapat mengemas semua javascript ke application.js menggunakan pipeline aset, maka pengguna hanya mengunduh skrip-skrip itu satu kali" - benar, tetapi dengan biaya banyak kode yang tidak perlu. Jika Anda dapat menyusun JS menjadi banyak file kecil alih-alih yang besar, Anda mendapatkan manfaat caching tanpa kode yang tidak perlu.
Marnen Laibow-Koser
1
@ MarnenLaibow-Koser Saya pikir akan lebih baik untuk mengatakan bahwa jika Anda mengemas semuanya menjadi satu skrip, pengguna Anda hanya perlu mengunduh 1 skrip untuk setiap halaman situs Anda. Jika Anda memiliki beberapa skrip untuk berbagai bagian aplikasi Anda, maka jelas pengguna harus mengunduh lebih dari satu skrip. Kedua metode ini akan di-cache, tentu saja, tetapi dalam kebanyakan kasus (aplikasi kecil-menengah), melayani satu aplikasi. Satu kali akan lebih efisien untuk mengunduh. Parsing dari JS mungkin cerita lain, tergantung pada apa yang Anda sajikan.
jakeonrails
1
@ Ziggy Juga, jika file kecil hanya digunakan pada 8 dari 100 halaman, mengapa kode harus tetap berada dalam cache pengguna sepanjang waktu? Lebih baik menjatuhkan barang-barang yang sebenarnya tidak diperlukan.
Marnen Laibow-Koser
16

Saya menyadari bahwa saya datang ke pesta ini agak terlambat, tetapi saya ingin memberikan solusi yang saya gunakan belakangan ini. Namun, izinkan saya pertama menyebutkan ...

The Rails 3.1 / 3.2 Way (Tidak, tuan. Saya tidak suka itu.)

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

Saya menyertakan yang berikut demi kelengkapan dalam jawaban ini, dan karena itu bukan solusi yang tidak dapat diperbaiki ... meskipun saya tidak terlalu peduli untuk itu.

"Rails Way" adalah solusi yang berorientasi pada pengontrol, daripada berorientasi pada pandangan seperti yang diminta oleh penulis asli dari pertanyaan ini. Ada file JS khusus pengontrol yang dinamai menurut pengontrolnya masing-masing. Semua file ini ditempatkan di pohon folder yang TIDAK termasuk secara default di salah satu application.js memerlukan arahan.

Untuk memasukkan kode khusus pengontrol, berikut ini ditambahkan ke tampilan.

<%= javascript_include_tag params[:controller] %>

Saya benci solusi ini, tetapi ada di sana dan cepat. Agaknya, Anda bisa memanggil file-file ini sesuatu seperti "people-index.js" dan "people-show.js" dan kemudian menggunakan sesuatu seperti "#{params[:controller]}-index"untuk mendapatkan solusi berorientasi tampilan. Sekali lagi, perbaikan cepat, tetapi itu tidak cocok dengan saya.

Cara Atribut Data Saya

Panggil saya gila, tapi saya ingin SEMUA JS saya dikompilasi dan diperkecil ke dalam application.js ketika saya menyebarkan. Saya tidak ingin harus ingat untuk memasukkan file-file kecil yang lalai ini di semua tempat.

Saya memuat semua JS saya dalam satu file cache yang kompak, yang akan segera di-cache. Jika bagian tertentu dari application.js saya perlu diaktifkan pada halaman, saya membiarkan HTML memberitahu saya, bukan Rails.

Daripada mengunci JS saya ke ID elemen tertentu atau mengotori HTML saya dengan kelas marker, saya menggunakan atribut data khusus yang disebut data-jstags.

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

Pada setiap halaman, saya menggunakan - masukkan metode perpustakaan JS yang disukai di sini - untuk menjalankan kode ketika DOM selesai memuat. Kode bootstrap ini melakukan tindakan berikut:

  1. Iterasi semua elemen dalam DOM yang ditandai dengan data-jstag
  2. Untuk setiap elemen, pisahkan nilai atribut pada spasi, buat array string tag.
  3. Untuk setiap string tag, lakukan pencarian di Hash untuk tag itu.
  4. Jika kunci yang cocok ditemukan, jalankan fungsi yang terkait dengannya, lewat elemen sebagai parameter.

Jadi katakan saya memiliki yang berikut didefinisikan di suatu tempat di application.js saya:

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

Acara bootstrap akan menerapkan my_autosuggest_initdan my_hint_initberfungsi terhadap input pencarian, mengubahnya menjadi input yang menampilkan daftar saran saat pengguna mengetik, serta memberikan semacam petunjuk input ketika input dibiarkan kosong dan tidak fokus.

Kecuali beberapa elemen ditandai data-jstag="auto-suggest", kode saran otomatis tidak pernah diaktifkan. Namun, selalu ada di sana, diperkecil, dan akhirnya di-cache dalam aplikasi saya. Untuk saat itu saya membutuhkannya di halaman.

Jika Anda perlu memberikan parameter tambahan ke fungsi JS yang ditandai, Anda harus menerapkan beberapa kreativitas. Entah menambahkan atribut data-paramter, menghasilkan beberapa jenis sintaks parameter, atau bahkan menggunakan pendekatan hybrid.

Bahkan jika saya memiliki beberapa alur kerja rumit yang tampaknya khusus untuk kontroler, saya hanya akan membuat file untuk itu di folder lib saya, masukkan ke application.js, dan beri tag dengan sesuatu seperti 'new-thing-wizard'. Ketika bootstrap saya mengenai tag itu, wizard saya yang bagus dan mewah akan dipakai dan dijalankan. Ini berjalan untuk tampilan pengontrol ketika diperlukan, tetapi tidak jika digabungkan ke controller. Bahkan, jika saya mengkodekan wizard saya dengan benar, saya mungkin dapat memberikan semua data konfigurasi dalam tampilan dan karena itu dapat menggunakan kembali wizard saya nanti untuk pengontrol lain yang membutuhkannya.

Bagaimanapun, ini adalah bagaimana saya telah mengimplementasikan JS halaman spesifik untuk sementara waktu sekarang, dan itu telah membantu saya dengan baik untuk desain situs sederhana dan untuk aplikasi yang lebih kompleks / kaya. Semoga salah satu dari dua solusi yang saya sajikan di sini, cara saya atau cara Rails, sangat membantu bagi siapa saja yang menemukan pertanyaan ini di masa depan.

Ryan
sumber
6
Satu detail kecil: ada dugaan dalam jawaban Anda bahwa setelah js di-cache browser, itu tidak berdampak. Ini tidak sepenuhnya benar. Browser memang menghindari unduhan, jika file js di-cache dengan benar, tetapi masih mengkompilasi kode pada setiap halaman render. Jadi, Anda harus menyeimbangkan pengorbanan. Jika Anda memiliki banyak JS dalam agregat, tetapi hanya beberapa yang digunakan per halaman, Anda mungkin dapat meningkatkan waktu halaman dengan memecah JS terpisah.
sujal
Untuk lebih lanjut tentang efek praktis dari langkah kompilasi yang saya bicarakan, lihat penjelasan 37 Sinyal tentang bagaimana pjax berdampak pada Basecamp Selanjutnya: 37signals.com/svn/posts/…
sujal
Itu poin yang adil. Setelah membaca artikel dan melihat kembali proyek-proyek di mana saya telah menggunakan solusi di atas, saya menyadari saya menulis pada dasarnya sama "mengirim HTML yang diubah" solusi yang mereka sebutkan dalam artikel. Kompilasi ulang JS yang sering tidak menjadi masalah di proyek saya karena itu. Langkah kompilasi adalah sesuatu yang akan saya ingat ketika saya bekerja di situs yang kurang berorientasi "aplikasi desktop".
Ryan
2
Downvoting untuk "Panggil aku gila, tapi aku ingin SEMUA JS saya dikompilasi dan diperkecil ke dalam application.js ketika saya menyebarkan." Anda benar-benar tidak menginginkan ini, karena itu membuat pengguna memuat JavaScript yang tidak diperlukannya dan itu membuat penangan Anda mencari atribut yang bahkan tidak akan ada di sana. Memiliki semuanya di app.js menggoda, dan Rails tentu saja membuatnya mudah, tetapi hal yang tepat untuk dilakukan adalah memodulasi JavaScript dengan lebih baik.
Marnen Laibow-Koser
Anda berhak memiliki pendapat yang berbeda ... dan secara teknis berhak untuk menurunkan pendapat karena perbedaan pendapat. Namun, akan lebih baik untuk melihat beberapa alasan mengapa satu file besar dan di-cache lebih rendah daripada memaksa beberapa permintaan HTTP untuk mengambil JS termodulasi. Selain itu, Anda salah tentang prosedur pencarian penangan. Nilai tag TIDAK dicari. Hanya satu pencarian yang pernah dilakukan dan menarik semua elemen yang memiliki atribut data-jstag. Itu tidak mencari berdasarkan nama tag, itu hanya menemukan semua elemen yang memiliki tag dan kemudian hanya instantiate objek yang diperlukan.
Ryan
7

Ini telah dijawab dan diterima sejak lama, tetapi saya menemukan solusi saya sendiri berdasarkan beberapa jawaban ini dan pengalaman saya dengan Rails 3+.

Pipa asetnya manis. Gunakan.

Pertama, di application.jsfile Anda , hapus//= require_tree.

Kemudian di Anda application_controller.rbbuat metode pembantu:

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

Kemudian di application.html.erbfile tata letak Anda , tambahkan pembantu baru Anda di antara javascript yang ada, diawali dengan rawpembantu:

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

Voila, sekarang Anda dapat dengan mudah membuat javascript khusus tampilan menggunakan struktur file yang sama yang Anda gunakan di tempat lain di rails. Cukup tempelkan file Anda app/assets/:namespace/:controller/action.js.erb!

Semoga itu bisa membantu orang lain!

Mike A
sumber
1
Tidakkah ini menyebabkan masalah setelah aset dikompilasi dan pada saat dijalankan <%= raw ... %>akan mengembalikan 404?
Nishant
Saya pikir pipa aset tidak-manis, karena itu menciptakan banyak file yang sering tidak boleh digunakan. Jadi bagi saya mengandalkan pipa aset menciptakan ketergantungan pada sistem yang tidak efisien.
Deborah
1
@DeborahSpeece Kapan pipa aset membuat file yang tidak boleh digunakan? Apakah Anda mengacaukan jalur pipa aset (baik) dengan require_tree /(buruk)?
Marnen Laibow-Koser
6

Anda dapat menambahkan baris ini di file tata letak Anda (mis. Application.html.erb) untuk secara otomatis memuat file javascript khusus controller (yang dibuat saat Anda membuat controller):

<%= javascript_include_tag params[:controller] %>

Anda juga bisa menambahkan baris untuk memuat file skrip secara otomatis dalam basis per tindakan.

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

Cukup masukkan skrip halaman Anda ke dalam subdirektori yang dinamai menurut nama pengontrol. Dalam file-file ini Anda dapat menyertakan skrip lain menggunakan = wajib. Akan lebih baik untuk membuat helper untuk memasukkan file hanya jika ada, untuk menghindari kegagalan 404 di browser.

mcubik
sumber
6
<%= javascript_include_tag params[:controller] %>
Tuan Bohr
sumber
2
Sepertinya ini mungkin bisa menjawab pertanyaan. Bisakah Anda menambahkan lebih banyak jawaban untuk menyempurnakannya?
6

Mungkin Anda akan menemukan permata pluggable_js sebagai solusi yang sesuai.

peresleguine
sumber
5

The LoadJS permata adalah pilihan lain:

LoadJS menyediakan cara untuk memuat kode Javascript khusus-halaman dalam aplikasi Rails tanpa menghilangkan keajaiban yang disediakan oleh Sprockets. Semua kode Javascript Anda akan dilanjutkan dengan diperkecil dalam satu file Javascript tetapi beberapa bagiannya hanya akan dieksekusi untuk halaman tertentu.

https://github.com/guidomb/loadjs

meleyal
sumber
3

Jawaban Philip cukup bagus. Berikut adalah kode untuk membuatnya berfungsi:

Di application.html.erb:

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

Dengan asumsi pengontrol Anda disebut Proyek, yang akan menghasilkan:

<body class="projects">

Kemudian di projects.js.coffee:

jQuery ->
  if $('body.projects').length > 0  
     $('h1').click ->
       alert 'you clicked on an h1 in Projects'
Rahil Sondhi
sumber
Downvoting: solusi apapun yang menempatkan kelas di <body>adalah ipso facto tidak benar. Lihat komentar saya di tempat lain di halaman ini.
Marnen Laibow-Koser
Jangan lakukan ini. Masalahnya di sini adalah bahwa setiap kali Anda menambahkan salah satu dari ini, Anda menambahkan sepotong js yang perlu dijalankan pada pemuatan halaman. Pasti dapat menyebabkan penurunan kinerja seiring proyek Anda tumbuh.
ifightcrime
2

JavaScripts hanya digabung ketika Anda memberi tahu Rails (Sprockets, bukan) untuk menggabungkannya.

yfeldblum
sumber
Tentu saja. Saya kira saya bertanya karena standar Rails 'termasuk semua yang ada di folder ... yang berarti David bermaksud untuk Anda melakukannya. Tapi seperti yang saya katakan di komentar lain ke @rubyprince, saya tidak yakin tentang eksekusi ketika dilakukan dengan cara ini. Saya pikir saya harus menonaktifkan //= require_tree .?
Fire Emblem
@FireEmblem Ya. require_tree .biasanya ide yang buruk.
Marnen Laibow-Koser
2

Ini adalah bagaimana saya memecahkan masalah styling: (permisi Haml)

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

Dengan cara ini saya memulai semua file .css.sass halaman khusus dengan:

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

Dengan cara ini Anda dapat dengan mudah menghindari bentrokan. Ketika datang ke file .js.coffee Anda hanya bisa menginisialisasi elemen seperti;

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

Semoga ini bisa membantu.

zeeraw
sumber
1
Baca bit terakhir lagi tolong, untuk javascript Anda dapat mengambil keuntungan dari struktur yang sama yang digunakan untuk stylesheet untuk menginisialisasi tampilan fungsi tertentu.
zeeraw
Philip, $('#post > #edit') ->tampaknya tidak valid. Bagaimana Anda lingkup jQuery untuk bekerja dalam lingkup?
Ramon Tayag
2
Baru-baru ini saya mulai memuat semua skrip java khusus dan style sheet dengan memanggilnya di application.html.haml; = javascript_include_tag "application"dan = javascript_include_tag params[:controller]dengan cara ini saya dapat menyimpan kode javascript tanpa harus menentukan lingkup di dalam file.
zeeraw
2

Saya setuju dengan jawaban Anda, untuk memeriksa apakah pemilih itu ada, gunakan:

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

(tidak melihat orang menambahkan solusi yang sebenarnya)

Kyle C
sumber
2

Saya tidak melihat jawaban yang benar-benar menyatukan dan menjabarkannya untuk Anda. Jadi, saya akan mencoba untuk menempatkan meleyal , Sujal (a la ClosureCowboy ), bagian pertama dari Ryan jawaban, dan bahkan Gal pernyataan berani tentang Backbone.js ... semua bersama-sama dengan cara yang singkat dan jelas. Dan, siapa tahu, saya bahkan mungkin bertemu Marnen Laibow-Koser .

Contoh suntingan

aset / 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 %>


aset / javascripts / foo.js

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


aset / javascripts / foostuff / foothis.js.coffee

alert "Hello world!"


Deskripsi singkat

  • Hapus //= require_tree .dari application.js dan daftarkan hanya JS yang dibagi setiap halaman.

  • Dua baris yang ditunjukkan di atas di application.html.erb memberi tahu halaman tempat menyertakan application.js dan JS khusus laman Anda.

  • Tiga baris yang ditunjukkan di atas dalam index.html.erb memberitahu pandangan Anda untuk mencari beberapa JS khusus halaman dan memasukkannya pada wilayah hasil bernama bernama ": javascript" (atau apa pun yang Anda ingin beri nama). Dalam contoh ini, pengontrolnya adalah "foo" sehingga Rails akan berusaha memasukkan "foo.js" di: area javascript menghasilkan dalam tata letak aplikasi.

  • Buat daftar JS khusus halaman Anda di foo.js (atau apa pun nama pengontrolnya). Daftar perpustakaan umum, pohon, direktori, apa pun.

  • Simpan JS khusus halaman khusus Anda di tempat yang mudah Anda rujuk selain JS khusus Anda. Dalam contoh ini, foo.js memerlukan pohon foostuff jadi letakkan JS kustom Anda di sana, seperti foothis.js.coffee .

  • Tidak ada aturan keras di sini. Jangan ragu untuk memindahkan barang-barang dan bahkan mungkin membuat beberapa wilayah hasil dari berbagai nama dalam berbagai tata letak jika diperlukan. Ini hanya menunjukkan satu kemungkinan langkah maju pertama. (Saya tidak melakukannya persis seperti ini karena kami menggunakan Backbone.js. Saya mungkin juga memilih untuk menjatuhkan foo.js ke dalam folder bernama foo, bukan foostuff tetapi belum memutuskannya.)

Catatan

Anda dapat melakukan hal serupa dengan CSS dan <%= stylesheet_link_tag params[:controller] %>tetapi ini di luar cakupan pertanyaan.

Jika saya melewatkan praktik terbaik yang mencolok di sini, kirimkan saya catatan dan saya akan menyesuaikan conisder. Rails cukup baru bagi saya dan, sejujurnya, sejauh ini saya tidak terkesan dengan kekacauan yang diakibatkan oleh pengembangan perusahaan dan semua lalu lintas yang dihasilkan oleh program Rails rata-rata.

juanitogan
sumber
ini sepertinya cara untuk pergi, saya akan melihat apakah saya bisa menerapkannya di aplikasi saya sendiri, terima kasih atas jawaban terinci.
martincarlin87
1

Saya punya solusi lain, yang walaupun primitif berfungsi dengan baik untuk saya dan tidak memerlukan strategi pemuatan selektif yang mewah. Masukkan fungsi siap dokumen nornal Anda, tetapi kemudian menguji lokasi windows saat ini untuk melihat apakah itu halaman javascript Anda dimaksudkan untuk:

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

Ini masih memungkinkan semua js dimuat oleh rel 3.x dalam satu paket kecil, tetapi tidak menghasilkan banyak overhead atau konflik dengan halaman yang tidak dimaksudkan js.

Peter Abramowitsch
sumber
1

jawaban ryguy adalah jawaban yang baik, meskipun sudah diturunkan ke poin negatif.

Terutama jika Anda menggunakan sesuatu seperti Backbone JS - setiap halaman memiliki tampilan Backbone sendiri. Kemudian file erb hanya memiliki satu baris javascript inline yang menjalankan kelas tampilan backbone kanan. Saya menganggapnya sebagai satu baris 'kode lem' dan oleh karena itu fakta bahwa inline-nya OK. Keuntungannya adalah Anda dapat menyimpan "require_tree" yang memungkinkan cache browser semua javascript.

di show.html.erb, Anda akan memiliki sesuatu seperti:

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

dan di file tata letak Anda, Anda perlu:

<%= yield :javascript %>
Gal
sumber
Downvoting. JavaScript sebaris tidak pernah merupakan ide yang baik. Bahkan jika itu kode lem, itu harus dalam file eksternal.
Marnen Laibow-Koser
1

Pindahkan semua file JS commom Anda ke sub-folder seperti 'app / assets / javascript / global' lalu di application.js, ubah //= require_tree .baris menjadi //= require_tree ./global.

Sekarang Anda bebas menempatkan JS khusus-pengontrol Anda di root 'app / aset / javascript /' dan mereka tidak akan dimasukkan dalam JS yang dikompilasi, yang digunakan hanya ketika Anda memanggilnya melalui = javascript_include_tagpada pengontrol / tampilan Anda.

Gedean Dias
sumber
Tidak mungkin, itu omong kosong JavaScript untuk memuat untuk satu halaman. Bahkan tidak masalah jika itu di-cache.
jackyalcine
1

Meskipun Anda memiliki beberapa jawaban di sini, saya pikir hasil edit Anda mungkin yang terbaik. Pola desain yang kami gunakan di tim kami yang kami dapatkan dari Gitlab adalah pola Dispatcher. Itu melakukan sesuatu yang mirip dengan apa yang Anda bicarakan, namun nama halaman diatur dalam tag tubuh dengan rel. Misalnya, dalam file tata letak Anda, cukup sertakan sesuatu seperti (dalam HAML):

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

Maka hanya ada satu penutupan dan pernyataan beralih di dispatcher.js.coffeefile Anda di folder javascripts Anda seperti:

$ ->
  new Dispatcher()

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

Yang perlu Anda lakukan dalam file individual (katakan products.js.coffeeatau login.js.coffeemisalnya) adalah melampirkannya di kelas dan kemudian mengglobal simbol kelas itu sehingga Anda dapat mengaksesnya di dispatcher:

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

Gitlab memiliki beberapa contoh tentang hal ini yang mungkin ingin Anda perhatikan jika Anda penasaran :)

onetwopunch
sumber
1

Proyek Paloma menawarkan pendekatan menarik untuk mengelola kode javascript khusus halaman.

Contoh penggunaan dari dokumen mereka:

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

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

Langkah 1. hapus require_tree. di application.js dan application.css Anda.

Langkah 2. Edit application.html.erb Anda (berdasarkan rails default) di folder layout. Tambahkan "params [: controller]" di tag berikut.

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

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

Step3. Tambahkan file di 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

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

etlds
sumber
Sementara ini secara teoritis dapat menjawab pertanyaan, akan lebih baik untuk memasukkan bagian-bagian penting dari jawaban di sini, dan menyediakan tautan untuk referensi.
Bhargav Rao
0

Saya belum mencoba ini, tetapi sepertinya yang berikut ini benar:

  • jika Anda memiliki content_for yaitu javascript (mis. dengan javascript nyata di dalamnya), sprocket tidak akan mengetahuinya dan karenanya ini akan bekerja dengan cara yang sama seperti sekarang.

  • jika Anda ingin mengecualikan file dari bundel besar javascript, Anda akan masuk ke file config / sprockets.yml dan memodifikasi source_files sesuai. Kemudian, Anda hanya perlu memasukkan file yang Anda kecualikan jika diperlukan.

Bill Eisenhauer
sumber
Apakah mengecualikan file atau menggunakan javascript khusus pada halaman itu sendiri dengan "cara yang benar"? Begitukah cara David menganjurkan orang untuk menggunakannya?
Fire Emblem
@FireEmblem Saya tidak terlalu peduli dengan apa yang dimaksudkan oleh David, karena saya pikir David tidak mengerti bagaimana mengatur JavaScript dengan benar.
Marnen Laibow-Koser
0

Saya menggabungkan beberapa jawaban menjadi:

Penolong aplikasi:

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

tata letak / 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  
kapellan
sumber
0

Pertama: hapus \\=require_treedari application.js Kedua: semua kode JS Anda harus dialokasikan di /app/assets/javascritptdan semua kode CSS Anda harus dialokasikan di/app/assets/stylesheets

Nicolla
sumber
-2

Mengikuti petunjuk dari Ryan, inilah yang telah saya lakukan-

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 (skrip kopi kontroler khusus, mis. kontroler: pengguna, aksi: dasbor)

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}}
Kruttik
sumber
Downvoting. Ini sangat berbelit-belit - belum lagi tidak aman (karena eval) jika HTML Anda dikompromikan oleh server yang retak atau skrip pengguna yang berbahaya.
Marnen Laibow-Koser
-3

Inilah cara melakukannya terutama jika Anda tidak harus mengeksekusi banyak pustaka untuk halaman spesifik Anda, tetapi hanya untuk menjalankan beberapa ratus baris JS lebih atau kurang.

Karena sangat baik untuk menanamkan kode Javascript ke dalam HTML, buat saja di bawah direktori app / views shared.js dan letakkan di sana halaman / kode spesifik halaman Anda di dalam my_cool_partial.html.erb

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

Jadi sekarang dari mana pun Anda mau, Anda cukup melakukannya:

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

Dan itu saja, k?

Valk
sumber
2
Downvoting. JavaScript sebaris tidak pernah disarankan. HTML seharusnya hanya berisi markup. JS dan CSS harus dalam file yang terpisah dan dapat digunakan kembali.
Marnen Laibow-Koser