UJS 代码组织和初始化

UJS Code Organization and Initialization

提问人:Swifthand 提问时间:1/22/2012 最后编辑:Swifthand 更新时间:3/14/2014 访问量:311

问:

我的问题分为两部分,看起来非常基本,但我在UJS或jQuery的介绍、指南或概述中没有看到任何讨论。我已经阅读了无数其他主题/问题,试图找到我可以复制风格或感觉的例子,但是那些问“我如何在jQuery中做X?”之类的玩具示例从未真正解释或演示他们的解决方案适合更大的应用程序的方式。

这样,我的问题是结构或建筑。

'tl;dr'版本的问题基本上是问“人们如何在大型应用程序中实际组织和初始化不显眼的JavaScript代码?具体说来:

  1. “当以不显眼的风格编写 JavaScript 时,如何跨文件组织代码?甚至应该这样做吗?(我知道只向客户端发送一个文件是有好处的,然后缓存该文件,所有后续加载速度都更快)。

  2. “如果相关的 JS 不再与页面显式加载,如何确保在正确的页面上正确初始化/调用/使用正确的代码,并且不会将时间浪费在运行与当前页面无关的选择器上?”

长版:

经过几个月的手工开发,我终于在Rails应用程序中使用了我自己脆弱的、现在已经足够好的JavaScript,我终于转向了UJS和jQuery的方式。我现在已经阅读了 http://jqfundamentals.com 的所有内容,除了“高级主题”部分。我很容易掌握jQuery的窍门,我的页面正在做我想让它们做的事,孤立地。

至于“不显眼的 JavaScript”,我不太清楚,但这个想法似乎主要是将 JS 完全分离到外部文件,并在事后通过以某种方式确定您所在的页面并在文档准备好后附加功能将 JS 功能捆绑到页面中。如果这不准确或不完整,请纠正我。

但我不明白的是如何组织我的代码并确保包含正确的 JavaScript 并将其附加到正确的页面。我能想象到的所有方法似乎要么效率极低,要么笨拙,要么使 UJS 的某些原则无效。

目前的情况是,我有一个庞大而笨拙的应用程序.js文件,其中塞满了各种选择器,这些选择器正在等待将功能放置到90%的页面上不存在的页面元素上。也就是说,第 1 页包含 A、B 和 C 项,而第 2 页包含 A、D 和 E 项。这经常导致事情破裂。我觉得我有两个令人不快的选择:

  1. 由于我想让服务器只发送一个大型 .js 文件,因此将我在 application.js 中的整个 JS 代码以某种方式包装在一个 switch 语句中,该语句计算出 (?) 它在哪个页面上,并且只对它知道应该在该页面上的元素进行 document.ready 调用。我完成这个任务的明显方法感觉非常丑陋,比如在页面中放置一个全局 JS 变量并检查它,这打破了最初吸引我的整个 UJS 禅宗。

  2. 将我的 JavaScript 拆分为不同的文件并有选择地加载它们。也就是说,“我在 view01.html.erb 中,因此我需要包含 view01.js”,它只处理 view01.html.erb 上的元素。起初这似乎更好,但是当涉及到部分,混合和匹配.js文件以及试图保持干燥时,很快就会变得笨拙。

我之所以选择UJS的这条路,是因为它具有非常干净、禅宗般的吸引力,但应用于一个更大的项目,它似乎比它的价值更麻烦。然而,比我聪明得多的人坚持不这样做,我完全同意内联和脚本标签绑定的 JavaScript 笨拙而丑陋,这让我相信我做错了什么。

到目前为止,我所做的(又名更长的版本,可能不值得您花时间阅读):

第 1 阶段:我正在开发一个 Rails 应用程序,因此我首先采用了许多 RailsCasts 教程中介绍的路线,这些教程演示了 application.js 中的代码,该代码通过选择器查找项目并向其附加一些功能。事情进展顺利,我有 2 或 3 个页面做了所有漂亮的 jQuery、Web2.0 ish、AJAX-y 的东西,这些东西现在很受孩子们的欢迎。棒!

