Sử dụng Rails 3.1, bạn đặt mã JavaScript cụ thể trên trang của bạn ở đâu?


388

Theo hiểu biết của tôi, tất cả JavaScript của bạn được hợp nhất thành 1 tệp. Rails thực hiện điều này theo mặc định khi nó thêm //= require_tree .vào dưới cùng của application.jstệp kê khai của bạn .

Điều này nghe có vẻ như là một trình tiết kiệm thực tế, nhưng tôi hơi lo ngại về mã JavaScript dành riêng cho trang. Mã này có được thực thi trên mỗi trang không? Điều cuối cùng tôi muốn là cho tất cả các đối tượng của tôi được khởi tạo cho mọi trang khi chúng chỉ cần trên 1 trang.

Ngoài ra, không có tiềm năng cho mã xung đột quá?

Hay bạn đặt một scriptthẻ nhỏ ở cuối trang vừa gọi vào một phương thức thực thi mã javascript cho trang?

Bạn không còn cần request.js nữa chứ?

Cảm ơn

EDIT : Tôi đánh giá cao tất cả các câu trả lời ... và tôi không nghĩ rằng họ đang thực sự gặp phải vấn đề. Một số trong số chúng là về kiểu dáng và dường như không liên quan ... và những cái khác chỉ đề cập đến javascript_include_tag... mà tôi biết là tồn tại (rõ ràng ...) nhưng có vẻ như cách Rails 3.1 sắp tới là kết thúc tất cả JavaScript của bạn thành 1 tệp thay vì tải JavaScript riêng lẻ ở cuối mỗi trang.

Giải pháp tốt nhất tôi có thể đưa ra là bọc một số tính năng nhất định trong divthẻ bằng ids hoặc classes. Trong mã JavaScript, bạn chỉ cần kiểm tra xem idhoặc classcó trên trang hay không và nếu có, bạn chạy mã JavaScript được liên kết với nó. Theo cách này, nếu phần tử động không có trên trang, mã JavaScript sẽ không chạy - mặc dù nó được bao gồm trong application.jstệp lớn được đóng gói bởi Sprockets.

Giải pháp trên của tôi có lợi ích là nếu một hộp tìm kiếm được bao gồm trên 8 trong số 100 trang, nó sẽ chỉ chạy trên 8 trang đó. Bạn cũng sẽ không phải bao gồm cùng một mã trên 8 trang trên trang web. Trên thực tế, bạn sẽ không bao giờ phải đưa các thẻ script thủ công vào trang web của mình ở bất cứ đâu nữa.

Tôi nghĩ rằng đây là câu trả lời thực sự cho câu hỏi của tôi.


11
"Cách Rails 3.1 sắp tới là kết thúc tất cả Javascript của bạn thành 1 tệp thay vì tải Javascript riêng lẻ ở cuối mỗi trang." - Chỉ có nhóm nòng cốt Rails mới và thực sự rất tệ khi biết cách để quản lý JavaScript. Các tệp nhỏ thường tốt hơn (xem ý kiến ​​của tôi ở nơi khác). Khi nói đến JavaScript, cách Rails hiếm khi là cách đúng (ngoại trừ đường dẫn tài sản, đá vào mông và khuyến khích CoffeeScript).
Marnen Laibow-Koser

Vì vậy, bạn sẽ bao gồm các tệp js cụ thể trang của bạn trên mỗi trang? Tôi nghĩ đó là một sự lãng phí, tôi đồng ý nhiều hơn với câu trả lời của ClosCowboy.
gerky

1
Bạn đã xem câu trả lời được chấp nhận cho câu hỏi này chưa? stackoverflow.com/questions/6571753/
hy

1
@DutGRIFF Nói cách khác: không, tốt nhất không nên làm mọi thứ theo cách Rails trong trường hợp này (hoặc ít nhất, không đặt mọi thứ vào application.js), và trên thực tế, tài liệu tham khảo bạn cung cấp chỉ ra lý do tại sao lại như vậy: tải xuống là phần chậm nhất của quy trình thực thi JS. Nhiều tập tin nhỏ có nhiều bộ nhớ cache hơn một tập tin lớn. Sau đó, những người Unholy Rails dường như không nhận ra rằng các khuyến nghị của họ không phù hợp với các nguyên tắc họ đang cố gắng tuân thủ và do đó, các khuyến nghị của họ không nên được coi trọng.
Marnen Laibow-Koser

1
@DutGRIFF Không, một tệp JS lớn thường không phải là một điều tốt ngay cả khi đã được lưu trữ. Xem nhận xét của tôi ở nơi khác trên trang này: các tệp nhỏ có thể nhắm mục tiêu các trang cụ thể tốt hơn và có thể được lưu trong bộ đệm ở mức độ chi tiết tốt hơn. Tôi không thấy bất kỳ trường hợp sử dụng tốt nào cho một tệp lớn trừ khi không có mã cụ thể theo trang nào cả .
Marnen Laibow-Koser

Câu trả lời:


157

Các tài liệu Đường ống tài sản đề xuất cách thực hiện JS dành riêng cho bộ điều khiển:

Ví dụ: nếu a ProjectsControllerđược tạo, sẽ có một tệp mới tại app/assets/javascripts/projects.js.coffeevà một tệp khác tại app/assets/stylesheets/projects.css.scss. Bạn nên đặt bất kỳ mã JavaScript hoặc CSS duy nhất nào cho bộ điều khiển bên trong các tệp nội dung tương ứng của chúng, vì các tệp này sau đó có thể được tải chỉ cho các bộ điều khiển này bằng các dòng như <%= javascript_include_tag params[:controller] %>hoặc <%= stylesheet_link_tag params[:controller] %>.

