使用Rails 3.1,您将“特定于页面的” JavaScript代码放在哪里?


388

据我了解,您所有的JavaScript都合并为1个文件。默认情况下,Rails在添加//= require_tree .application.js清单文件的底部时会执行此操作。

这听起来像是真正的救命稻草,但我有点担心页面特定的JavaScript代码。此代码是否在每个页面上执行?我想要的最后一件事是,当只在一页上需要所有对象时,将为每页实例化我的所有对象。

此外,代码是否也可能发生冲突?

或者,您是否script在页面底部放置了一个小的标签,该标签只是调用执行该页面javascript代码的方法?

那么您不再需要require.js吗?

谢谢

编辑:我感谢所有的答案...,我认为他们并没有真正解决问题。其中一些是关于样式的,似乎没有关系...而其他人只是提到javascript_include_tag...,我知道确实存在(显然...),但是看来Rails 3.1的前进方向是包装所有内容。您的JavaScript分成1个文件,而不是在每个页面的底部加载单个JavaScript。

我能想到的最好的解决方案是divids或classes 将某些功能包装在标签中。在JavaScript代码中,您只需检查页面上的idclass,如果是,则运行与其关联的JavaScript代码。这样,如果动态元素不在页面上,则JavaScript代码不会运行-即使它已包含在application.jsSprockets打包的海量文件中。

我上面的解决方案的好处是,如果搜索框包含在100个页面中的8个页面中,它将仅在这8个页面上运行。您也不必在网站的8个页面中包含相同的代码。实际上,您再也不必在您的网站上再包含任何手动脚本标记。

我认为这是对我问题的实际答案。


11
“ Rails 3.1的前进方式是将所有Javascript打包到1个文件中,而不是在每个页面的底部加载单个Javascript。” —仅是因为Rails核心团队一直(而且一直以来)真的不知道如何管理JavaScript。小文件通常更好(请参阅其他地方的评论)。当涉及到JavaScript时,Rails的方法很少是正确的方法(除了资产管道(会打屁股)和鼓励CoffeeScript之外)。
Marnen Laibow-Koser 2012年

因此,您将在每个页面上包含特定于页面的js文件吗?我认为这很浪费,我更同意ClosureCowboy的回答。
gerky 2012年

1
您是否已查看该问题的可接受答案?stackoverflow.com/questions/6571753/...
rassom

1
@DutGRIFF换句话说:不,在这种情况下,最好不要以Rails的方式进行操作(或者至少不要将所有内容都放入application.js),事实上,您提供的参考资料指出了这样做的原因:下载是JS执行过程中最慢的部分。许多小文件比一个大文件更具可缓存性。这样,Unholy Rails的人们似乎并没有意识到他们的建议与他们要遵循的原则不一致,因此不应认真对待他们的建议。
Marnen Laibow-Koser 2014年

1
@DutGRIFF不,即使缓存了一个大的JS文件,通常也不是一件好事。请参阅我在此页面其他地方的评论:小文件可以更好地定位特定页面,并且可以更精细地缓存。除非完全没有特定页面的代码否则我看不到单个大文件有什么用例。
Marnen Laibow-Koser 2014年

Answers:


157

Asset Pipeline文档建议如何执行特定于控制器的JS:

例如,如果ProjectsController生成了a ,则在处将有一个新文件,app/assets/javascripts/projects.js.coffee而在处将有另一个文件app/assets/stylesheets/projects.css.scss。您应该将控制器特有的任何JavaScript或CSS放入其各自的资产文件中,因为然后可以使用诸如<%= javascript_include_tag params[:controller] %>或的行仅为这些控制器加载这些文件<%= stylesheet_link_tag params[:controller] %>

链接到:asset_pipeline


50
这是最优雅的方法。而且,您将需要删除// = require_tree行。从application.js.coffee
zsljulius

2
我完全同意这种方法。其他方法似乎很笨拙,但最终仍会加载一个巨大的js文件。我正在处理的项目在合并/缩小后几乎有2mb的JS文件/插件等。
Bill Garrison

2
我对Rails相当陌生,但是在我看来,这应该是默认行为。
Ross Hambrick