第二阶段:然后我又写了一页,事情开始破裂。我在应用程序.js中的所有代码都通过$('selector')访问内容。这个新页面没有任何与其他页面相同的元素,因此当我从其他页面中的代码尝试获取 id='special_area' 的元素时,对 $('#special_area')[0] 的调用中断,因为它是空的。对每个选择器的结果进行 null 检查感觉是错误的,所以我最终摸索着找到了解决方案,即不使用我熟悉和喜爱的 DOM 元素,而是使用这些可以链接的花哨的 jQuery 对象。整洁。

第 3 阶段:我意识到,有了这些链接的jQuery对象,只要我所有的ID都是唯一的,或者我在每个页面上放置了某种唯一的DIV,页面上不存在的任何元素都会返回一个空集,并且(希望)不会中断。我还不知道,无论我想用它们做什么,它们都不会破裂,因为它们是空的,因此我不会假设是这种情况。但不知何故,我可以看到它通过暴力破解我整个应用程序中的所有元素并以某种方式智能地忽略当前页面上不存在的元素来勉强发挥作用。

此外,jQuery Fundamentals 指南反复讨论了继续运行选择器的成本。如果我只是暴力运行我所有的选择器,让它不应用那些不存在的选择器,我会在每个页面上放置一个巨大的静态开销,因为随着我的应用程序大小的增长,单个 .js 文件也是如此,每个页面都必须检查越来越多的元素实际上没有出现在它上面!

第 4 阶段:我了解了命名空间是如何在 JS 中完成的,虽然我计划使用它们,但我不认为它能解决这个特定问题。如果可以,请解释。除此之外,我表达失望和困惑的能力已经崩溃了,我想知道人们在现实生活中™是如何组织和初始化 UJS 代码的?

jQuery Ruby-on-Rails 架构 unobtrusive-javascript

评论

0赞 bubblez 10/2/2013
与其在每次页面加载时检查现有元素并有条件地执行当前页面的合适代码(这可能会导致性能不佳,正如您所说的那样),不如以相反的方式尝试。即:在每个特殊的布局/页面中,都包含一个小的内联 JavaScript(最小化请求数),它在您的 AppliAction.js 中调用正确的初始值设定项。该解决方案可能不严格遵循 UJS 范式,但它可能实用/有益,足以证明其使用是合理的。
0赞 steakchaser 3/2/2014
我意识到这并不能回答您上面的所有问题,但这是一个很好的起点,可以很好地控制给定控制器和操作的 JS 将运行/附加/任何内容:viget.com/inspire/......

答:

0赞 Stepan Riha 3/14/2014 #1

tl;博士回答

  1. 将您的代码拆分为 AMD 模块,然后使用 RequireJS 等 AMD 加载器加载它们。是的,应该这样做。
  2. 在您的部分视图中,只需执行相应的模块即可require()

更长的答案

使用 RequireJS 等 AMD 加载器,您可以将代码拆分到各个模块中。您还可以将共享代码(例如jQuery)存储在公共模块中,然后将其用作视图模块的先决条件。

您可以阅读异步模块定义RequireJS并将其与jQuery一起使用,但这是我处理它的方式:

  1. 让您的主要文档包含配置您的 AMD 加载程序。您可能希望加载共享模块(jQuery 等),以便浏览器可以在请求之间缓存它们。
  2. For each partial view, implement a coresponding JS module.
  3. In your partial view, add a script block to load the module and execute its initialization function

View Module

Your module will look something like this (we're using jQuery as an AMD module, too):

js/partials/view01.js

define(["jquery", function($) {
  "use strict";
  return {
    init: function() {
      // This is where you do your UJS work for this partial
      // ...
    }
  }
});

Partial View

In your partial view, you'll load the module and call the init function:

view01.html.erb

<script>
require("partials/view01", function(view) {
  view.init();
});
</script>
<!-- More HTML for this partial view -->