Rails - 如何仅在某些页面上包含Javascript文件

时间:2016-05-21 07:13:11

标签: javascript ruby-on-rails ruby ruby-on-rails-4 asset-pipeline

我有一个WYSIWYG编辑器,我已在网站中内置并自定义。有许多Javascript文件只需要使用WYSIWYG编辑器加载到页面上;目前它们被加载到每个页面上(甚至在某些页面上打破其他Javascript)。

目前,Javascript文件位于assets/javascript/wysiwyg/中,并未作为application.js中的需求文件包含在内,但由于资产管道(我认为),因此仍会包含在每个页面中。

我想知道我是否可以从其他页面中排除这些文件。是否可以将它们从资产管道移动到public/目录并将它们(在咖啡脚本文件中,可能?)导入相应的视图中?

2 个答案:

答案 0 :(得分:16)

您可以将要手动加载的任何Javascript文件放在应用程序的public/javascripts/lib目录中,它们不会包含在资产管道中。然后,您可以在需要它们的页面上手动加载它们。

例如,在一个项目中,我使用Chosen jQuery插件,我就像这样加载它:

<script type="text/javascript" src="/javascripts/lib/chosen.jquery.min.js"></script>

Rails将从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个外部库(jQueryUnderscore.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]

这会更多地清理控制器操作,并使识别依赖关系变得更容易。但是,考虑到我的代码库的大小和剩下的紧迫任务,我还没有采取这种飞跃。也许它会鼓励你这样做才能开始。

答案 1 :(得分:0)

现在这已经越来越老了,我们处于Rails 6的Webpacker世界中,但是如果您想以老式的Sprockets方式简化事情,您可能会喜欢下面描述的方法。请注意,这实际上是按视图进行的-其他答案对于按控制器的更广泛范围也有很好的解决方法。

让您的主布局声明一个“内容为”部分。例如,在application.htm.erb中:

<!DOCTYPE html>
<html>
  <head>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    ...etc...

    <%= content_for :head %>

  </head>
  ...
</html>

在这里,:head只是一个标签,您可以给它指定任何名称。您也可以根据需要在布局中包含任意数量的此类声明。它们只是Rails允许视图向外部布局的那些部分插入额外内容的方式。因此-您可以在各个视图中使用它,在布局的<script>部分中添加JS文件<head>标签。

例如,假设我已通过将其一个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编译页面时,它将在布局的“内容开头”部分中替换该标签,因此script标签将最终在其位置。当然,这将意味着在页面加载时进行额外的HTTP提取,但仅在使用页面的视图中进行。在上面的示例中,JS库很大,通常只用于一两个与用户更改密码有关的地方;因此,即使将其几乎从未使用过,也可以将其放入已编译的application.js中并在任何地方使用,这是一个巨大的胜利。

content_for的内幕非常简单,因此非常聪明。如果您的视图是由多个部分构成的,并且其中不止一个进行了声明,则它们不会相互覆盖。每个变量都被连接到正确的位置,因此最终结果几乎是您期望的,没有任何令人讨厌的惊喜。

您还需要采取另一步骤来避免Sprockets出现异常,因为您要包含的资产未预先编译。您需要告诉Sprockets它存在;由于某种原因,这不是自动确定的。在config/initializers/assets.rb中,声明“带外” /未知文件,这些文件未包含在例如application.js清单文件:

Rails.application.config.assets.precompile += %w( zxcvbn.js )

Rails生成的开箱即用assets.rb带有注释对此进行了解释,并带有注释示例。

这一切都与资产管道正常协作,因此它与管道中的任何其他事物一样好(或坏,取决于您的经验!),开发模式中的调试版本和生产中的最小内容(取决于您)管道配置)。