ChatGPT解决这个技术问题 Extra ChatGPT

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

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

这听起来像是一个真正的救生员,但我有点担心特定于页面的 JavaScript 代码。这段代码会在每一页上执行吗?我想要的最后一件事是为每个页面实例化我的所有对象,而这些对象只需要在一页上。

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

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

那么你不再需要 require.js 了吗?

谢谢

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

我能想到的最佳解决方案是用 idclass 将某些功能包装在 div 标记中。在 JavaScript 代码中,您只需检查页面上是否有 idclass,如果是,则运行与其关联的 JavaScript 代码。这样,如果动态元素不在页面上,则 JavaScript 代码不会运行 - 即使它已包含在 Sprockets 打包的大量 application.js 文件中。

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

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

“Rails 3.1 的前进方式是将所有的 Javascript 打包到一个文件中,而不是在每个页面的底部加载单独的 Javascript。”——仅因为 Rails 核心团队一直并且一直不知道如何做管理 JavaScript。小文件通常更好(请参阅我在其他地方的评论)。谈到 JavaScript,Rails 方式很少是正确的方式(除了资产管道,它踢屁股,以及 CoffeeScript 的鼓励)。
因此,您将在每个页面上包含特定于页面的 js 文件?我认为这是一种浪费,我更同意 ClosureCowboy 的回答。
你有没有看过这个问题的公认答案? stackoverflow.com/questions/6571753/…
@DutGRIFF 换句话说:不,在这种情况下最好不要以 Rails 方式做事(或者至少不要将所有内容都放在 application.js 中),实际上您提供的参考资料指出了为什么会这样:下载是 JS 执行过程中最慢的部分。许多小文件比一个大文件更容易缓存。 Unholy Rails 的人似乎没有意识到,他们的建议与他们试图遵守的原则不一致,因此他们的建议不应该被认真对待。
@DutGRIFF 不,即使缓存了大型 JS 文件,通常也不是一件好事。请参阅我在此页面其他地方的评论:小文件可以更好地针对特定页面,并且可以以更精细的粒度缓存。除非根本没有特定于页面的代码,否则我看不到单个大文件的任何好的用例。

a
ankit suthar

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

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

Link to: asset_pipeline


这是最优雅的方式。但是,您还需要删除行 //= require_tree 。来自 application.js.coffee
我完全同意这种方法。其他方法看起来很笨重,最终仍然会加载一个巨大的 js 文件。我正在处理的项目在合并/缩小后有将近 2mb 的 JS 文件/插件等。
我对 Rails 很陌生,但在我看来,这应该是默认行为。
对于特定于动作的控件,我的布局中有这个,因为并非每个控制器的每个动作都有特定的 JS。 page_specific_js = "#{params[:controller]}_#{params[:action]}" 然后; javascript_include_tag page_specific_js if Rails.application.assets.find_asset page_specific_js
控制器特定的操作是否仍然被缩小?它们是添加到由 sprockets 创建的单个 js 文件中,还是导致对资产文件的多次请求?
C
Chandru

对于特定于页面的 js,您可以使用 Garber-Irish solution

因此,对于两个控制器 - 汽车和用户,您的 Rails javascripts 文件夹可能如下所示:

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

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

和 markup_based_js_execution 将包含 UTIL 对象的代码,以及 DOM-ready 的 UTIL.init 执行。

不要忘记把它放到你的布局文件中:

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

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

我希望这能帮到您。