12
对于特定于动作的控件,我将其放置在布局中,因为并非每个控制器的每个动作都具有特定的JS。 page_specific_js = "#{params[:controller]}_#{params[:action]}"然后; javascript_include_tag page_specific_js if Rails.application.assets.find_asset page_specific_js
Sujimichi

2
控制器的特定动作是否仍会减少?是将它们添加到由链轮创建的单个js文件中,还是麋鹿这导致对资产文件的多个请求?
杰森

77

对于特定页面的js,您可以使用Garber-Irish解决方案

因此,对于两个控制器(汽车和用户),您的Rails javascripts文件夹可能看起来像这样:

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

而且javascript看起来像这样:

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

和markup_based_js_execution将包含UTIL对象的代码以及在DOM就绪的UTIL.init上执行的代码。

并且不要忘记将其放入布局文件中:

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

我还认为,最好使用类而不是data-*属性,以获得更好的特定于页面的CSS。正如Jason Garber所提到的:特定于页面的CSS选择器可能真的很尴尬(使用data-*属性时)

我希望这能帮到您。


4
如果您需要一个变量可用于用户控制器中的所有操作,但不适用于其他控制器,该怎么办?这种方法不存在范围界定问题吗?
tybro0103 2012年

@ tybro0103,我认为要实现此行为,您想window.varForOneController='val'在此控制器init函数中编写类似的内容。gon gem也可以在这里提供帮助(github.com/gazay/gon)。可能还有其他解决方法。
welldan97 2012年

1
@ welldan97不赞成您的解释-非常好-而是因为Garber-Irish结构是邪恶的。它会在每个页面上加载所有JS,并依赖<body>元素上的类和ID进行处理。这是与DOM斗争的明确标志:在通常情况下,<body>元素不需要类或ID,因为文档中只有一个。正确的方法只是删除//= require_tree .和使用特定于页面的JavaScript。如果您正在积极尝试不这样做,那么您就是在努力进行不良实践。
Marnen Laibow-Koser 2012年

2
@ MarnenLaibow-Koser我个人认为,将所有js合并到一个文件中并将其最小化时,将每个页面上的所有js加载都适合大多数项目。我相信它总体上可以为​​用户更快地工作。至少它更像是一个冲突的js文件与许多冲突的文件(例如,请查看stackoverflow.com/questions/555696/…)。同样,如果它使代码更简单并为您工作,则在主体上使用类和id也没有什么不好。Modernizr(modernizr.com)以及其他一些库也可以做到这一点。
welldan97 2012年

2
@ MarnenLaibow-Koser对我来说,rails资产管道似乎是与编译进行比较的不错的选择。程序员将它们的javascript用解耦的模块编写,然后将它们集中在一起,缩小并提供服务。就像编译语言一样,总是会有程序员认为他们比编译器领先一步……但是我认为这很少是真的。
Ziggy

65

我们知道您已经回答了自己的问题,但这是另一个选择:

基本上,您假设

//= require_tree .

是必须的。不是。随时将其删除。在我当前的应用程序中,老实说,我首先使用3.1.x,我制作了三个不同的顶级JS文件。我的application.js档案只有

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

这样,我可以创建带有自己的顶级JS文件的子目录,这些子目录仅包含我需要的内容。

关键是:

  1. 您可以删除require_tree-Rails可让您更改其所做的假设
  2. 名称没有什么特别的application.js- assets/javascript子目录中的任何文件都可以包含带有以下内容的预处理程序指令://=

希望有帮助,并为ClosureCowboy的答案添加一些细节。

苏加尔


8
+1对于像我这样的新手,很高兴知道。如果可以的话,我会给它+2。
jrhorn424'3

5
@sujal确实如此。Rails核心团队臭名昭著的JavaScript管理。随意忽略他们的建议,只使用资产管道中的部分。:)
Marnen Laibow-Koser 2012年

1
非常感谢您的建议。根据应用程序的模块,我没有几个“顶级” JS文件。效果很好。
elsurudo

1
+1对我来说,重要的一点是您可以替换为//= require_tree .//= require_directory .以便可以将所有现有文件保留在原处,并为页面特定文件创建新目录。
zelanix