Liên kết đến: property_pipeline


50
Đây là cách thanh lịch nhất để làm điều đó. Nhưng ngoài ra, bạn sẽ cần xóa dòng // = allow_tree. từ application.js.coffee
zsljulius

2
Tôi hoàn toàn đồng ý với phương pháp này. Các phương pháp khác có vẻ rất cục mịch và cuối cùng vẫn tải một tệp js khổng lồ. Dự án tôi đang làm việc có các tệp / plugin JS có giá trị gần 2mb, vv SAU KHI được kết hợp / thu nhỏ.
Bill Garrison

2
Tôi khá mới với Rails, nhưng dường như đây là hành vi mặc định.
Ross Hambrick

12
Đối với điều khiển cụ thể hành động tôi có điều này trong bố cục của mình, vì không phải mọi hành động cho mọi trình điều khiển đều có JS cụ thể. page_specific_js = "#{params[:controller]}_#{params[:action]}"và sau đó; javascript_include_tag page_specific_js if Rails.application.assets.find_asset page_specific_js
Sujimichi

2
Các hành động cụ thể của bộ điều khiển vẫn được giảm thiểu? Có phải chúng được thêm vào tệp js duy nhất được tạo bởi sprockets hoặc elk điều này dẫn đến nhiều yêu cầu cho tệp tài sản?
Jason

77

Đối với js dành riêng cho trang, bạn có thể sử dụng giải pháp Garber-Irish .

Vì vậy, thư mục Rails javascripts của bạn có thể trông như thế này cho hai bộ điều khiển - ô tô và người dùng:

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

Và javascripts sẽ trông như thế này:

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

và markup_basing_js_execut sẽ chứa mã cho đối tượng UTIL và trên thực thi UTIL.init sẵn sàng DOM.

Và đừng quên đặt nó vào tập tin bố trí của bạn:

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

Tôi cũng nghĩ rằng tốt hơn là sử dụng các lớp thay vì các data-*thuộc tính, cho css trang cụ thể tốt hơn. Như Jason Garber đã đề cập: bộ chọn CSS dành riêng cho trang có thể thực sự khó xử (khi bạn sử dụng data-*thuộc tính)

Tôi hy vọng điều này sẽ giúp bạn.


4
Điều gì xảy ra nếu bạn cần một biến có sẵn cho tất cả các hành động trong bộ điều khiển người dùng, nhưng không có sẵn trong các bộ điều khiển khác? Không phải phương pháp này có một số vấn đề phạm vi?
tybro0103

@ tybro0103, tôi nghĩ để thực hiện hành vi này, bạn muốn viết một cái gì đó giống như window.varForOneController='val'trong hàm init của trình điều khiển này. Ngoài ra gon gem có thể giúp đỡ ở đây ( github.com/gazay/gon ). Có thể có cách giải quyết khác.
welldan97

1
@ welldan97 Downvote không phải vì lời giải thích của bạn - điều này là tuyệt vời - nhưng bởi vì cấu trúc Garber-Ailen là xấu xa. Nó tải tất cả JS của bạn trên mỗi trang và phụ thuộc vào các lớp và ID trên phần tử <body> để sắp xếp mọi thứ. Đó là một dấu hiệu chắc chắn để chống lại DOM: trong các trường hợp thông thường, phần tử <body> không cần một lớp hoặc ID, vì chỉ có một tài liệu trong một tài liệu. Cách thích hợp để làm điều này chỉ đơn giản là xóa //= require_tree .và sử dụng JavaScript dành riêng cho trang. Nếu bạn chủ động cố gắng không làm điều đó, thì bạn đang phấn đấu để thực hành xấu.
Marnen Laibow-Koser

2
@ MarnenLaibow-Koser Cá nhân tôi tin rằng việc tải tất cả js trên mỗi trang là tốt cho hầu hết các dự án khi bạn kết hợp tất cả js vào một tệp và giảm thiểu nó. Tôi tin rằng nó hoạt động tổng thể nhanh hơn cho người dùng. Ít nhất nó giống như xung đột một tệp js so với nhiều tệp (ví dụ: nhìn vào stackoverflow.com/questions/555696/ mẹo ). Ngoài ra, không có gì xấu trong việc sử dụng các lớp và id trên cơ thể nếu nó làm cho mã đơn giản hơn và làm việc cho bạn. Modernizr ( modernizr.com ) thực hiện điều này, và một số libs khác quá.
welldan97

2
@ MarnenLaibow-Koser đường ống tài sản đường ray, với tôi, có vẻ như là một ứng cử viên tốt để so sánh với việc biên soạn. Một lập trình viên viết javascript của họ trong các mô-đun tách rời tốt đẹp, và sau đó nó được gộp lại với nhau, rút ​​gọn và phục vụ. Cũng giống như trong trường hợp ngôn ngữ được biên dịch, sẽ luôn có những lập trình viên nghĩ rằng họ đi trước một bước so với trình biên dịch ... nhưng tôi nghĩ điều này hiếm khi đúng.
Ziggy

65

Tôi thấy rằng bạn đã trả lời câu hỏi của riêng bạn, nhưng đây là một lựa chọn khác:

Về cơ bản, bạn đang đưa ra giả định rằng

//= require_tree .

bắt buộc. Nó không thể. Hãy loại bỏ nó. Trong ứng dụng hiện tại của tôi, lần đầu tiên tôi thực hiện với 3.1.x, tôi đã tạo ba tệp JS cấp cao nhất khác nhau. application.jsTập tin của tôi chỉ có

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

