Rails - 如何仅在某些页面上包含 Javascript 文件
Rails - how to include Javascript files only on certain pages
我有一个 WYSIWYG 编辑器,我已将其内置到站点中并进行了自定义。有很多Javascript个文件只需要用所见即所得的编辑器加载到页面上;目前它们被加载到每个页面上(甚至在某些页面上破坏其他 Javascript)。
目前 Javascript 文件在 assets/javascript/wysiwyg/
中,并没有作为要求文件包含在 application.js
中,但由于资产管道的原因仍然包含在每个页面上(我认为).
我想知道我是否可以从其他页面中排除这些文件。是否可以将它们从资产管道移动到 public/
目录并将它们(也许在咖啡脚本文件中?)导入到适当的视图中?
您可以将要手动加载的任何 Javascript 文件放在应用程序的 public/javascripts/lib
目录中,它们不会包含在资产管道中。然后您可以在需要它们的页面上手动加载它们。
例如,在一个项目中,我使用 Chosen jQuery 插件,我这样加载它:
<script type="text/javascript" src="/javascripts/lib/chosen.jquery.min.js"></script>
Rails 将从 public/
获取 public 文件,因此您只需从那里引用您的文件(删除 public/
位)。
这个项目相当大,有 88 个控制器、662 个动作和总共 38 个自定义 javascript 库,这些库在应用程序中偶尔使用,包括降价编辑器、图表库,甚至 jQuery UI.
为了管理蔓延并尽可能保持每个页面紧凑,我做了两件事:1) 在我的控制器中,我设置了一个实例变量,@page_libs
,列出要加载的库,和 2) 布局使用 @page_libs
中的值以在需要时包含专业 Javascript。
控制器操作可能如下所示:
def edit
@products = products.find(params[:id])
@page_libs = [:ui, :textile]
end
app/views/layouts/application.html.erb
将其包含在正确的位置:
<%- if @page_libs&.include?(:ui) || @page_libs&.include?(:table) %>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/jquery-ui.min.js"></script>
<script type="text/javascript" src="/javascripts/lib/chosen.jquery.min.js"></script>
<% end -%>
<%- if @page_libs&.include?(:swiper) %>
<script type="text/javascript" src="/javascripts/lib/idangerous.swiper.min.js"></script>
<% end -%>
<%- if @page_libs&.include?(:table) %>
<script type="text/javascript" src="/javascripts/lib/jquery.handsontable.full.js"></script>
<% end -%>
<%- if @page_libs&.include?(:textile) %>
<script type="text/javascript" src="/javascripts/lib/textile.js" charset="utf-8"></script>
<% end -%>
请注意,第一个包含是 jQuery UI,我从 CDN 加载它,而不是从我的应用程序 public
加载。此技术与外部库以及您托管的库一样适用。事实上,我的应用程序中的大多数页面仅依赖于 2 个外部库 (jQuery and Underscore.js),但可以选择从外部源加载多达 16 个其他 Javascript 库。限制页面上的外部库可以显着减少页面加载时间,这会直接提升应用程序的性能。
有时,Javascript 库也会包含 CSS 组件。或者,您甚至可以包含特定于页面的 CSS。外部样式表可以采用相同的方法。这些是上述 Javascript 库的相应页面特定样式表 "includes":
<%- if @page_libs&.include?(:ui) %>
<link rel="stylesheet" type="text/css" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/themes/smoothness/jquery-ui.css">
<link rel="stylesheet" type="text/css" href="/stylesheets/lib/chosen.min.css">
<% end -%>
<%- if @page_libs&.include?(:swiper) %>
<link rel="stylesheet" type="text/css" href="/stylesheets/lib/idangerous.swiper.css">
<% end -%>
这样,无论一个页面需要多少(或几个)库,我在项目中都有一个单一的点来管理库。我可能最终会在 ApplicationController 中创建一系列自定义 before_action
处理程序来定义页面需要包含哪些库。像这样:
before_action: :include_library_ui, only: [:new, :edit]
before_action: :include_library_swiper, only: [:show]
这会稍微清理控制器操作并更容易识别依赖项。然而,考虑到我的代码库的大小和剩余的紧迫任务,我还没有迈出这一步。也许它会激励你开始这样做。
这已经过时了,我们正处在 Rails 6 的 Webpacker 世界中,但是如果您想以旧式 Sprockets 方式使事情变得更简单,您可能会喜欢下面描述的方法。请注意,它确实是 per-view - 其他答案有更广泛范围的好方法 per-controller stuff.
让您的主布局声明一个 'content for' 部分。例如,在 application.htm.erb
:
<!DOCTYPE html>
<html>
<head>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
...etc...
<%= content_for :head %>
</head>
...
</html>
在这里,:head
只是一个标签,你可以给它起任何你想要的名字。您也可以在布局中拥有任意数量的此类声明。它们只是 Rails 让视图将额外内容插入外部布局的那些位的方式。所以 - 您可以在各个视图中使用它在布局的 <head>
部分内添加您的 JS 文件 <script>
标签。
例如,假设我通过将 zxcvbn.js 库的一个 JavaScript 文件复制到 vendor/assets/javascripts/zxcvbn.js
中,手动“安装”了 zxcvbn.js 库。我有一个需要这个的 edit.html.erb
页面,所以在该文件的顶部,我添加:
<% content_for :head do %>
<%= javascript_include_tag('zxcvbn', 'data-turbolinks-track': 'reload') %>
<% end %>
...删除 Turbolinks 属性(如果您不使用它)。这意味着当 ERB 编译页面时,它将在布局的 'content for head' 部分内替换该标记,因此脚本标记最终出现在它的位置。这当然意味着在页面加载时进行额外的 HTTP 获取,但仅在使用它的视图中。在上面的示例中,JS 库非常大,通常只用于与用户更改密码相关的一两个地方;所以这是一个很大的胜利,而不是将它集中到编译的 application.js
中并在任何地方提供服务,即使它几乎从未被使用过。
content_for
的东西非常聪明,因为在引擎盖下非常简单。如果您的视图是由多个部分构建的,并且其中有多个部分进行了声明,则它们不会相互覆盖。每个都被连接到正确的位置,所以最终结果几乎是你所期望的,没有任何令人讨厌的惊喜。
您还需要执行一个步骤来避免 Sprockets 出现异常,因为您尝试包含的资产未预编译。您需要告诉 Sprockets 它存在;由于某种原因,这不是自动确定的。在 config/initializers/assets.rb
中,声明“out-of-band”/未包含在 e.g. 中的未知文件。 application.js
清单文件:
Rails.application.config.assets.precompile += %w( zxcvbn.js )
out-of-box Rails-generated assets.rb
有对此进行解释的评论和一个 commented-out 示例。
这一切都与资产管道正常合作,因此它与管道中的其他任何东西一样好(或差,取决于您的经验!),开发模式下的调试版本和生产中的缩小内容(主题到您的管道配置)。
我有一个 WYSIWYG 编辑器,我已将其内置到站点中并进行了自定义。有很多Javascript个文件只需要用所见即所得的编辑器加载到页面上;目前它们被加载到每个页面上(甚至在某些页面上破坏其他 Javascript)。
目前 Javascript 文件在 assets/javascript/wysiwyg/
中,并没有作为要求文件包含在 application.js
中,但由于资产管道的原因仍然包含在每个页面上(我认为).
我想知道我是否可以从其他页面中排除这些文件。是否可以将它们从资产管道移动到 public/
目录并将它们(也许在咖啡脚本文件中?)导入到适当的视图中?
您可以将要手动加载的任何 Javascript 文件放在应用程序的 public/javascripts/lib
目录中,它们不会包含在资产管道中。然后您可以在需要它们的页面上手动加载它们。
例如,在一个项目中,我使用 Chosen jQuery 插件,我这样加载它:
<script type="text/javascript" src="/javascripts/lib/chosen.jquery.min.js"></script>
Rails 将从 public/
获取 public 文件,因此您只需从那里引用您的文件(删除 public/
位)。
这个项目相当大,有 88 个控制器、662 个动作和总共 38 个自定义 javascript 库,这些库在应用程序中偶尔使用,包括降价编辑器、图表库,甚至 jQuery UI.
为了管理蔓延并尽可能保持每个页面紧凑,我做了两件事:1) 在我的控制器中,我设置了一个实例变量,@page_libs
,列出要加载的库,和 2) 布局使用 @page_libs
中的值以在需要时包含专业 Javascript。
控制器操作可能如下所示:
def edit
@products = products.find(params[:id])
@page_libs = [:ui, :textile]
end
app/views/layouts/application.html.erb
将其包含在正确的位置:
<%- if @page_libs&.include?(:ui) || @page_libs&.include?(:table) %>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/jquery-ui.min.js"></script>
<script type="text/javascript" src="/javascripts/lib/chosen.jquery.min.js"></script>
<% end -%>
<%- if @page_libs&.include?(:swiper) %>
<script type="text/javascript" src="/javascripts/lib/idangerous.swiper.min.js"></script>
<% end -%>
<%- if @page_libs&.include?(:table) %>
<script type="text/javascript" src="/javascripts/lib/jquery.handsontable.full.js"></script>
<% end -%>
<%- if @page_libs&.include?(:textile) %>
<script type="text/javascript" src="/javascripts/lib/textile.js" charset="utf-8"></script>
<% end -%>
请注意,第一个包含是 jQuery UI,我从 CDN 加载它,而不是从我的应用程序 public
加载。此技术与外部库以及您托管的库一样适用。事实上,我的应用程序中的大多数页面仅依赖于 2 个外部库 (jQuery and Underscore.js),但可以选择从外部源加载多达 16 个其他 Javascript 库。限制页面上的外部库可以显着减少页面加载时间,这会直接提升应用程序的性能。
有时,Javascript 库也会包含 CSS 组件。或者,您甚至可以包含特定于页面的 CSS。外部样式表可以采用相同的方法。这些是上述 Javascript 库的相应页面特定样式表 "includes":
<%- if @page_libs&.include?(:ui) %>
<link rel="stylesheet" type="text/css" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/themes/smoothness/jquery-ui.css">
<link rel="stylesheet" type="text/css" href="/stylesheets/lib/chosen.min.css">
<% end -%>
<%- if @page_libs&.include?(:swiper) %>
<link rel="stylesheet" type="text/css" href="/stylesheets/lib/idangerous.swiper.css">
<% end -%>
这样,无论一个页面需要多少(或几个)库,我在项目中都有一个单一的点来管理库。我可能最终会在 ApplicationController 中创建一系列自定义 before_action
处理程序来定义页面需要包含哪些库。像这样:
before_action: :include_library_ui, only: [:new, :edit]
before_action: :include_library_swiper, only: [:show]
这会稍微清理控制器操作并更容易识别依赖项。然而,考虑到我的代码库的大小和剩余的紧迫任务,我还没有迈出这一步。也许它会激励你开始这样做。
这已经过时了,我们正处在 Rails 6 的 Webpacker 世界中,但是如果您想以旧式 Sprockets 方式使事情变得更简单,您可能会喜欢下面描述的方法。请注意,它确实是 per-view - 其他答案有更广泛范围的好方法 per-controller stuff.
让您的主布局声明一个 'content for' 部分。例如,在 application.htm.erb
:
<!DOCTYPE html>
<html>
<head>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
...etc...
<%= content_for :head %>
</head>
...
</html>
在这里,:head
只是一个标签,你可以给它起任何你想要的名字。您也可以在布局中拥有任意数量的此类声明。它们只是 Rails 让视图将额外内容插入外部布局的那些位的方式。所以 - 您可以在各个视图中使用它在布局的 <head>
部分内添加您的 JS 文件 <script>
标签。
例如,假设我通过将 zxcvbn.js 库的一个 JavaScript 文件复制到 vendor/assets/javascripts/zxcvbn.js
中,手动“安装”了 zxcvbn.js 库。我有一个需要这个的 edit.html.erb
页面,所以在该文件的顶部,我添加:
<% content_for :head do %>
<%= javascript_include_tag('zxcvbn', 'data-turbolinks-track': 'reload') %>
<% end %>
...删除 Turbolinks 属性(如果您不使用它)。这意味着当 ERB 编译页面时,它将在布局的 'content for head' 部分内替换该标记,因此脚本标记最终出现在它的位置。这当然意味着在页面加载时进行额外的 HTTP 获取,但仅在使用它的视图中。在上面的示例中,JS 库非常大,通常只用于与用户更改密码相关的一两个地方;所以这是一个很大的胜利,而不是将它集中到编译的 application.js
中并在任何地方提供服务,即使它几乎从未被使用过。
content_for
的东西非常聪明,因为在引擎盖下非常简单。如果您的视图是由多个部分构建的,并且其中有多个部分进行了声明,则它们不会相互覆盖。每个都被连接到正确的位置,所以最终结果几乎是你所期望的,没有任何令人讨厌的惊喜。
您还需要执行一个步骤来避免 Sprockets 出现异常,因为您尝试包含的资产未预编译。您需要告诉 Sprockets 它存在;由于某种原因,这不是自动确定的。在 config/initializers/assets.rb
中,声明“out-of-band”/未包含在 e.g. 中的未知文件。 application.js
清单文件:
Rails.application.config.assets.precompile += %w( zxcvbn.js )
out-of-box Rails-generated assets.rb
有对此进行解释的评论和一个 commented-out 示例。
这一切都与资产管道正常合作,因此它与管道中的其他任何东西一样好(或差,取决于您的经验!),开发模式下的调试版本和生产中的缩小内容(主题到您的管道配置)。