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

根据我的理解,所有的JavaScript都合并到一个文件中。默认情况下,当Rails将//= require_tree .添加到你的application.js清单文件的底部时,它会这样做。

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

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

还是在页面底部放置一个小的script标记,只调用一个执行页面javascript代码的方法?

你不再需要require.js了吗?

谢谢

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

我能想到的最好的解决方案是用__abc1或classes将某些特性包装在div标记中。在JavaScript代码中,只需检查页面上是否有idclass,如果是,则运行与之相关的JavaScript代码。这样,如果动态元素不在页面上,JavaScript代码就不会运行——即使它包含在由Sprockets打包的庞大application.js文件中。

我上面的解决方案的好处是,如果一个搜索框包含在100个页面中的8个页面上,那么它将只在这8个页面上运行。你也不需要在网站的8个页面上包含相同的代码。事实上,您再也不需要在站点的任何地方包含手动脚本标记。

我想这就是我问题的真正答案。

81296 次浏览

JavaScripts只有在你告诉Rails(链轮)合并它们时才会合并。

我还没有尝试过这个方法,但看起来下面的方法是正确的:

  • 如果你有一个content_for是javascript(例如与真正的javascript在里面),链轮将不知道它,因此这将以同样的方式工作,因为它现在。

  • 如果你想从javascript包中排除一个文件,你可以进入config/sprockets。并相应地修改source_files文件。然后,您只需在需要的地方包括您排除的任何文件。

这就是我如何解决样式问题:(请原谅哈姆)

%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_include_tag…我知道这是存在的(很明显…),但Rails 3.1的前进方式似乎是将所有Javascript打包到一个文件中,而不是在每个页面的底部加载单个Javascript。

我能想到的最好的解决方案是用__abc1或classes将某些特性包装在div标记中。在javascript代码中。然后你只需检查idclass是否在页面上,如果是,你就运行与它相关的javascript代码。这样,如果动态元素不在页面上,javascript代码就不会运行——即使它包含在由Sprockets打包的庞大application.js文件中。

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

我想这就是我问题的真正答案。

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

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

你的主application.js清单文件可以配置为从global/加载它的文件。特定的页面或页面组可以有它们自己的清单,这些清单从它们自己的特定目录加载文件。链轮将自动结合application.js加载的文件与特定于页面的文件,这允许这个解决方案工作。

此技术也可以用于style_sheets/

菲利普的回答很好。下面是让它工作的代码:

在application.html.erb:

< p > <代码> & lt;身体类= " & lt; % = params[:控制器].parameterize %祝辞”比; < /代码> < / p >

假设你的控制器叫做Projects,它会生成:

< p > <代码> & lt;身体类= "项目"比; < /代码> < / p >

然后在projects.js.coffee中:

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

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

所以你的Rails javascripts文件夹对于两个控制器(cars和users)是这样的:

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上。初始化执行。

别忘了把这个放到你的布局文件中:

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

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

我希望这对你有所帮助。

资产管道文档建议如何做特定于控制器的JS:

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

asset_pipeline链接

我知道你已经回答了自己的问题,但还有另一种选择

基本上,你假设

//= require_tree .

是必需的。它不是。请随意删除它。在我当前的应用程序中,第一个应用程序使用3.1。老实说,我已经做了三个不同的顶级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的回答有所帮助,并添加了一些细节。

Sujal

我知道我来这个派对有点晚了,但我想提出一个我最近一直在用的解决方案。然而,让我首先提一下……

The Rails 3.1/3.2 Way(不,先生,我不喜欢它。)

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

为了完整起见,我在这个回答中包括以下内容,因为这不是一个不可行的解决方案……虽然我不太喜欢。

“Rails方式”是一个面向控制器的解决方案,而不是像这个问题的原始作者所要求的那样面向视图。有一些特定于控制器的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。

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

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

在每个页面上,我使用-在这里插入首选JS库方法-在DOM完成加载时运行代码。这段引导代码执行以下操作:

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

所以说,我有以下定义在我的应用程序。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
};

bootstrapping事件将对搜索输入应用my_autosuggest_initmy_hint_init函数,将其转换为一个输入,在用户输入时显示建议列表,以及在输入为空白和不集中时提供某种输入提示。

除非某些元素被标记为data-jstag="auto-suggest",否则自动提示代码永远不会触发。然而,它总是在那里,缩小并最终缓存在我的application.js中,以便我在页面上需要它。

如果你需要将额外的参数传递给你标记的JS函数,你将不得不应用一些创造性。要么添加数据参数属性,提出某种参数语法,甚至使用混合方法。

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