如果您需要一个对用户控制器中的所有操作都可用但在其他控制器中不可用的变量怎么办?这种方法没有一些范围问题吗?
@tybro0103,我认为要实现此行为,您想在此控制器初始化函数中编写类似 window.varForOneController='val' 的内容。 gon gem 也可以在这里提供帮助(github.com/gazay/gon)。可能还有其他解决方法。
@welldan97 否决不是因为您的解释——这很好——而是因为加伯爱尔兰结构是邪恶的。它会在每个页面上加载你所有的 JS,并依赖于 <body> 上的类和 ID。元素来整理东西。这是与 DOM 战斗的明确迹象:在正常情况下,<body> element 不需要类或 ID,因为文档中只有一个。执行此操作的正确方法是删除 //= require_tree . 并使用特定于页面的 JavaScript。如果您正在积极尝试不这样做,那么您正在努力进行不良实践。
@MarnenLaibow-Koser 我个人认为,当您将所有 js 合并到一个文件中并将其最小化时,在每个页面上加载所有 js 对大多数项目都有好处。我相信它总体上对用户来说工作得更快。至少它更像是一个 js 文件与多个冲突(即查看 stackoverflow.com/questions/555696/…)。如果它使代码更简单并且适合您,那么在 body 上使用类和 id 也没有什么不好。 Modernizr(modernizr.com) 可以做到这一点,其他一些库也是如此。
@MarnenLaibow-Koser 对我来说,rails 资产管道似乎是与编译进行比较的好选择。程序员在很好的解耦模块中编写他们的 javascript,然后将其集中在一起,缩小并提供服务。就像编译语言一样,总会有程序员认为他们比编译器领先一步……但我认为这很少是真的。
O
Outside_Box

我看到您已经回答了自己的问题,但这是另一种选择:

基本上,你假设

//= require_tree .

是必须的。它不是。随意删除它。在我当前的应用程序中,我第一次使用 3.1.x,老实说,我制作了三个不同的顶级 JS 文件。我的 application.js 文件只有

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

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

关键是:

您可以删除 require_tree - Rails 允许您更改它所做的假设 application.js 的名称没有什么特别之处 - assets/javascript 子目录中的任何文件都可以包含带有 //= 的预处理器指令

希望对 ClosureCowboy 的回答有所帮助并添加一些细节。

苏哈尔


+1 对于像我这样的新手来说,这真是太好了。如果可以的话,我会给它+2。
@sujal 没错。 Rails 核心团队因糟糕的 JavaScript 管理而臭名昭著。随意忽略他们的建议,只使用资产管道的好的部分。 :)
非常感谢这个建议。我没有几个“顶级” JS 文件,这取决于我的应用程序的模块。效果很好。
+1 对我来说重要的一点是,您可以将 //= require_tree . 替换为 //= require_directory .,这样您就可以将所有现有文件保留在原处,并为页面特定文件创建新目录。
C
Ciro Santilli Путлер Капут 六四事

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

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

您的主 application.js 清单文件可以配置为从 global/ 加载其文件。特定页面或页面组可以有自己的清单,从它们自己的特定目录加载文件。 Sprockets 会自动将 application.js 加载的文件与您的页面特定文件结合起来,从而使该解决方案发挥作用。

此技术也可用于 style_sheets/


你现在让我想吃纸杯蛋糕了.. Dangit!
我真的很喜欢这个解决方案。我唯一遇到的问题是那些额外的清单没有被压缩/丑化。虽然它们已正确编译。有解决方案还是我错过了什么?
这是否意味着浏览器加载一个js文件,即全局+页面特定文件的组合?
如果你有空,你能看看我的问题吗? stackoverflow.com/questions/17055213/…
@clst 我相信这是您正在寻找的答案:guides.rubyonrails.org/asset_pipeline.html#precompiling-assets
F
Fire Emblem

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

我能想到的最佳解决方案是用 idclass 将某些功能包装在 div 标记中。在 JavaScript 代码中。然后您只需检查 idclass 是否在页面上,如果是,则运行与其关联的 javascript 代码。这样,如果动态元素不在页面上,则 javascript 代码不会运行 - 即使它已包含在 Sprockets 打包的大量 application.js 文件中。

我上面的解决方案的好处是,如果 100 个页面中有 8 个包含搜索框,它将仅在这 8 个页面上运行。您也不必在网站的 8 个页面上包含相同的代码。事实上,您再也不必在您的网站上任何地方包含手动脚本标签——除了可能预加载数据。

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