41

另一个选择:创建页面或模型特定的文件,您可以在assets/javascripts/文件夹内创建目录。

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

application.js可以将主清单文件配置为从中加载其文件global/。特定页面或页面组可以具有自己的清单,这些清单从它们自己的特定目录中加载文件。Sprockets会自动将加载的文件application.js与特定于页面的文件合并在一起,从而使该解决方案有效。

也可以使用此技术style_sheets/


13
你现在让我渴望纸杯蛋糕.. Dangit!
Chuck Bergeron,

我真的很喜欢这个解决方案。我唯一的问题是这些多余的清单未压缩/丑化。虽然它们已正确编译。有解决方案还是我错过了什么?
clst

1
这是否意味着浏览器加载一个js文件,即全局+页面特定文件的组合?
lulalala 2012年

你能看看我的问题吗?stackoverflow.com/questions/17055213/...
鲆小号

1
@clst我相信这是您正在寻找的答案:guides.rubyonrails.org/asset_pipeline.html#precompiling-assets
FrontierPsycho

23

我很感谢所有答案...而且我认为他们并没有真正解决问题。其中一些是关于样式的,似乎没有关系...而其他人只是提到javascript_include_tag...,我知道确实存在(显然...),但是看来Rails 3.1的前进方向是包装所有内容。您的Javascript合并为1个文件,而不是在每个页面的底部加载单个Javascript。

我能想到的最好的解决方案是divids或classes 将某些功能包装在标签中。在javascript代码中。然后,您只需检查页面上的idclass是否存在,如果是,则运行与其关联的javascript代码。这样,如果动态元素不在页面上,则javascript代码不会运行-即使application.jsSprockets打包的海量文件中也包含了该代码。

我上面的解决方案的好处是,如果搜索框包含在100页中的8页中,则它将仅在这8页上运行。您也不必在网站的8个页面中包含相同的代码。实际上,您无需再在网站上的任何位置添加手动脚本标签-只需预先加载数据即可。

我认为这是对我问题的实际答案。


但是您实际上需要这些手动<script>标签。是的,类和id是答案的一部分,但对于用户而言,加载特定页面不需要的JavaScript没有任何意义。
Marnen Laibow-Koser 2012年

4
@ MarnenLaibow-Koser不向每个唯一页面添加手动脚本标签的原因是,您必须在每个页面视图上下载该脚本内容。如果您能够使用资产管道将所有JavaScript打包到application.js中,则用户只需下载一次这些脚本,然后在所有后续页面加载时从缓存中提取application.js
jakeonrails 2013年

@jakeonrails“未在每个唯一页面上添加手动脚本标签的原因是,您必须在每个页面视图上下载该脚本内容”,这是完全错误的。该脚本将被下载一次,然后根据进一步的请求从浏览器的缓存中获取。“如果您能够使用资产管道将所有javascript打包到application.js中,那么用户只能一次下载这些脚本。” —确实如此,但是代价是很多不必要的代码。如果您可以将JS构造为许多小文件而不是一个大文件,那么无需使用不必要的代码即可获得缓存优势。
Marnen Laibow-Koser 2013年

1
@ MarnenLaibow-Koser我认为最好是说,如果将所有内容打包到一个脚本中,则用户只需为网站的任何页面下载1个脚本。如果您对应用程序的不同部分具有多个脚本,那么显然用户必须下载多个脚本。当然,这两种方法都将被缓存,但是在大多数情况下(中小型应用程序),一次提供一个application.js的下载效率将更高。解析JS可能是另一回事,具体取决于您提供的服务。
jakeonrails

1
@Ziggy另外,如果小文件仅在100页中的8页上使用,为什么代码应始终位于用户的缓存中?最好实际丢弃不需要的东西。
Marnen Laibow-Koser 2013年

16

我知道我来参加这个聚会有点晚了,但是我想提出一个我最近一直在使用的解决方案。但是,我首先要提一下...

Rails 3.1 / 3.2的方式(不,先生。我不喜欢它。)

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

为了完整起见,我将以下内容包括在内,因为这不是可行的解决方案……尽管我不太在意。