Bằng cách này, tôi có thể tạo các thư mục con, với các tệp JS cấp cao nhất của riêng chúng, chỉ bao gồm những gì tôi cần.

Các phím là:

  1. Bạn có thể xóa require_tree- Rails cho phép bạn thay đổi các giả định mà nó đưa ra
  2. Không có gì đặc biệt về tên application.js- bất kỳ tệp nào trong assets/javascriptthư mục con có thể bao gồm các chỉ thị tiền xử lý với//=

Hy vọng rằng sẽ giúp và thêm một số chi tiết cho câu trả lời của ClosCowboy.

Sujal


8
+1 Điều này thật tuyệt khi biết với một người mới như tôi. Tôi sẽ cho nó +2 nếu tôi có thể.
jrhorn424

5
@sujal Chính xác. Nhóm nòng cốt Rails nổi tiếng với việc quản lý JavaScript sâu. Hãy bỏ qua các đề xuất của họ và chỉ sử dụng các phần tốt của đường ống tài sản. :)
Marnen Laibow-Koser

1
Cảm ơn rất nhiều vì lời khuyên này. Tôi không có một số tệp JS "cấp cao nhất", tùy thuộc vào mô-đun của ứng dụng. Hoạt động tốt.
ngày

1
1 Vấn đề quan trọng ở đây đối với tôi là bạn có thể thay thế //= require_tree .với //= require_directory ., do đó bạn có thể giữ tất cả các file đang tồn tại mà họ đang có và tạo thư mục mới cho các tập tin trang cụ thể.
zelanix

41

Một tùy chọn khác: để tạo các tệp cụ thể theo trang hoặc mô hình, bạn có thể tạo các assets/javascripts/thư mục trong thư mục của mình .

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

application.jsTệp kê khai chính của bạn có thể được cấu hình để tải các tệp của nó global/. Các trang hoặc nhóm trang cụ thể có thể có các bảng kê khai riêng tải các tệp từ các thư mục cụ thể của riêng chúng. Sprockets sẽ tự động kết hợp các tệp được tải application.jsvới các tệp cụ thể theo trang của bạn, cho phép giải pháp này hoạt động.

Kỹ thuật này có thể được sử dụng style_sheets/là tốt.


13
Bạn đã làm tôi thèm bánh cupcake bây giờ .. Dangit!
Chuck Bergeron

Tôi thực sự thích giải pháp này. Vấn đề duy nhất tôi có với nó là những biểu hiện thêm đó không bị nén / xấu xí. Chúng được biên dịch đúng mặc dù. Có một giải pháp hay tôi đang thiếu một cái gì đó?
clst

1
điều này có nghĩa là trình duyệt tải một tệp js, đó là sự kết hợp của tệp cụ thể toàn cầu + trang?
lulalala

Bạn có thể xem câu hỏi của tôi nếu bạn có sẵn? stackoverflow.com/questions/17055213/ cường
Maximus S

1
@clst Tôi tin rằng đây là câu trả lời mà bạn đang tìm kiếm: guide.rubyonrails.org/asset_pipeline.html#precompiling-assets
FrontierPologistso

23

Tôi đánh giá cao tất cả các câu trả lời ... và tôi không nghĩ rằng họ đang thực sự gặp phải vấn đề. Một số trong số chúng là về kiểu dáng và dường như không liên quan ... và những cái khác chỉ đề cập đến javascript_include_tag... mà tôi biết là tồn tại (rõ ràng ...) nhưng có vẻ như cách Rails 3.1 sắp tới là kết thúc tất cả Javascript của bạn vào 1 tệp thay vì tải Javascript riêng lẻ ở cuối mỗi trang.

Giải pháp tốt nhất tôi có thể đưa ra là bọc một số tính năng nhất định trong divthẻ bằng ids hoặc classes. Trong mã javascript. Sau đó, bạn chỉ cần kiểm tra xem idhoặc classtrên trang, và nếu có, bạn chạy mã javascript được liên kết với nó. Theo cách này, nếu phần tử động không có trên trang, mã javascript sẽ không chạy - mặc dù nó được bao gồm trong application.jstệp lớn được đóng gói bởi Sprockets.

Giải pháp trên của tôi có lợi ích là nếu một hộp tìm kiếm được bao gồm trên 8 trong số 100 trang, nó sẽ chỉ chạy trên 8 trang đó. Bạn cũng sẽ không phải bao gồm cùng một mã trên 8 trang trên trang web. Trên thực tế, bạn sẽ không bao giờ phải đưa các thẻ script thủ công vào trang web của mình ở bất cứ đâu nữa - ngoại trừ việc có thể tải trước dữ liệu.

Tôi nghĩ rằng đây là câu trả lời thực sự cho câu hỏi của tôi.


Nhưng bạn thực sự muốn những <script>thẻ thủ công. Đúng, các lớp và id là một phần của câu trả lời, nhưng sẽ vô nghĩa khi người dùng tải JavaScript mà trang cụ thể đó không yêu cầu.
Marnen Laibow-Koser

4
@ MarnenLaibow-Koser lý do không thêm các thẻ script thủ công vào mỗi trang duy nhất là bạn phải tải xuống nội dung tập lệnh đó trên mỗi lượt xem trang. Nếu bạn có thể đóng gói tất cả javascript vào application.js bằng cách sử dụng đường dẫn tài sản, thì người dùng chỉ tải xuống các tập lệnh đó một lần và kéo application.js từ bộ đệm trên tất cả các lần tải trang tiếp theo
jakeonrails