不管怎样,这就是我现在实现特定页面JS的方式,它对简单的站点设计和更复杂/丰富的应用程序都很有帮助。希望我在这里介绍的两种解决方案之一,我的方式或Rails的方式,对将来遇到这个问题的任何人都有帮助。

在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(特定于控制器的coffeescript,e。G控制器:用户,动作:仪表盘)

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

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

<%= javascript_include_tag params[:controller] %>

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

<%= javascript_include_tag params[:controller] + "/" + params[:action] %>
只需将页面脚本放到以控制器名称命名的子目录中。在这些文件中,您可以使用=require包含其他脚本。 最好是创建一个helper,只在文件存在时包含该文件,以避免在浏览器中出现404错误

我有另一个解决方案,虽然原始的工作对我来说很好,不需要任何花哨的选择性加载策略。放入普通的文档准备函数,然后测试当前的窗口位置,看看它是否是你的javascript所针对的页面:

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

这仍然允许rails 3加载所有的js。X放在一个小包中,但不会产生太多开销,也不会与js不打算用于的页面产生任何冲突。

我同意你的回答,检查选择器是否存在,使用:

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

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

你也可以将js分组在文件夹中,并根据页面继续使用资产管道有选择地加载javascript

这个问题在很久以前就被回答和接受了,但是我基于其中一些答案和我使用Rails 3+的经验提出了我自己的解决方案。

资产管道是甜蜜的。使用它。

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

然后在你的application_controller.rb中创建一个helper方法:

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 include中添加你的新helper,并以raw helper为前缀:

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

希望这能帮助到其他人!

Ryguy的答案是一个很好的答案,尽管它被投票为负分。

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

在show.html。你会得到这样的东西:

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

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

<%= yield :javascript %>

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

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

<%= javascript_include_tag params[:controller] %>

LoadJS是另一个选项:

LoadJS提供了一种在Rails应用程序中加载特定页面Javascript代码的方法,而不会失去由Sprockets提供的魔力。你所有的Javascript代码都将被压缩到一个Javascript文件中,但它的某些部分只会在某些页面上执行。

https://github.com/guidomb/loadjs

也许你会发现pluggable_js宝石作为合适的解决方案。

下面是如何做到这一点,特别是如果你不需要为特定的页面执行大量的库,而只需要运行几百行JS或多或少。

因为它完全可以将Javascript代码嵌入到HTML中,只需在app/views共享.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?

我没有看到一个答案,真正把它放在一起,并为你铺开。因此,我将尝试将meleyalsujal (la ClosureCowboy), 瑞恩的答案的第一部分,甚至加的关于Backbone.js的粗体声明…以一种简短而清晰的方式把它们放在一起。而且,谁知道呢,我甚至可能满足Marnen Laibow-Koser的的要求。

例子编辑

资产/ javascript / application.js

//= require jquery
//= require jquery_ujs
//= require lodash.underscore.min
...
< p >
视图/布局/ application.html.erb < / p >
  ...
</footer>


<!-- Javascripts ================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<%= javascript_include_tag "application" %>
<%= yield :javascript %>


</body>
</html>
< p >
视图/ foo / index.html.erb < / p >
...
<% content_for :javascript do %>
<%= javascript_include_tag params[:controller] %>
<% end %>
< p >
资产/ javascript / foo.js < / p >
//= require moment
//= require_tree ./foostuff
< p >
资产/ javascript / foostuff / foothis.js.coffee < / p >
alert "Hello world!"


简要描述

  • application.js中删除//= require_tree .,只列出每个页面共享的JS。

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

  • 上面index.html中显示的三行。>告诉你的视图寻找一些特定于页面的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程序产生的所有流量并没有太大印象。

我之前使用这个方法:http://theflyingdeveloper.com/controller-specific-assets-with-rails-4/。超级简单,依赖于控制器选择适当的js来加载。

虽然你在这里有几个答案,我认为你的编辑可能是最好的选择。我们在团队中使用的一个设计模式是从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有几个这样的例子,如果你好奇的话,你可能想要戳一下:)

我把一些答案组合成:

应用助手:

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

帕洛玛项目提供了一个有趣的方法来管理页面特定的javascript代码。

他们文档中的用法示例:

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


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

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

步骤3。在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
< p >引用: http://theflyingdeveloper.com/controller-specific-assets-with-rails-4/ < / p >
首先:从application.js中删除__abc0 第二:所有的JS代码必须位于/app/assets/javascritpt,所有的CSS代码必须位于/app/assets/stylesheets