“ Rails Way”是一个面向控制器的解决方案,而不是按此问题的原始作者要求的面向视图的解决方案。有特定于控制器的JS文件,它们以各自的控制器命名。所有这些文件都放在一个文件夹树中,该文件夹树默认情况下不包含在任何application.js require指令中。

为了包括特定于控制器的代码,将以下内容添加到视图中。

<%= javascript_include_tag params[:controller] %>

我讨厌这个解决方案,但是很快就可以了。大概,您可以改为将这些文件称为“ people-index.js”和“ people-show.js”,然后使用诸如"#{params[:controller]}-index"获取面向视图的解决方案之类的方法。再说一次,快速修复,但对我来说不好。

我的数据属性方式

让我发疯,但是我希望在部署时将所有JS编译并缩小到application.js中。我不想记住到处都包含这些散乱的小文件。

我将所有JS加载到一个紧凑的,即将被浏览器缓存的文件中。如果需要在页面上触发我的application.js的某个片段,我让HTML告诉我,而不是Rails。

我使用一个名为的自定义数据属性,而不是将JS锁定到特定的元素ID或用标记类乱扔HTML data-jstags

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

在每个页面上,我都使用-在此处插入首选的JS库方法-在DOM完成加载后运行代码。该引导代码执行以下操作:

  1. 遍历标有DOM的所有元素 data-jstag
  2. 对于每个元素,在空间上分割属性值,创建一个标签字符串数组。
  3. 对于每个标签字符串,在哈希中执行该标签的查找。
  4. 如果找到匹配的键,请运行与其关联的功能,并将元素作为参数传递。

假设我在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
};

引导事件将对搜索输入应用my_autosuggest_initmy_hint_init功能,将其转换为在用户键入时显示建议列表的输入,并在输入留空且未集中时提供某种输入提示。

除非使用标记某些元素data-jstag="auto-suggest",否则自动触发的代码永远不会触发。但是,它总是存在,缩小并最终在页面上需要的那些时间缓存在我的application.js中。

如果需要将其他参数传递给标记的JS函数,则必须施加一些创造力。添加数据参数属性,提出某种参数语法,甚至使用混合方法。

即使我有一些似乎特定于控制器的复杂工作流程,我也只会在我的lib文件夹中为其创建一个文件,将其打包到application.js中,并用“ new-thing-wizard”之类的标签对其进行标记。当我的引导程序点击该标签时,我的漂亮向导就会实例化并运行。它在需要时针对该控制器的视图运行,但未与该控制器耦合。实际上,如果我对向导进行了正确的编码,则也许可以在视图中提供所有配置数据,因此以后可以将向导重新用于需要它的任何其他控制器。

无论如何,这是我一段时间以来一直在实现页面特定的JS的方式,它对于简单的网站设计和更复杂/更丰富的应用程序都非常有用。希望我在这里介绍的两个解决方案之一(我的方式或Rails方式)对以后遇到此问题的任何人都有所帮助。


6
一个小细节:答案中有一个概念,即一旦将js缓存到浏览器中,它就不会产生影响。这不是真的。如果正确缓存了js文件,浏览器的确会避免下载,但仍会在每个页面渲染中编译代码。因此,您必须权衡取舍。如果总计有很多JS,但每页只使用了一些JS,则可以通过拆分JS来缩短页面时间。
sujal 2012年

欲了解更多关于该编译步骤我谈论的实际效果,看37 Signals的大本营接下来的pjax如何影响解释:37signals.com/svn/posts/...
sujal

这是一个公平的观点。阅读本文并回顾使用上述解决方案的项目后,我意识到我编写了与本文中提到的本质上相同的“发送更改后的HTML”解决方案。因此,在我的项目中,JS的频繁重新编译并不是问题。在不太面向“桌面应用程序”的网站上工作时,我会记住编译步骤。
瑞安

2
拒绝投票:“让我发疯,但部署时我希望将所有JS编译并缩小到application.js中。” 您确实不希望这样做,因为这会使用户加载他不需要的JavaScript,并且使处理程序查找甚至不存在的属性。将所有内容都包含在app.js中是很诱人的,Rails无疑使它变得容易,但是正确的做法是更好地模块化JavaScript。
Marnen Laibow-Koser 2012年