但是您实际上想要那些手动 <script> 标记。是的,类和 id 是答案的一部分,但用户加载该特定页面不需要的 JavaScript 是没有意义的。
@MarnenLaibow-Koser 不向每个唯一页面添加手动脚本标签的原因是您必须在每个页面视图上下载该脚本内容。如果您能够使用资产管道将所有 javascript 打包到 application.js 中,那么用户只需下载一次这些脚本,并在所有后续页面加载时从缓存中提取 application.js
@jakeonrails“不向每个唯一页面添加手动脚本标签的原因是您必须在每个页面视图上下载该脚本内容”——完全错误。该脚本将被下载一次,然后将在进一步请求时从浏览器的缓存中获取。 “如果您能够使用资产管道将所有 javascript 打包到 application.js 中,那么用户只需下载一次这些脚本”——没错,但代价是大量不必要的代码。如果您可以将您的 JS 构建成许多小文件而不是一个大文件,那么您可以获得缓存优势,而无需编写不必要的代码。
@MarnenLaibow-Koser 我认为最好说如果您将所有内容打包到一个脚本中,您的用户只需为您网站的任何页面下载 1 个脚本。如果您的应用程序的不同部分有多个脚本,那么显然用户必须下载多个脚本。当然,这两种方法都会被缓存,但在大多数情况下(中小型应用程序),一次服务一个 application.js 会更有效地下载。 JS 的解析可能是另一回事,这取决于您提供的服务。
@Ziggy另外,如果小文件仅在100页中的8页上使用,为什么代码应该一直位于用户的缓存中?更好地实际丢弃不需要的东西。
R
Ryan

我意识到我来晚了一点,但我想提出一个我最近一直在使用的解决方案。不过,我首先要提...

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

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

为了完整起见,我在此答案中包含以下内容,并且因为它不是一个不可行的解决方案……尽管我不太关心它。

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

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

<%= 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 完成加载后运行代码。此引导代码执行以下操作:

遍历 DOM 中用 data-jstag 标记的所有元素 对于每个元素,在空间上拆分属性值,创建一个标签字符串数组。对于每个标签字符串,在该标签的 Hash 中执行查找。如果找到匹配的键,则运行与其关联的函数,将元素作为参数传递。

假设我在 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 方式,对将来遇到这个问题的任何人都有帮助。


一个小细节:你的答案中有一个概念,一旦 js 被浏览器缓存,它就没有影响。这并不完全正确。如果 js 文件被正确缓存,浏览器确实会避免下载,但它仍然会在每个页面渲染时编译代码。所以,你必须权衡取舍。如果您总共有很多 JS,但每个页面只使用一些,您可以通过拆分 JS 来缩短页面时间。
有关我正在谈论的那个编译步骤的实际效果的更多信息,请参阅 37 Signals 对 pjax 如何影响 Basecamp 的解释 Next:37signals.com/svn/posts/…
这是一个公平的观点。在阅读了这篇文章并回顾了我使用上述解决方案的项目后,我意识到我编写了与他们在文章中提到的基本相同的“发送更改的 HTML”解决方案。因此,频繁地重新编译 JS 在我的项目中并不是问题。当我在较少面向“桌面应用程序”的网站上工作时,我会牢记编译步骤。
反对“叫我疯了,但我希望在部署时将我的所有 JS 编译并缩小到 application.js 中。”你真的不想要这个,因为它会让用户加载他不需要的 JavaScript,并且它会让你的处理程序寻找甚至不存在的属性。将所有内容都放在 app.js 中是很诱人的,Rails 确实让它变得简单,但正确的做法是更好地模块化 JavaScript。
您有权发表不同的意见……从技术上讲,您有权对意见分歧投反对票。但是,很高兴看到为什么一个大的缓存文件不如强制多个 HTTP 请求来获取模块化 JS 的一些理由。此外,您对处理程序搜索过程有误。不搜索标签值。只执行一次搜索,它会拉出所有具有 data-jstag 属性的元素。它不按标签名称搜索,它只查找所有具有标签的元素,然后仅实例化所需的对象。
M
Mike A

很久以前就已经回答并接受了这个问题,但是我根据其中一些答案和我对 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 中!

希望对其他人有所帮助!