@jakeonrails "lý do không thêm các thẻ script thủ công vào mỗi trang duy nhất là bạn phải tải xuống nội dung tập lệnh đó trên mỗi lượt xem trang". Tập lệnh sẽ được tải xuống một lần, sau đó sẽ được tìm nạp từ bộ đệm của trình duyệt theo các yêu cầu khác. "Nếu bạn có thể đóng gói tất cả javascript vào application.js bằng cách sử dụng đường dẫn tài sản, thì người dùng chỉ tải xuống các tập lệnh đó một lần duy nhất", nhưng với chi phí rất nhiều mã không cần thiết. Nếu bạn có thể cấu trúc JS của bạn thành nhiều tệp nhỏ thay vì một tệp lớn, bạn sẽ nhận được các lợi ích bộ đệm mà không cần mã không cần thiết.
Marnen Laibow-Koser

1
@ MarnenLaibow-Koser Tôi nghĩ sẽ tốt hơn nếu nói rằng nếu bạn đóng gói mọi thứ thành một tập lệnh, người dùng của bạn chỉ phải tải xuống 1 tập lệnh cho bất kỳ trang nào trên trang web của bạn. Nếu bạn có nhiều tập lệnh cho các phần khác nhau của ứng dụng thì rõ ràng người dùng phải tải xuống nhiều tập lệnh. Cả hai phương thức sẽ được lưu trữ, tất nhiên, nhưng trong hầu hết các trường hợp (ứng dụng vừa), việc phục vụ một application.js một lần sẽ hiệu quả hơn để tải xuống. Phân tích cú pháp của JS có thể là một câu chuyện khác, tùy thuộc vào những gì bạn phục vụ.
jakeonrails

1
@Ziggy Ngoài ra, nếu tệp nhỏ chỉ được sử dụng trên 8 trên 100 trang, tại sao mã phải nằm trong bộ đệm của người dùng mọi lúc? Tốt hơn là thực sự bỏ những thứ không cần thiết.
Marnen Laibow-Koser

16

Tôi nhận ra rằng tôi đến bữa tiệc này hơi muộn, nhưng tôi muốn đưa ra một giải pháp mà tôi đã sử dụng gần đây. Tuy nhiên, hãy để tôi đề cập đầu tiên ...

Đường ray 3.1 / 3.2 (Không, thưa ngài. Tôi không thích nó.)

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

Tôi bao gồm những điều sau đây vì mục đích hoàn chỉnh trong câu trả lời này và bởi vì đó không phải là một giải pháp không thể thực hiện được ... mặc dù tôi không quan tâm nhiều đến nó.

"Rails Way" là một giải pháp hướng đến bộ điều khiển, thay vì hướng theo quan điểm như tác giả ban đầu của câu hỏi này được yêu cầu. Có các tệp JS dành riêng cho bộ điều khiển được đặt tên theo các bộ điều khiển tương ứng của chúng. Tất cả các tệp này được đặt trong cây thư mục KHÔNG được bao gồm theo mặc định trong bất kỳ tệp application.js nào yêu cầu chỉ thị.

Để bao gồm mã dành riêng cho bộ điều khiển, phần sau đây được thêm vào dạng xem.

<%= javascript_include_tag params[:controller] %>

Tôi ghét giải pháp này, nhưng nó ở đó và nó nhanh chóng. Có lẽ, thay vào đó, bạn có thể gọi các tệp này một cái gì đó như "people-index.js" và "people-show.js" và sau đó sử dụng một cái gì đó như "#{params[:controller]}-index"để có được một giải pháp hướng theo quan điểm. Một lần nữa, sửa chữa nhanh chóng, nhưng nó không phù hợp với tôi.

Cách thuộc tính dữ liệu của tôi

Gọi tôi là điên, nhưng tôi muốn TẤT CẢ các JS của tôi được biên dịch và rút gọn thành application.js khi tôi triển khai. Tôi không muốn phải nhớ bao gồm các tệp straggler nhỏ này ở mọi nơi.

Tôi tải tất cả các mã JS của tôi trong một tệp nhỏ gọn, sắp có trong bộ nhớ cache của trình duyệt. Nếu một phần nhất định trong application.js của tôi cần được kích hoạt trên một trang, tôi sẽ để HTML cho tôi biết, không phải Rails.

Thay vì khóa JS của tôi vào ID phần tử cụ thể hoặc xả HTML của tôi bằng các lớp đánh dấu, tôi sử dụng một thuộc tính dữ liệu tùy chỉnh được gọi data-jstags.

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

Trên mỗi trang, tôi sử dụng - chèn phương thức thư viện JS ưa thích vào đây - để chạy mã khi DOM tải xong. Mã bootstrapping này thực hiện các hành động sau:

  1. Lặp lại tất cả các thành phần trong DOM được đánh dấu bằng data-jstag
  2. Đối với mỗi phần tử, phân chia giá trị thuộc tính trên không gian, tạo một chuỗi các chuỗi thẻ.
  3. Đối với mỗi chuỗi thẻ, hãy thực hiện tra cứu trong Hash cho thẻ đó.
  4. Nếu tìm thấy khóa khớp, hãy chạy hàm được liên kết với nó, truyền phần tử làm tham số.

Vì vậy, giả sử tôi có các định nghĩa sau ở đâu đó trong application.js của mình:

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

Sự kiện bootstrapping sẽ áp dụng các chức năng my_autosuggest_initmy_hint_initchức năng đối với đầu vào tìm kiếm, biến nó thành đầu vào hiển thị danh sách các đề xuất trong khi người dùng nhập, cũng như cung cấp một số loại gợi ý đầu vào khi đầu vào bị bỏ trống và không tập trung.