您有权有不同的意见...并且在技术上有权就不同意见进行投票。但是,很高兴看到一些理由,说明为什么一个大且缓存的文件不如强制多个HTTP请求抢占模块化JS。此外,您对于处理程序搜索过程也有误。不搜索标签值。只会执行一次搜索,并且会拉出所有具有data-jstag属性的元素。它不会按标签名称进行搜索,它只会查找所有具有标签的元素,然后仅实例化所需的对象。
瑞安

7

这个问题早在很早以前就已经得到了回答和接受,但是我根据其中一些答案以及我在Rails 3+方面的经验提出了自己的解决方案。

资产管道很不错。用它。

首先,在您的application.js文件中,删除//= require_tree.

然后在您application_controller.rb创建一个辅助方法中:

helper_method :javascript_include_view_js //Or something similar

def javascript_include_view_js
    if FileTest.exists? "app/assets/javascripts/"+params[:controller]+"/"+params[:action]+".js.erb"
        return '<script src="/assets/'+params[:controller]+'/'+params[:action]+'.js.erb" type="text/javascript"></script>'
    end
end

然后,在您的application.html.erb布局文件中,在现有的javascript包括中添加新的帮助器,并以该raw帮助器为前缀:

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

瞧,现在您可以使用在Rails中其他所有地方使用的相同文件结构轻松创建特定于视图的javascript。只需粘贴您的文件app/assets/:namespace/:controller/action.js.erb

希望对别人有帮助!


1
资产预编译后,在运行时<%= raw ... %>会返回404吗?
Nishant 2013年

我认为资产管道不甜,因为它会创建一堆经常不应该使用的文件。因此,对我来说,依赖资产管道会导致对效率低下的系统的依赖。
Deborah 2013年

1
@DeborahSpeece资产管道何时创建不应使用的文件?您是否在混淆资产管道(好)和require_tree /(坏)?
Marnen Laibow-Koser 2013年

6

您可以在布局文件(例如application.html.erb)中添加此行,以自动加载特定于控制器的javascript文件(生成控制器时创建的javascript文件):

<%= javascript_include_tag params[:controller] %>

您还可以添加一行以按操作自动加载脚本文件。

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

只需将页面脚本放入以控制器名称命名的子目录中即可。在这些文件中,您可以使用= require包含其他脚本。创建一个仅在文件存在时就包括该文件的助手将是很好的,以避免浏览器出现404错误。


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

2
看来它可以回答这个问题。您能否在答案中添加更多内容以充实自己?


5

LoadJS宝石是另一种选择:

LoadJS提供了一种在Rails应用程序中加载特定于页面的Javascript代码的方法,而不会失去Sprockets提供的魔力。您所有的Javascript代码都会在一个Javascript文件中继续最小化,但其中的某些部分只会在某些页面上执行。

https://github.com/guidomb/loadjs


3

菲利普的答案很好。这是使其工作的代码:

在application.html.erb中:

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

假设您的控制器称为“项目”,它将生成:

<body class="projects">

然后在projects.js.coffee中:

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

Downvoting:任何解决方案,看跌期权类的<body>在事实上不正确。在此页面的其他地方查看我的评论。
Marnen Laibow-Koser 2012年

不要这样 这里的问题是,每次添加其中一个时,您都会添加另一页需要在页面加载时执行的js。随着项目的增长,肯定会导致性能下降。
ifightcrime 2015年

2

仅当您告诉Rails(而不是Sprockets)合并JavaScript时,才会合并JavaScript。


当然。我想我问是因为Rails的默认值包含了文件夹中的所有内容……这意味着David打算让您这样做。但是就像我在@rubyprince的其他评论中所说的那样,我不确定执行此操作时的执行情况。我以为我必须禁用//= require_tree .
消防会徽

@FireEmblem是的。require_tree .通常是个坏主意。
Marnen Laibow-Koser 2012年

2

这就是我解决样式问题的方法:(对不起,Haml)

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

这样,我将使用以下命令启动所有页面特定的.css.sass文件:

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