这不会在资产预编译后导致问题,并且在运行时 <%= raw ... %> 将返回 404 吗?
我认为资产管道并不甜,因为它创建了一堆通常不应该使用的文件。因此,对我来说,依赖资产管道正在创建对低效系统的依赖。
@DeborahSpeece 资产管道何时创建不应使用的文件?您是否将资产管道(好)与 require_tree /(坏)混淆了?
m
mcubik

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

<%= javascript_include_tag params[:controller] %>

您还可以添加一行以在每个操作的基础上自动加载脚本文件。

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

只需将您的页面脚本放入以控制器名称命名的子目录中。在这些文件中,您可以使用 =require 包含其他脚本。最好创建一个帮助程序以仅在文件存在时才包含该文件,以避免浏览器中出现 404 失败。


M
Mr Bohr
<%= javascript_include_tag params[:controller] %>

这看起来可能能够回答这个问题。您能否在答案中添加更多内容以充实它?
p
peresleguine

也许您会发现 pluggable_js gem 是合适的解决方案。


C
Community

LoadJS gem 是另一种选择:

LoadJS 提供了一种在 Rails 应用程序中加载特定于页面的 Javascript 代码的方法,而不会失去 Sprockets 提供的魔力。您的所有 Javascript 代码将继续缩小到一个 Javascript 文件中,但其中的某些部分只会在某些页面上执行。 https://github.com/guidomb/loadjs


R
Rahil Sondhi

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

在 application.html.erb 中:

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

假设您的控制器名为 Projects,它将生成:

<body class="projects">

然后在 projects.js.coffee 中:

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

否决:将类放在 <body> 上的任何解决方案事实上 不正确。在此页面的其他地方查看我的评论。
不要这样做。这里的问题是,每次添加其中一个时,都会添加另一段需要在页面加载时执行的 js。随着项目的增长,肯定会导致性能下降。
y
yfeldblum

只有当您告诉 Rails(而不是 Sprockets)合并它们时,JavaScript 才会合并。


当然。我想我问是因为 Rails 的默认设置包括文件夹中的所有内容......这意味着 David 打算让你这样做。但就像我在对@rubyprince 的另一条评论中所说的那样,我不确定以这种方式执行时是否会执行。我在想我必须禁用 //= require_tree . 吗?
@FireEmblem 是的。 require_tree . 通常是个坏主意。
z
zeeraw

这就是我解决样式问题的方法:(请原谅 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')

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


请再次阅读最后一点,对于 javascript,您可以利用用于样式表的相同结构来初始化视图特定功能。
Philip,$('#post > #edit') -> 似乎无效。你如何确定 jQuery 的范围以在一个范围内工作?
最近,我开始通过在 application.html.haml 中调用它来加载所有特定于控制器的 java 脚本和样式表; = javascript_include_tag "application"= javascript_include_tag params[:controller] 这样我就可以限制 javascript 代码,而无需在文件内指定范围。
C
Community

您还可以将 js 分组到文件夹中,并根据页面继续使用资产管道到 load your javascript selectively


A
Adi Inbar

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

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

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


j
juanitogan

我没有看到真正将所有内容放在一起并为您列出的答案。因此,我将尝试将 meleyal、sujal(a la ClosureCowboy)、Ryan 回答的第一部分,甚至 Gal 对 Backbone.js 的大胆声明……以一种简短明了的方式放在一起。而且,谁知道呢,我什至可能会满足 Marnen Laibow-Koser 的要求。

示例编辑

资产/javascripts/application.js

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

视图/布局/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>

意见/foo/index.html.erb

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

资产/javascripts/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”(或您想要命名的任何名称)的命名 yield 区域中。在本例中,控制器是“foo”,因此 Rails 将尝试在应用程序布局的 :javascript yield 区域中包含“foo.js”。

在 foo.js(或任何控制器名称)中列出您的页面特定 JS。列出常用库、树、目录等。

将您的自定义页面特定 JS 保存在您可以轻松引用它的地方,与您的其他自定义 JS 分开。在此示例中,foo.js 需要 foostuff 树,因此请将您的自定义 JS 放在那里,例如 foothis.js.coffee。

这里没有硬性规定。随意移动东西,如果需要,甚至可以在各种布局中创建多个不同名称的屈服区域。这只是显示了向前迈出的一个可能的第一步。 (考虑到我们使用 Backbone.js,我不会完全这样做。我也可能选择将 foo.js 放到一个名为 foo 而不是 foostuff 的文件夹中,但还没有决定。)

笔记

您可以使用 CSS 和 <%= stylesheet_link_tag params[:controller] %> 做类似的事情,但这超出了问题的范围。

如果我在这里错过了一个明显的最佳实践,请给我留言,我会考虑进行调整。 Rails 对我来说相当新,老实说,到目前为止,我对它默认给企业开发带来的混乱以及 Rails 程序平均产生的所有流量印象不深。


这看起来像是要走的路,我会看看我是否可以在我自己的应用程序中实现它,感谢您的详细回答。
P
Peter Abramowitsch

我有另一个解决方案,虽然原始对我来说很好,并且不需要任何花哨的选择性加载策略。放入您的正常文档准备功能,然后测试当前窗口位置以查看它是否是您的 javascript 用于的页面:

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

这仍然允许 Rails 3.x 在一个小包中加载所有 js,但不会产生太多开销或与不打算使用 js 的页面发生任何冲突。


G
Gal

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 从来都不是一个好主意。即使它是胶水代码,它也应该在外部文件中。
A
Adi Inbar

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

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


没办法,要为一页加载大量的 JavaScript。即使它被缓存也没关系。
o
onetwopunch

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

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

然后在您的 javascripts 文件夹中的 dispatcher.js.coffee 文件中只有一个闭包和一个 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 有几个这样的例子,如果你好奇的话,你可能想看看 :)