Trừ khi một số yếu tố được gắn thẻ data-jstag="auto-suggest", mã tự động đề xuất không bao giờ kích hoạt. Tuy nhiên, nó luôn ở đó, được thu nhỏ và cuối cùng được lưu trữ trong application.js của tôi cho những lần tôi cần nó trên một trang.

Nếu bạn cần chuyển các tham số bổ sung cho các hàm JS được gắn thẻ của mình, bạn sẽ phải áp dụng một số sáng tạo. Hoặc thêm các thuộc tính paramter dữ liệu, đưa ra một số loại cú pháp tham số hoặc thậm chí sử dụng một cách tiếp cận lai.

Ngay cả khi tôi có một số quy trình công việc phức tạp có vẻ đặc trưng cho bộ điều khiển, tôi sẽ chỉ tạo một tệp cho nó trong thư mục lib của mình, đóng gói nó vào application.js và gắn thẻ nó với một cái gì đó như 'new-thing-wizard'. Khi bootstrap của tôi chạm vào thẻ đó, trình hướng dẫn ưa thích, đẹp mắt của tôi sẽ được khởi tạo và chạy. Nó chạy cho (các) chế độ xem của bộ điều khiển đó khi cần, nhưng không được kết hợp với bộ điều khiển. Trong thực tế, nếu tôi mã đúng trình hướng dẫn của mình, tôi có thể cung cấp tất cả dữ liệu cấu hình trong chế độ xem và do đó có thể sử dụng lại trình hướng dẫn của mình sau này cho bất kỳ trình điều khiển nào khác cần.

Dù sao, đây là cách tôi đã triển khai JS cụ thể của trang bây giờ và nó đã phục vụ tốt cho tôi cả cho các thiết kế trang web đơn giản và cho các ứng dụng phức tạp / phong phú hơn. Hy vọng rằng một trong hai giải pháp tôi đã trình bày ở đây, theo cách của tôi hoặc theo cách của Rails, sẽ hữu ích cho bất kỳ ai gặp phải câu hỏi này trong tương lai.


6
Một chi tiết nhỏ: có một khái niệm trong câu trả lời của bạn rằng một khi js được lưu vào trình duyệt, nó không có tác động. Điều này không hoàn toàn đúng. Trình duyệt thực sự tránh tải xuống, nếu tệp js được lưu trữ đúng cách, nhưng nó vẫn biên dịch mã trên mỗi trang kết xuất. Vì vậy, bạn phải cân bằng sự đánh đổi. Nếu bạn có rất nhiều JS tổng hợp, nhưng chỉ một số được sử dụng trên mỗi trang, bạn có thể cải thiện thời gian trang bằng cách tách rời JS.
sujal

Để biết thêm về hiệu quả thiết thực đó bước biên dịch Tôi đang nói về, xem giải thích 37 Tín hiệu của pjax ảnh hưởng như thế nào Basecamp Next: 37signals.com/svn/posts/...
sujal

Đó là một điểm công bằng. Sau khi đọc bài viết và nhìn lại các dự án mà tôi đã sử dụng giải pháp trên, tôi nhận ra rằng tôi đã viết về cơ bản cùng một giải pháp "gửi HTML đã thay đổi" mà họ đề cập trong bài viết. Việc biên dịch lại thường xuyên của JS đã không phải là một vấn đề trong các dự án của tôi vì điều đó. Bước biên dịch là điều tôi sẽ ghi nhớ khi tôi làm việc trên các trang web ít "ứng dụng máy tính để bàn".
Ryan

2
Tải xuống cho "Gọi tôi là điên, nhưng tôi muốn TẤT CẢ mã JS của tôi được biên dịch và rút gọn thành application.js khi tôi triển khai." Bạn thực sự không muốn điều này, vì nó khiến người dùng tải JavaScript mà anh ta không cần và nó khiến trình xử lý của bạn tìm kiếm các thuộc tính thậm chí sẽ không có ở đó. Có tất cả mọi thứ trong app.js rất hấp dẫn và Rails chắc chắn làm cho nó dễ dàng, nhưng điều thích hợp để làm là mô đun hóa JavaScript tốt hơn.
Marnen Laibow-Koser

Bạn có quyền đưa ra một ý kiến ​​khác ... và về mặt kỹ thuật có quyền hạ thấp ý kiến ​​khác biệt. Tuy nhiên, thật tuyệt khi thấy một số bằng chứng về lý do tại sao một tệp lớn và được lưu trong bộ nhớ cache lại kém hơn khi buộc nhiều yêu cầu HTTP phải lấy JS được mô đun hóa. Hơn nữa, bạn đang nhầm lẫn về thủ tục tìm kiếm xử lý. Các giá trị thẻ KHÔNG được tìm kiếm. Chỉ có một tìm kiếm được thực hiện và nó kéo tất cả các thành phần có thuộc tính data-jstag. Nó không tìm kiếm theo tên thẻ, nó chỉ tìm tất cả các thành phần có thẻ và sau đó chỉ khởi tạo các đối tượng cần thiết.
Ryan

7

Điều này đã được trả lời và chấp nhận từ lâu, nhưng tôi đã đưa ra giải pháp của riêng mình dựa trên một số câu trả lời này và kinh nghiệm của tôi với Rails 3+.

Các đường ống tài sản là ngọt ngào. Sử dụng nó.

Đầu tiên, trong application.jstập tin của bạn , loại bỏ//= require_tree.