这样,您可以轻松避免任何冲突。当涉及到.js.coffee文件时,您可以初始化元素,例如;

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

希望这对一些人有所帮助。


1
请再次阅读最后一点,对于javascript,您可以利用样式表所用的相同结构来初始化视图特定的功能。
zeeraw

菲利普,$('#post > #edit') ->似乎无效。您如何确定jQuery在某个范围内的工作范围?
拉蒙·塔亚格

2
最近,我通过在application.html.haml中调用它来开始加载所有控制器特定的Java脚本和样式表。= javascript_include_tag "application"= javascript_include_tag params[:controller]这样我可以保持JavaScript代码的局限,而无需指定文件的范围内。
zeeraw


2

我同意您的回答,要检查该选择器是否存在,请使用:

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

(没有看到任何人添加实际的解决方案)


2

我看不到真正能将所有内容组合在一起并为您提供答案的答案。因此,我将尝试将meleyalsujal(la ClosureCowboy),Ryan的答案的第一部分,甚至是Gal关于Backbone.js 大胆声明……以一种简短明了的方式结合在一起。而且,谁知道,我什至可以满足Marnen Laibow-Koser的要求。

范例编辑

资产/ Java脚本/ 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 %>


资产/ javascript / foo.js

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


资产/ javascripts / foostuff / foothis.js.coffee

alert "Hello world!"


简要描述;简介

  • 去掉 //= require_tree .application.js中,仅列出每个页面共享的JS。

  • 上面显示的两行 application.html.erb中告诉页面在哪里包括application.js和您的页面特定的JS。

  • 上面显示的三行 index.html.erb中告诉您的视图寻找某些特定于页面的JS,并将其包含在名为“:javascript”(或您想要命名的名称)的指定屈服区域中。在此示例中,控制器为“ foo”,因此Rails将尝试在应用程序布局的:javascript yield区域中包括“ foo.js”。

  • 列出您的页面特定JS foo.js(或任何命名的控制器)中。列出常见的库,树,目录等等。

  • 将自定义页面特定的JS放在某个地方,以便与其他自定义JS分开轻松地引用它。在此示例中,foo.js需要foostuff树,因此将您的自定义JS放在此处,例如 foothis.js.coffee)放在此处

  • 这里没有硬性规定。随意移动内容,甚至在需要时甚至可以创建具有各种名称的多个产量区域。这只是表明可能迈出的第一步。(鉴于我们使用Backbone.js的方式,我不会完全按照这种方式进行操作。我也可能选择将foo.js放到名为foo而不是foostuff的文件夹中,但尚未决定。)

笔记

您可以使用CSS进行类似的操作,<%= stylesheet_link_tag params[:controller] %>但这超出了问题的范围。

如果我错过了这里最出色的最佳实践,请给我发送便笺,我会考虑改编。Rails对我来说还很陌生,说实话,到目前为止,它对企业开发和平均Rails程序产生的所有流量所带来的混乱并没有给我留下深刻的印象。


这看起来像是要走的路,我要看看是否可以在自己的应用中实现它,谢谢详细的回答。
martincarlin87

1

我有另一种解决方案,尽管它对我来说很好用,并且不需要任何花哨的选择性加载策略。放入标准文档就绪功能,但是然后测试当前的Windows位置,以查看它是否是您的JavaScript所针对的页面:

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

这仍然允许所有的js在一个小包装中由rails 3.x加载,但是不会产生太多开销或与不适合js的页面发生任何冲突。


1

ryguy的答案是一个很好的答案,即使它被否决了负面观点。

特别是如果您使用的是Backbone JS之类的东西,则每个页面都有自己的Backbone视图。然后erb文件只有一行内联javascript,可以启动正确的主干视图类。我认为它是一行“胶水代码”,因此它的内联是可以的。好处是您可以保留您的“ require_tree”,这使浏览器可以缓存所有javascript。

在show.html.erb中,您将看到以下内容:

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

在您的布局文件中,您需要:

<%= yield :javascript %>

投票失败。内联JavaScript从来都不是一个好主意。即使它是粘合代码,也应位于外部文件中。
Marnen Laibow-Koser 2014年

