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

IT技术 javascript ruby-on-rails ruby-on-rails-3.1 asset-pipeline sprockets
2021-02-01 13:45:31

据我了解,您的所有 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.js由 Sprockets 打包的海量文件中。

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

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

6个回答

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

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

链接到:asset_pipeline

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

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

因此,对于两个控制器(汽车和用户),您的 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'在这个控制器 init 函数中编写类似的东西gon gem 也可以在这里提供帮助(github.com/gazay/gon)。可以有其他解决方法。
2021-04-02 13:45:31
@MarnenLaibow-Koser 我个人认为,当您将所有 js 合并到一个文件中并将其最小化时,在每个页面上加载所有 js 对大多数项目都有好处。我相信它总体上对用户来说工作得更快。至少它更像是一个 js 文件与多个 js 文件的冲突(即查看stackoverflow.com/questions/555696/...)。如果在 body 上使用类和 id 可以使代码更简单并且适合您,那么它也没有什么不好。Modernizr( modernizr.com ) 这样做,其他一些库也这样做。
2021-04-04 13:45:31
@MarnenLaibow-Koser 对我来说,rails 资产管道似乎是与编译进行比较的不错选择。程序员在很好的解耦module中编写他们的 javascript,然后将其集中在一起、缩小并提供服务。就像编译语言的情况一样,总会有程序员认为他们比编译器领先一步……但我认为这很少是真的。
2021-04-05 13:45:31
如果您需要一个变量可用于用户控制器中的所有操作,但在其他控制器中不可用,该怎么办?这种方法没有一些范围问题吗?
2021-04-06 13:45:31
@welldan97 不赞成你的解释——这很好——而是因为 Garber-Irish 结构是邪恶的。它在每个页面上加载您所有的 JS,并依赖于 <body> 元素上的类和 ID 来解决问题。这是对抗 DOM 的明确标志:在正常情况下,<body> 元素不需要类或 ID,因为文档中只有一个。执行此操作的正确方法是删除//= require_tree .并使用特定于页面的 JavaScript。如果您正在积极尝试不这样做,那么您就是在努力进行不良练习。
2021-04-09 13:45:31

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

基本上,你是在假设

//= 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 的回答有所帮助并添加一些细节。

苏加尔

+1 对我来说,重要的一点是您可以替换为//= require_tree .//= require_directory .这样您就可以将所有现有文件保留在原处,并为页面特定文件创建新目录。
2021-03-11 13:45:31
+1 对于像我这样的新手来说,知道这一点真是太好了。如果可以的话,我会给它+2。
2021-03-15 13:45:31
@sujal 没错。Rails 核心团队以糟糕的 JavaScript 管理而臭名昭著。随意忽略他们的建议,只使用资产管道好的部分。:)
2021-03-30 13:45:31
非常感谢这个建议。我没有几个“顶级”JS 文件,这取决于我的应用程序的module。效果很好。
2021-04-02 13:45:31

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

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

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

这种技术也可以用于style_sheets/

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

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

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

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

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

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