Sau đó, trong việc application_controller.rbtạo một phương thức trợ giúp của bạn :

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

Sau đó, trong application.html.erbtệp bố cục của bạn , hãy thêm trình trợ giúp mới của bạn trong số các javascript hiện có, bao gồm tiền tố với trình rawtrợ giúp:

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

Voila, bây giờ bạn có thể dễ dàng tạo javascript cụ thể bằng cách sử dụng cùng cấu trúc tệp bạn sử dụng ở mọi nơi khác trong đường ray. Đơn giản chỉ cần dán các tập tin của bạn vào app/assets/:namespace/:controller/action.js.erb!

Hy vọng rằng sẽ giúp người khác!


1
Điều này có gây ra sự cố sau khi tài sản được biên dịch trước và trong thời gian chạy, <%= raw ... %>nó sẽ trả về 404 không?
Nishant

Tôi nghĩ rằng đường dẫn tài sản là không ngọt, vì nó tạo ra một loạt các tệp thường không nên được sử dụng. Vì vậy, đối với tôi dựa vào đường ống tài sản đang tạo ra sự phụ thuộc vào một hệ thống không hiệu quả.
Deborah

1
@DeborahSpeece Khi nào đường ống tài sản tạo các tệp không nên được sử dụng? Bạn có nhầm lẫn giữa đường ống tài sản (tốt) với require_tree /(xấu) không?
Marnen Laibow-Koser

6

Bạn có thể thêm dòng này trong tệp bố cục của mình (ví dụ application.html.erb) để tự động tải tệp javascript cụ thể của trình điều khiển (dòng được tạo khi bạn tạo bộ điều khiển):

<%= javascript_include_tag params[:controller] %>

Bạn cũng có thể thêm một dòng để tự động tải tệp tập lệnh theo từng hành động.

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

Chỉ cần đặt các tập lệnh trang của bạn vào một thư mục con được đặt tên theo tên của bộ điều khiển. Trong các tệp này, bạn có thể bao gồm các tập lệnh khác bằng cách sử dụng = Yêu cầu. Sẽ thật tuyệt khi tạo một trình trợ giúp chỉ bao gồm tệp nếu nó tồn tại, để tránh lỗi 404 trong trình duyệt.


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

2
Điều này có vẻ như nó có thể trả lời câu hỏi. Bạn có thể vui lòng thêm nhiều hơn vào câu trả lời để xác thịt không?


5

Các LoadJS đá quý là một lựa chọn khác:

LoadJS cung cấp cách tải mã Javascript dành riêng cho trang trong ứng dụng Rails mà không mất phép thuật do Sprockets cung cấp. Tất cả mã Javascript của bạn sẽ tiếp tục được thu nhỏ trong một tệp Javascript nhưng một số phần của nó sẽ chỉ được thực thi cho một số trang nhất định.

https://github.com/guidomb/loadjs


3

Câu trả lời của Philip khá hay. Đây là mã để làm cho nó hoạt động:

Trong application.html.erb:

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

Giả sử bộ điều khiển của bạn được gọi là Dự án, điều đó sẽ tạo ra:

<body class="projects">

Sau đó, trong dự án.js.coffee:

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

Downvote: bất kỳ giải pháp nào đặt một lớp trên <body>ipso facto không chính xác. Xem ý kiến ​​của tôi ở nơi khác trên trang này.
Marnen Laibow-Koser

Đừng làm điều này. Vấn đề ở đây là mỗi khi bạn thêm một trong số này, bạn sẽ thêm một đoạn js khác cần được thực thi khi tải trang. Chắc chắn có thể gây ra một số suy giảm hiệu suất khi dự án của bạn phát triển.
ifightcrime

2

JavaScripts chỉ được hợp nhất khi bạn nói với Rails (Sprockets, thay vào đó) để hợp nhất chúng.


Tất nhiên. Tôi đoán tôi hỏi vì mặc định của Rails bao gồm mọi thứ trong thư mục ... có nghĩa là David dự định cho bạn làm điều đó. Nhưng như tôi đã nói trong phần bình luận khác với @rubyprince, tôi không chắc về việc thực thi khi nó được thực hiện theo cách này. Tôi đang nghĩ mình phải vô hiệu hóa //= require_tree .?
Fire Badge

@FireEm Flag Có. require_tree .thường là một ý tưởng tồi
Marnen Laibow-Koser

2

Đây là cách tôi giải quyết vấn đề kiểu dáng: (xin lỗi Haml)

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

Bằng cách này, tôi bắt đầu tất cả các tệp .css.sass cụ thể của trang bằng:

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

Bằng cách này bạn có thể dễ dàng tránh bất kỳ cuộc đụng độ. Khi nói đến tệp .js.coffee, bạn có thể khởi tạo các phần tử như;

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

Hy vọng điều này đã giúp một số.


1
Vui lòng đọc lại bit cuối cùng, đối với javascript, bạn có thể tận dụng cấu trúc tương tự được sử dụng cho biểu định kiểu để khởi tạo các chức năng xem cụ thể.
zeeraw

Philip, $('#post > #edit') ->dường như không hợp lệ. Làm thế nào để bạn phạm vi jQuery để làm việc trong một phạm vi?
Ramon Tayag

2
Gần đây, tôi đã bắt đầu tải tất cả các tập lệnh java và biểu định kiểu cụ thể của trình điều khiển bằng cách gọi tệp này trong application.html.haml; = javascript_include_tag "application"= javascript_include_tag params[:controller]theo cách này tôi có thể giữ mã javascript giới hạn mà không phải chỉ định phạm vi bên trong tệp.
zeeraw