1

将所有commom JS文件移动到“ app / assets / javascript / global”等子文件夹,然后在application.js中,将//= require_tree .行修改为//= require_tree ./global

现在,您可以自由地将控制器特定的JS放在'app / assets / javascript /'根目录下,它们将不包含在已编译的JS中,仅当您= javascript_include_tag在控制器/视图上通过调用它们时才使用它们。


没办法,那是一小段要加载的JavaScript。甚至不缓存也没关系。
jackyalcine 2012年

1

尽管您在这里有几个答案,但我认为您的编辑可能是最好的选择。从Gitlab获得的团队中使用的一种设计模式是Dispatcher模式。它的功能类似于您在说的,但是页面名称是通过rails在body标签中设置的。例如,在您的布局文件中,只需包含以下内容(在HAML中):

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

然后dispatcher.js.coffee,在javascripts文件夹中的文件中只有一个闭包和一个switch语句,如下所示:

$ ->
  new Dispatcher()

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

所有你需要在单独的文件做(说products.js.coffeelogin.js.coffee为例)是将它们用一个类,然后是全球化类符号,因此您可以在调度访问:

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

Gitlab有几个这样的例子,如果您好奇的话,您可能想参考一下:)


1

Paloma项目提供了一种有趣的方法来管理页面特定的javascript代码。

他们的文档中的用法示例:

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

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

1

步骤1。删除require_tree。在您的application.js和application.css中。

第2步。在布局文件夹中编辑application.html.erb(默认为rails)。在以下标记中添加“ params [:controller]”。

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

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

第三步 在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

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


尽管从理论上讲这可以回答问题,但最好在此处包括答案的基本部分,并提供链接以供参考。
巴尔加夫(Bhargav Rao)

0

我还没有尝试过,但是看起来像是这样:

  • 如果您的content_for是javascript(例如其中包含真正的javascript),则链轮将不知道该内容,因此它的工作方式与现在相同。

  • 如果要从一大堆javascript中排除文件,则应进入config / sprockets.yml文件并相应地修改source_files。然后,您只需在需要的地方包括任何排除的文件。


那么,排除文件或在页面本身上使用自定义JavaScript是“正确的方式”吗?是David想要人们使用它的方式吗?
消防会徽

@FireEmblem我不太关心David的意图,因为我不认为David理解如何正确组织JavaScript。
Marnen Laibow-Koser 2012年


0

我将一些答案合并为:

应用程序助手:

module ApplicationHelper
  def js_page_specific_include
    page_specific_js = params[:controller] + '_' + params[:action]
    if Rails.application.assets.find_asset(page_specific_js).nil?
      javascript_include_tag 'application', 'data-turbolinks-track' => true
    else
      javascript_include_tag 'application', page_specific_js, 'data-turbolinks-track' => true
    end
  end
end

layouts / application.html.haml:

 <!DOCTYPE html>
%html{lang: 'uk'}
  %head   
    = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
   bla-bla-bla
    = js_page_specific_include   
   bla-bla-bla  

0

首先:\\=require_tree从application.js中删除;其次:您所有的JS代码都必须位于,/app/assets/javascritpt而所有CSS代码必须都应位于/app/assets/stylesheets


-2

跟随瑞安(Ryan)的带头,这是我所做的-

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(特定于控制器的脚本,例如controller:users,action:dashboard)

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

投票失败。这真是令人费解的eval困扰-更不用说(由于导致的)不安全(如果您的HTML被破解的服务器或恶意用户脚本破坏了)。
Marnen Laibow-Koser 2014年

-3

特别是在不必为特定页面执行大量库而只需要多少运行几百行JS的情况下,这是特别的方法。

由于将Javascript代码嵌入HTML很好,因此只需在app / views shared.js目录下创建,然后在my_cool_partial.html.erb内放置页面的特定代码即可

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

因此,现在无论您想从哪里做,都可以:

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

就是这样,k?


2
投票失败。内联JavaScript绝对不可取。HTML仅应包含标记。JS和CSS应该在单独的可重用文件中。
Marnen Laibow-Koser 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.