z
zavg

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

他们文档中的用法示例:

var UsersController = Paloma.controller('Users'); // 在执行 Rails User#new 时执行。 UsersController.prototype.new = function(){ alert('Hello Sexy User!' ); };


e
etlds

步骤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/


虽然这在理论上可以回答问题,但it would be preferable在此处包含答案的基本部分,并提供链接以供参考。
B
Bill Eisenhauer

我还没有尝试过,但看起来以下是正确的:

如果您有一个 content_for 是 javascript(例如,其中包含真正的 javascript),sprockets 将不知道它,因此它的工作方式与现在相同。

如果你想从 JavaScript 的大包中排除一个文件,你可以进入 config/sprockets.yml 文件并相应地修改 source_files。然后,您只需在需要时包含您排除的任何文件。


那么排除文件或在页面本身上使用自定义javascript是“正确的方式”吗?这就是大卫打算让人们使用它的方式吗?
@FireEmblem 我不太关心大卫的意图,因为我认为大卫不了解如何正确组织 JavaScript。
E
Eddie Prislac

我以前使用这种方法做过: http://theflyingdeveloper.com/controller-specific-assets-with-rails-4/ 。超级简单,依靠控制器来选择合适的 js 来加载。


k
kapellan

我将一些答案合并为:

应用助手:

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

布局/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  

N
Nicollas

第一:从 application.js 中删除 \\=require_tree 第二:您的所有 JS 代码必须位于 /app/assets/javascritpt 并且您的所有 CSS 代码必须位于 /app/assets/stylesheets


K
Kruttik

在 Ryan 的带领下,这就是我所做的-

应用程序.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(控制器特定的咖啡脚本,例如控制器:用户,操作:仪表板)

window.users_dashboard_onload = () ->
    alert("controller action called")
window.users_onload = () ->
    alert("controller called")

应用程序.html.haml

%body{:data=>{:view=>controller.controller_name, :action=>controller.action_name}}

投反对票。如果您的 HTML 被破解的服务器或恶意用户脚本破坏,这非常令人费解——更不用说不安全(由于 eval)。
v
valk

以下是如何做到这一点,特别是如果您不必为特定页面执行大量库,而只需或多或少地运行几百行 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?


投反对票。内联 JavaScript 永远不可取。 HTML 应该只包含标记。 JS 和 CSS 应该在单独的、可重用的文件中。