2

Tôi đồng ý với câu trả lời của bạn, để kiểm tra xem bộ chọn đó có ở đó không, sử dụng:

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

(không thấy ai thêm giải pháp thực tế)


2

Tôi không thấy một câu trả lời thực sự kết hợp tất cả lại và đưa ra cho bạn. Vì vậy, tôi sẽ cố gắng đưa meleyal , sujal (a la ClosCowboy ), phần đầu tiên trong câu trả lời của Ryan và thậm chí cả tuyên bố táo bạo của Gal về Backbone.js ... tất cả cùng nhau theo một cách ngắn gọn và rõ ràng. Và, ai biết được, tôi thậm chí có thể đáp ứng yêu cầu của Marnen Laibow-Koser .

Chỉnh sửa ví dụ

tài sản / javascripts / application.js

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


lượt xem / bố cục / 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>


lượt xem / foo / index.html.erb

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


tài sản / javascripts / foo.js

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


tài sản / javascripts / foo ware / foothis.js.coffee

alert "Hello world!"


Mô tả ngắn gọn

  • Xóa //= require_tree .khỏi application.js và chỉ liệt kê JS mà mỗi trang chia sẻ.

  • Hai dòng hiển thị ở trên trong application.html.erb cho trang biết nơi bao gồm application.js và JS dành riêng cho trang của bạn.

  • Ba dòng hiển thị ở trên trong index.html.erb cho biết quan điểm của bạn để tìm kiếm một số JS cụ thể theo trang và đưa nó vào một vùng sản lượng được đặt tên có tên là ": javascript" (hoặc bất cứ điều gì bạn muốn đặt tên cho nó). Trong ví dụ này, bộ điều khiển là "foo" vì vậy Rails sẽ cố gắng đưa "foo.js" vào vùng: javascript năng suất trong bố cục ứng dụng.

  • Liệt kê JS cụ thể theo trang của bạn trong foo.js (hoặc bất cứ điều gì bộ điều khiển được đặt tên). Liệt kê các thư viện chung, một cây, thư mục, bất cứ điều gì.

  • Giữ JS cụ thể theo trang tùy chỉnh của bạn ở một nơi nào đó nơi bạn có thể dễ dàng tham chiếu nó ngoài JS tùy chỉnh khác của bạn. Trong ví dụ này, foo.js yêu cầu cây foo ware , vì vậy hãy đặt JS tùy chỉnh của bạn ở đó, chẳng hạn như foothis.js.coffee .

  • Không có quy tắc cứng ở đây. Thoải mái di chuyển mọi thứ xung quanh và thậm chí có thể tạo ra nhiều vùng sản lượng của các tên khác nhau trong các bố cục khác nhau nếu cần. Điều này chỉ cho thấy một bước đầu tiên có thể về phía trước. (Tôi không thực hiện chính xác như thế này khi chúng tôi sử dụng Backbone.js. Tôi cũng có thể chọn thả foo.js vào một thư mục có tên foo thay vì foo ware nhưng vẫn chưa quyết định điều đó.)

Ghi chú

Bạn có thể làm những điều tương tự với CSS và <%= stylesheet_link_tag params[:controller] %>điều này nằm ngoài phạm vi của câu hỏi.

Nếu tôi bỏ lỡ một thực hành tốt nhất rực rỡ ở đây, hãy gửi cho tôi một ghi chú và tôi sẽ thích nghi. Rails khá mới đối với tôi và thành thật mà nói, tôi không ấn tượng quá lớn với sự hỗn loạn mà nó mang lại theo mặc định cho sự phát triển doanh nghiệp và tất cả lưu lượng truy cập mà chương trình Rails trung bình tạo ra.


Điều này có vẻ như là cách để đi, tôi sẽ xem liệu tôi có thể thực hiện nó trong ứng dụng của mình không, cảm ơn vì câu trả lời chi tiết.
martincarlin87

1

Tôi có một giải pháp khác, mặc dù nguyên thủy hoạt động tốt với tôi và không cần bất kỳ chiến lược tải chọn lọc ưa thích nào. Đặt trong chức năng sẵn sàng tài liệu của bạn, nhưng sau đó kiểm tra vị trí cửa sổ hiện tại để xem đó có phải là trang mà javascript của bạn dành cho:

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

Điều này vẫn cho phép tất cả các js được tải bởi rails 3.x trong một gói nhỏ, nhưng không tạo ra nhiều chi phí hoặc bất kỳ xung đột nào với các trang mà js không có ý định.


1

Câu trả lời của ryguy là một câu trả lời hay, mặc dù nó đã bị hạ xuống thành điểm âm.

Đặc biệt là nếu bạn đang sử dụng một cái gì đó như Backbone JS - mỗi trang có chế độ xem Xương sống riêng. Sau đó, tệp erb chỉ có một dòng javascript nội tuyến kích hoạt lớp xem xương sống bên phải. Tôi coi đó là một dòng duy nhất của "mã keo" và do đó thực tế là nội tuyến của nó là ổn. Ưu điểm là bạn có thể giữ "request_tree" cho phép trình duyệt lưu tất cả các javascript.

trong show.html.erb, bạn sẽ có một cái gì đó như:

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

và trong tệp bố cục của bạn, bạn sẽ cần:

<%= yield :javascript %>

Hạ cấp. JavaScript nội tuyến không bao giờ là một ý tưởng tốt. Ngay cả khi đó là mã keo, nó vẫn phải ở trong một tệp bên ngoài.
Marnen Laibow-Koser

