使用 Rails 3.1,您将“特定于页面的”JavaScript 代码放在哪里?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6167805/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using Rails 3.1, where do you put your "page specific" JavaScript code?
提问by Fire Emblem
To my understanding, all of your JavaScript gets merged into 1 file. Rails does this by default when it adds //= require_tree .
to the bottom of your application.js
manifest file.
据我了解,您的所有 JavaScript 都合并到 1 个文件中。当 Rails 添加//= require_tree .
到application.js
清单文件的底部时,它会默认执行此操作。
This sounds like a real life-saver, but I am a little concerned about page-specific JavaScript code. Does this code get executed on every page? The last thing I want is for all of my objects to be instantiated for every page when they are only needed on 1 page.
这听起来像是一个真正的救星,但我有点担心特定于页面的 JavaScript 代码。这段代码会在每一页上执行吗?我想要的最后一件事是当我的所有对象只在一页上需要时,为每个页面实例化。
Also, isn't there potential for code that clashes too?
另外,代码是否也有可能发生冲突?
Or do you put a small script
tag at the bottom of the page that just calls into a method that executes the javascript code for the page?
或者你是否script
在页面底部放置了一个小标签,它只是调用一个为页面执行 javascript 代码的方法?
Do you no longer need require.js then?
那么你不再需要 require.js 了吗?
Thanks
谢谢
EDIT: I appreciate all the answers... and I don't think they are really getting at the problem. Some of them are about styling and don't seem to relate... and others just mention javascript_include_tag
... which I know exists (obviously...) but it would appear that the Rails 3.1 way going forward is to wrap up all of your JavaScript into 1 file rather than loading individual JavaScript at the bottom of each page.
编辑:我感谢所有的答案......我认为他们并没有真正解决问题。其中一些是关于样式的,似乎没有关系......而其他人只是提到javascript_include_tag
......我知道存在(显然......)但似乎Rails 3.1前进的方式是结束所有您的 JavaScript 到 1 个文件中,而不是在每个页面的底部加载单独的 JavaScript。
The best solution I can come up with is to wrap certain features in div
tags with id
s or class
es. In the JavaScript code, you just check if the id
or class
is on the page, and if it is, you run the JavaScript code that is associated with it. This way if the dynamic element is not on the page, the JavaScript code doesn't run - even though it's been included in the massive application.js
file packaged by Sprockets.
我能想到的最佳解决方案是div
用id
s 或class
es将某些功能包装在标签中。在 JavaScript 代码中,您只需检查id
或class
是否在页面上,如果是,则运行与其关联的 JavaScript 代码。这样,如果页面上没有动态元素,JavaScript 代码就不会运行——即使它已包含在application.js
Sprockets 打包的海量文件中。
My above solution has the benefit that if a search box is included on 8 of the 100 pages, it will run on only those 8 pages. You also won't have to include the same code on 8 of the pages on the site. In fact, you'll never have to include manual script tags on your site anywhere ever again.
我的上述解决方案的好处是,如果搜索框包含在 100 个页面中的 8 个页面上,它将仅在这 8 个页面上运行。您也不必在网站的 8 个页面上包含相同的代码。事实上,您再也不必在您的网站上的任何地方包含手动脚本标签了。
I think this is the actual answer to my question.
我认为这是我问题的实际答案。
采纳答案by Fire Emblem
I appreciate all the answers... and I don't think they are really getting at the problem. Some of them are about styling and don't seem to relate... and others just mention javascript_include_tag
... which I know exists (obviously...) but it would appear that the Rails 3.1 way going forward is to wrap up all of your Javascript into 1 file rather than loading individual Javascript at the bottom of each page.
我很欣赏所有的答案……而且我认为他们并没有真正解决问题。其中一些是关于样式的,似乎没有关系......而其他人只是提到javascript_include_tag
......我知道存在(显然......)但似乎Rails 3.1前进的方式是结束所有您的 Javascript 到 1 个文件中,而不是在每个页面的底部加载单独的 Javascript。
The best solution I can come up with is to wrap certain features in div
tags with id
s or class
es. In the javascript code. Then you just check if the id
or class
is on the page, and if it is, you run the javascript code that is associated with it. This way if the dynamic element is not on the page, the javascript code doesn't run - even though it's been included in the massive application.js
file packaged by Sprockets.
我能想到的最佳解决方案是div
用id
s 或class
es将某些功能包装在标签中。在javascript代码中。然后,您只需检查id
或class
是否在页面上,如果是,则运行与之关联的 javascript 代码。这样,如果页面上没有动态元素,javascript 代码就不会运行 - 即使它已包含在application.js
由 Sprockets 打包的海量文件中。
My above solution has the benefit that if a search box is included on 8 of the 100 pages, it will run on only those 8 pages. You also won't have to include the same code on 8 of the pages on the site. In fact, you'll never have to include manual script tags on your site anywhere ever again - except to maybe preload data.
我的上述解决方案的好处是,如果搜索框包含在 100 个页面中的 8 个页面上,它将仅在这 8 个页面上运行。您也不必在网站的 8 个页面上包含相同的代码。事实上,您再也不必在您的网站上的任何地方包含手动脚本标签了——除了可能预加载数据。
I think this is the actual answer to my question.
我认为这是我问题的实际答案。
回答by meleyal
The Asset Pipeline docs suggest how to do controller-specific JS:
Asset Pipeline 文档建议如何执行特定于控制器的 JS:
For example, if a
ProjectsController
is generated, there will be a new file atapp/assets/javascripts/projects.js.coffee
and another atapp/assets/stylesheets/projects.css.scss
. You should put any JavaScript or CSS unique to a controller inside their respective asset files, as these files can then be loaded just for these controllers with lines such as<%= javascript_include_tag params[:controller] %>
or<%= stylesheet_link_tag params[:controller] %>
.
例如,如果
ProjectsController
生成了 a ,则会有一个新文件 atapp/assets/javascripts/projects.js.coffee
和另一个 atapp/assets/stylesheets/projects.css.scss
。您应该将控制器独有的任何 JavaScript 或 CSS 放入其各自的资产文件中,因为这些文件可以仅为这些控制器加载,例如<%= javascript_include_tag params[:controller] %>
或<%= stylesheet_link_tag params[:controller] %>
。
回答by welldan97
For the page-specific js you can use Garber-Irish solution.
对于特定于页面的 js,您可以使用Garber-Irish 解决方案。
So your Rails javascripts folder might look like this for two controllers - cars and users:
因此,对于两个控制器(汽车和用户),您的 Rails javascripts 文件夹可能如下所示:
javascripts/
├── application.js
├── init.js
├── markup_based_js_execution
├── cars
│ ├── init .js
│ ├── index.js
│ └── ...
└── users
└── ...
And javascripts will look like this:
并且 javascripts 将如下所示:
// application.js
//=
//= require init.js
//= require_tree cars
//= require_tree users
// init.js
SITENAME = new Object();
SITENAME.cars = new Object;
SITENAME.users = new Object;
SITENAME.common.init = function (){
// Your js code for all pages here
}
// cars/init.js
SITENAME.cars.init = function (){
// Your js code for the cars controller here
}
// cars/index.js
SITENAME.cars.index = function (){
// Your js code for the index method of the cars controller
}
and markup_based_js_execution will contain code for UTIL object, and on DOM-ready UTIL.init execution.
和 markup_based_js_execution 将包含 UTIL 对象的代码,以及 DOM-ready UTIL.init 执行。
And don't forget to put this to your layout file:
并且不要忘记将其放入您的布局文件中:
<body data-controller="<%= controller_name %>" data-action="<%= action_name %>">
I also think that it is better to use classes instead of data-*
attributes, for the better page-specific css. As Jason Garber have mentioned: page-specific CSS selectors can get really awkward (when you use data-*
attributes)
我也认为最好使用类而不是data-*
属性,以获得更好的页面特定 css。正如 Jason Garber 所提到的:页面特定的 CSS 选择器可能会变得非常笨拙(当您使用data-*
属性时)
I hope this will help you.
我希望这能帮到您。
回答by sujal
I see that you've answered your own question, but here's another option:
我看到你已经回答了你自己的问题,但这是另一种选择:
Basically, you're making the assumption that
基本上,你是在假设
//= require_tree .
is required. It's not. Feel free to remove it. In my current application, the first I'm doing with 3.1.x honestly, I've made three different top level JS files. My application.js
file only has
是必须的。它不是。随意删除它。在我当前的应用程序中,老实说,我第一次使用 3.1.x,我已经制作了三个不同的顶级 JS 文件。我的application.js
文件只有
//= require jquery
//= require jquery_ujs
//= require_directory .
//= require_directory ./api
//= require_directory ./admin
This way, I can create subdirectories, with their own top level JS files, that only include what I need.
这样,我可以创建子目录,使用它们自己的顶级 JS 文件,只包含我需要的内容。
The keys are:
关键是:
- You can remove
require_tree
- Rails lets you change the assumptions it makes - There's nothing special about the name
application.js
- any file in theassets/javascript
subdirectory can include pre-processor directives with//=
- 您可以删除
require_tree
- Rails 允许您更改它所做的假设 - 名称没有什么特别之处
application.js
-assets/javascript
子目录中的任何文件都可以包含预处理器指令//=
Hope that helps and adds some details to ClosureCowboy's answer.
希望对 ClosureCowboy 的回答有所帮助并添加一些细节。
Sujal
苏加尔
回答by ClosureCowboy
Another option: to create page- or model-specific files, you could create directories inside your assets/javascripts/
folder.
另一种选择:要创建特定于页面或模型的文件,您可以在assets/javascripts/
文件夹中创建目录。
assets/javascripts/global/
assets/javascripts/cupcakes
assets/javascripts/something_else_specific
Your main application.js
manifest file could be configured to load its files from global/
. Specific pages or groups of pages could have their own manifests which load files from their own specific directories. Sprockets will automatically combine the files loaded by application.js
with your page-specific files, which allows this solution to work.
您的主application.js
清单文件可以配置为从global/
. 特定页面或页面组可以有自己的清单,从它们自己的特定目录加载文件。Sprockets 会自动将加载的文件application.js
与特定于页面的文件结合起来,这使得该解决方案能够发挥作用。
This technique can be used for style_sheets/
as well.
这种技术也可以用于style_sheets/
。
回答by Ryan
I realize I'm coming to this party a bit late, but I wanted to throw in a solution that I've been using lately. However, let me first mention...
我意识到我参加这个聚会有点晚了,但我想提出一个我最近一直在使用的解决方案。不过,我首先要提...
The Rails 3.1/3.2 Way (No, sir. I don't like it.)
Rails 3.1/3.2 方式(不,先生。我不喜欢它。)
See: http://guides.rubyonrails.org/asset_pipeline.html#how-to-use-the-asset-pipeline
请参阅:http: //guides.rubyonrails.org/asset_pipeline.html#how-to-use-the-asset-pipeline
I'm including the following for the sake of completeness in this answer, and because it's not an unviable solution... though I don't care much for it.
为了完整起见,我在此答案中包含以下内容,并且因为它不是一个不可行的解决方案......尽管我不太关心它。
The "Rails Way" is a controller-oriented solution, rather than being view-oriented as the original author of this question requested. There are controller-specific JS files named after their respective controllers. All of these files are placed in a folder tree that is NOT included by default in any of the application.js require directives.
“Rails Way”是一个面向控制器的解决方案,而不是像这个问题的原作者所要求的那样面向视图。有特定于控制器的 JS 文件以其各自的控制器命名。所有这些文件都放在一个文件夹树中,默认情况下不包含在任何 application.js require 指令中。
To include controller-specific code, the following is added to a view.
为了包含特定于控制器的代码,将以下内容添加到视图中。
<%= javascript_include_tag params[:controller] %>
I loathe this solution, but it's there and it's quick. Presumably, you could instead call these files something like "people-index.js" and "people-show.js" and then use something like "#{params[:controller]}-index"
to get a view-oriented solution. Again, quick fix, but it doesn't sit well with me.
我讨厌这个解决方案,但它就在那里,而且很快。据推测,您可以将这些文件称为“people-index.js”和“people-show.js”,然后使用类似的东西"#{params[:controller]}-index"
来获得面向视图的解决方案。再次,快速修复,但它不适合我。
My Data Attribute Way
我的数据属性方式
Call me crazy, but I want ALL of my JS compiled and minified into application.js when I deploy. I don't want to have to remember to include these little straggler files all over the place.
说我疯了,但我希望在部署时将我所有的 JS 编译并缩小到 application.js 中。我不想一定要记住把这些零散的小文件到处都是。
I load all of my JS in one compact, soon-to-be browser cached, file. If a certain piece of my application.js needs to be fired on a page, I let the HTML tell me, not Rails.
我将所有 JS 加载到一个紧凑的、即将被浏览器缓存的文件中。如果需要在页面上触发我的 application.js 的某个部分,我会让 HTML 告诉我,而不是 Rails。
Rather than locking my JS to specific element IDs or littering my HTML with marker classes, I use a custom data attribute called data-jstags
.
我没有将我的 JS 锁定到特定的元素 ID 或用标记类乱扔我的 HTML,而是使用一个名为data-jstags
.
<input name="search" data-jstag="auto-suggest hint" />
On each page, I use - insert preferred JS library method here -to run code when the DOM has finished loading. This bootstrapping code performs the following actions:
在每个页面上,我使用- 在此处插入首选 JS 库方法 -在 DOM 完成加载时运行代码。此引导代码执行以下操作:
- Iterate over all elements in the DOM marked with
data-jstag
- For each element, split the attribute value on space, creating an array of tag strings.
- For each tag string, perform a lookup in a Hash for that tag.
- If a matching key is found, run the function that is associated with it, passing the element as a parameter.
- 遍历 DOM 中标有的所有元素
data-jstag
- 对于每个元素,在空间上拆分属性值,创建一个标签字符串数组。
- 对于每个标签字符串,在该标签的哈希中执行查找。
- 如果找到匹配的键,则运行与其关联的函数,将该元素作为参数传递。
So say I have the following defined somewhere in my application.js:
所以说我在 application.js 的某处定义了以下内容:
function my_autosuggest_init(element) {
/* Add events to watch input and make suggestions... */
}
function my_hint_init(element) {
/* Add events to show a hint on change/blur when blank... */
/* Yes, I know HTML 5 can do this natively with attributes. */
}
var JSTags = {
'auto-suggest': my_autosuggest_init,
'hint': my_hint_init
};
The bootstrapping event is going to apply the my_autosuggest_init
and my_hint_init
functions against the search input, turning it into an input that displays a list of suggestions while the user types, as well as providing some kind of input hint when the input is left blank and unfocused.
bootstrapping 事件将对搜索输入应用my_autosuggest_init
和my_hint_init
函数,将其转换为在用户键入时显示建议列表的输入,并在输入为空白和未聚焦时提供某种输入提示。
Unless some element is tagged with data-jstag="auto-suggest"
, the auto-suggest code never fires. However, it's always there, minified and eventually cached in my application.js for those times that I need it on a page.
除非某个元素被标记为data-jstag="auto-suggest"
,否则自动建议代码永远不会触发。但是,它总是在那里,缩小并最终缓存在我的 application.js 中,以便我在页面上需要它。
If you need to pass additional parameters to your tagged JS functions, you'll have to apply some creativity. Either add data-paramter attributes, come up with some kind of parameter syntax, or even use a hybrid approach.
如果您需要向标记的 JS 函数传递额外的参数,则必须发挥一些创造力。添加数据参数属性,提出某种参数语法,甚至使用混合方法。
Even if I have some complicated workflow that seems controller-specific, I will just create a file for it in my lib folder, pack it into application.js, and tag it with something like 'new-thing-wizard'. When my bootstrap hits that tag, my nice, fancy wizard will be instantiated and run. It runs for that controller's view(s) when needed, but is not otherwise coupled to the controller. In fact, if I code my wizard right, I might be able to provide all configuration data in the views and therefore be able to re-use my wizard later for any other controller that needs it.
即使我有一些看起来特定于控制器的复杂工作流程,我也会在我的 lib 文件夹中为它创建一个文件,将其打包到 application.js 中,并用诸如“new-thing-wizard”之类的东西标记它。当我的引导程序命中该标签时,我漂亮、漂亮的向导将被实例化并运行。它在需要时为该控制器的视图运行,但不以其他方式耦合到控制器。事实上,如果我对我的向导进行正确编码,我可能能够在视图中提供所有配置数据,因此可以稍后将我的向导重新用于需要它的任何其他控制器。
Anyway, this is how I've been implementing page specific JS for a while now, and it has served me well both for simple site designs and for more complex/rich applications. Hopefully one of the two solutions I've presented here, my way or the Rails way, is helpful to anyone who comes across this question in the future.
无论如何,这就是我实现页面特定 JS 一段时间以来的方式,它对简单的站点设计和更复杂/丰富的应用程序都非常有用。希望我在这里提出的两种解决方案之一,我的方式或 Rails 方式,对将来遇到这个问题的任何人都有帮助。
回答by Mike A
This has been answered and accepted long ago, but I came up with my own solution based on some of these answers and my experience with Rails 3+.
很久以前就已经回答并接受了这个问题,但是我根据其中一些答案和我对 Rails 3+ 的经验提出了自己的解决方案。
The asset pipeline is sweet. Use it.
资产管道是甜蜜的。用它。
First, in your application.js
file, remove //= require_tree.
首先,在您的application.js
文件中,删除//= require_tree.
Then in your application_controller.rb
create a helper method:
然后在您application_controller.rb
创建一个辅助方法中:
helper_method :javascript_include_view_js //Or something similar
def javascript_include_view_js
if FileTest.exists? "app/assets/javascripts/"+params[:controller]+"/"+params[:action]+".js.erb"
return '<script src="/assets/'+params[:controller]+'/'+params[:action]+'.js.erb" type="text/javascript"></script>'
end
end
Then in your application.html.erb
layout file, add your new helper among the existing javascript includes, prefixed with the raw
helper:
然后在您的application.html.erb
布局文件中,在现有的 javascript 包含中添加您的新助手,并以raw
助手为前缀:
<head>
<title>Your Application</title>
<%= stylesheet_link_tag "application", :media => "all" %>
<%= javascript_include_tag "application" %>
<%= raw javascript_include_view_js %>
</head>
Voila, now you can easily create view-specific javascript using the same file structure you use everywhere else in rails. Simply stick your files in app/assets/:namespace/:controller/action.js.erb
!
瞧,现在您可以使用与 Rails 中其他任何地方使用的文件结构相同的文件结构轻松创建特定于视图的 javascript。只需粘贴您的文件app/assets/:namespace/:controller/action.js.erb
!
Hope that helps someone else!
希望能帮助别人!
回答by mcubik
You can add this line in your layout file (e.g. application.html.erb) to automatically load the controller specific javascript file (the one that was created when you generated the controller):
您可以在布局文件(例如 application.html.erb)中添加这一行以自动加载控制器特定的 javascript 文件(在您生成控制器时创建的文件):
<%= javascript_include_tag params[:controller] %>
You also could add a line to automatically load a script file in a per-action basis.
您还可以添加一行以在每个操作的基础上自动加载脚本文件。
<%= javascript_include_tag params[:controller] + "/" + params[:action] %>
Just put your page scripts into a subdirectoriy named after the controller name. In these files you could include other scripts using =require. It would be nice to create a helper to include the file only if it exists, to avoid a 404 fail in the browser.
只需将您的页面脚本放入以控制器名称命名的子目录中。在这些文件中,您可以使用 =require 包含其他脚本。如果文件存在,创建一个助手来包含文件会很好,以避免浏览器中的 404 失败。
回答by Mr Bohr
<%= javascript_include_tag params[:controller] %>
回答by peresleguine
Maybe you will find pluggable_jsgem as suitable solution.
也许你会发现pluggable_jsgem 作为合适的解决方案。