1

Di chuyển tất cả các tệp JS commom của bạn vào một thư mục con như 'app / property / javascript / global' sau đó trong application.js, sửa đổi //= require_tree .dòng thành //= require_tree ./global.

Bây giờ bạn có thể tự do đặt JS dành riêng cho bộ điều khiển của bạn trên root 'app / nội dung / javascript /' và chúng sẽ không được bao gồm trong JS được biên dịch, được sử dụng ngay khi bạn gọi chúng qua trình = javascript_include_tagđiều khiển / khung nhìn của bạn.


Không thể nào, đó là một đống JavaScript để tải cho một trang. Thậm chí không quan trọng nếu nó được lưu trữ.
jackyalcine

1

Mặc dù bạn có một vài câu trả lời ở đây, tôi nghĩ rằng chỉnh sửa của bạn có lẽ là đặt cược tốt nhất. Một mẫu thiết kế mà chúng tôi sử dụng trong nhóm của chúng tôi mà chúng tôi đã nhận được từ Gitlab là mẫu Dispatcher. Nó thực hiện một cái gì đó tương tự như những gì bạn đang nói, tuy nhiên tên trang được đặt trong thẻ body bằng đường ray. Ví dụ: trong tệp bố cục của bạn, chỉ cần bao gồm một cái gì đó như (trong HAML):

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

Sau đó, chỉ có một lần đóng và một câu lệnh chuyển đổi trong dispatcher.js.coffeetệp của bạn trong thư mục javascripts của bạn như vậy:

$ ->
  new Dispatcher()

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

Tất cả những gì bạn cần làm trong các tệp riêng lẻ ( ví dụ products.js.coffeehoặc nói login.js.coffee) là gửi chúng trong một lớp và sau đó toàn cầu hóa biểu tượng lớp đó để bạn có thể truy cập nó trong bộ điều phối:

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

Gitlab có một vài ví dụ về điều này mà bạn có thể muốn chọc ngoáy trong trường hợp bạn tò mò :)


1

Dự án Paloma cung cấp cách tiếp cận thú vị để quản lý mã javascript cụ thể của trang.

Ví dụ sử dụng từ tài liệu của họ:

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

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

1

Bước 1. xóa request_tree. trong application.js và application.css của bạn.

Bước 2. Chỉnh sửa application.html.erb (theo rails default) trong thư mục layout. Thêm "params [: controller]" trong các thẻ sau.

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

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

Bước 3. Thêm một tập tin trong cấu hình / khởi tạo / tài sản.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

tài liệu tham khảo: http://thefellingdeveloper.com/controll-specific-assets-with-rails-4/


Trong khi về mặt lý thuyết có thể trả lời câu hỏi, tốt hơn là nên bao gồm các phần thiết yếu của câu trả lời ở đây và cung cấp liên kết để tham khảo.
Bhargav Rao

0

Tôi đã không thử điều này, nhưng có vẻ như sau đây là đúng:

  • nếu bạn có một content_for là javascript (ví dụ với javascript thực sự bên trong nó), sprockets sẽ không biết về nó và do đó điều này sẽ hoạt động giống như bây giờ.

  • nếu bạn muốn loại trừ một tệp khỏi gói javascript lớn, bạn sẽ truy cập tệp config / sprockets.yml và sửa đổi source_files cho phù hợp. Sau đó, bạn sẽ chỉ bao gồm bất kỳ tệp nào bạn loại trừ khi cần.


Là loại trừ các tệp hoặc sử dụng javascript tùy chỉnh trên chính trang đó là "đúng cách"? Có phải đó là cách David dự định mọi người sử dụng nó?
Fire Badge

@FireEm Flag Tôi không quan tâm lắm đến những gì David dự định, vì tôi không nghĩ David hiểu cách tổ chức JavaScript đúng cách.
Marnen Laibow-Koser


0

Tôi kết hợp một số câu trả lời vào:

Người trợ giúp ứng dụng:

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

bố cục / 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

Thứ nhất: xóa \\=require_treekhỏi application.js Thứ hai: tất cả mã JS của bạn phải được sắp xếp tại /app/assets/javascritptvà tất cả mã CSS của bạn phải được sắp xếp tại/app/assets/stylesheets


-2

Theo sự dẫn dắt từ Ryan, đây là những gì tôi đã làm-

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 cụ thể của trình điều khiển, ví dụ: trình điều khiển: người dùng, hành động: bảng điều khiển)

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

Hạ cấp. Điều này thật vô lý - không đề cập đến sự không an toàn (do eval) nếu HTML của bạn bị xâm nhập bởi một máy chủ bị bẻ khóa hoặc một bản mô tả người dùng độc hại.
Marnen Laibow-Koser

-3

Đây là cách thực hiện đặc biệt là nếu bạn không phải thực thi hàng tấn thư viện cho trang cụ thể của mình, nhưng chỉ để chạy vài trăm dòng JS ít nhiều.

Vì việc nhúng mã Javascript vào HTML là hoàn toàn tốt, chỉ cần tạo trong thư mục app / view shared.js và đặt mã trang / trang cụ thể của bạn vào trong my_cool_partial.html.erb

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

Vì vậy, bây giờ từ bất cứ nơi nào bạn muốn bạn chỉ cần làm:

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

Và đó là nó, k?


2
Hạ cấp. JavaScript nội tuyến không bao giờ được khuyến khích. HTML chỉ nên chứa đánh dấu. JS và CSS phải ở trong các tệp riêng biệt, có thể sử dụng lại.
Marnen Laibow-Koser
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.