UJS代码组织和初始化
我的问题由两部分组成,看起来非常基本,但我在 UJS 或 jQuery 的介绍、指南或概述中没有看到任何讨论。我已经阅读了无数其他主题/问题,试图找到我可以复制其风格或感觉的示例,但是那些询问“如何在 jQuery 中执行 X?”的玩具示例等等,从来没有真正解释或演示他们的解决方案如何适应更大的应用程序。
这样,我的问题是结构性的或建筑性的。
“tl;dr”版本的问题基本上会问“人们实际上是如何在大型应用程序中组织和初始化不引人注目的 JavaScript 代码的?”具体来说:
“以不显眼的风格编写 JavaScript 时,如何跨文件组织代码?是否应该这样做?” (我知道只向客户端发送一个文件是有好处的,然后该文件会被缓存,并且所有后续加载都会更快)。
“如果相关的 JS 不再显式地随页面加载,如何确保正确的代码在正确的页面上正确初始化/调用/使用,并且时间不会浪费在运行不相关的选择器上到当前页面?”
长版本:
在我们的 Rails 应用程序中手工编写我自己的脆弱的、目前已经足够好的 JavaScript 几个月之后,我终于转换到 UJS 和 jQuery 的方式。我现在已经阅读了所有 http://jqfundamentals.com,除了“高级主题”部分。我很容易掌握 jQuery 的窍门,并且我的页面正在做我想要的事情,独立。
至于“Unobtrusive JavaScript”,我不太清楚,但这个想法似乎主要是将 JS 完全分离为外部文件,并在事后通过以某种方式确定您所在的页面,将 JS 功能捆绑到页面中并在文档准备好后附加功能。如果这不准确或不完整,请纠正我。
但我不明白的是如何组织我的代码并确保包含正确的 JavaScript 并将其附加到正确的页面。我能想象到的所有方法似乎要么效率极低、笨拙,要么使 UJS 的一些原则失效。
目前的情况是,我有一个庞大且笨重的 application.js 文件,其中塞满了各种选择器,这些选择器正在等待将功能放置到 90% 的页面上不存在的页面元素上。也就是说,第 1 页有项目 A、B 和 C,而第 2 页有项目 A、D 和 E。这经常会导致出现问题。我觉得我只剩下两个令人不快的选择:
由于我想让服务器只发送一个大的 .js 文件,因此将 application.js 中的整个 JS 代码以某种方式包装在一个 switch 语句中,该 switch 语句可以计算出 (? )它位于哪个页面上,并且只使 document.ready 调用它知道应该位于该页面上的元素。 可以实现这一点的明显方法感觉非常丑陋,例如在页面内放置一个全局 JS 变量并检查它,这打破了最初吸引我的 UJS 的整个禅宗。
将我的 JavaScript 分成不同的文件并有选择地加载它们。也就是说,“我在 view01.html.erb 中,因此我需要包含 view01.js”,它只处理 view01.html.erb 上的元素。乍一看这似乎更好,但当涉及部分、混合和匹配 .js 文件以及尝试保持 DRY 时,它会变得很笨重。
我走上了 UJS 这条路,因为它有一种非常干净、禅宗般的吸引力,但应用于更大的项目时,它似乎比它的价值更麻烦。然而,比我聪明得多的人却坚持不然,而且我完全同意内联和脚本标签绑定的 JavaScript 既笨重又丑陋,这让我相信我做错了什么。
到目前为止我做了什么 (又名甚至更长的版本,可能不值得您花时间阅读):
第一阶段:我正在开发 Rails 应用程序,因此我首先采用了许多 RailsCasts 教程中介绍的路线演示 application.js 中的代码,该代码通过选择器查找项目并向其附加一些功能。事情进展顺利,我用 2 或 3 个页面完成了所有现在很受孩子们欢迎的漂亮的 jQuery、Web2.0 式、AJAX 式的东西。惊人的!
第 2 阶段:然后我又制作了一页,事情开始出现问题。我的所有代码都在 application.js 中,通过 $('selector') 访问内容。这个新页面没有任何与其他页面相同的元素,因此当其他页面的代码尝试获取 id='special_area' 的元素时,调用 $('#special_area')[0 ] 中断,因为它是空的。对每个选择器的结果进行 Null 检查感觉都是错误的,因此我最终摸索出一种解决方案:不使用我所了解和喜爱的 DOM 元素,而是使用这些可以链接的奇特 jQuery 对象。整洁的。
阶段 3: 我意识到,使用这些链接的 jQuery 对象,只要我的所有 ID 都是唯一的,或者我在每个页面上放置某种唯一的 DIV,页面上不存在的任何元素将返回一个空集并且(希望)不会中断。我还没有足够的知识来肯定地说,无论我想用它们做什么,它们都不会破裂,因为它们是空的,因此我不会假设情况是这样。但不知何故,我可以看到它通过暴力破解整个应用程序中的所有元素并以某种方式智能地忽略当前页面上不存在的元素来稍微解决问题。
此外,jQuery 基础指南反复讨论了继续运行选择器的成本有多大。如果我只是强力运行所有选择器并让它不应用那些不存在的选择器,那么我就会在每个页面上放置巨大的静态开销,因为随着我的应用程序大小的增长,单个页面也会增加.js 文件,每个页面都必须检查越来越多实际上没有出现在其上的元素!
阶段 4: 我了解了如何在 JS 中完成名称空间,虽然我计划使用它们,但我不认为它可以解决这个特定问题。如果可以的话,请解释一下。除此之外,我表达失望和困惑的能力已经崩溃,我想知道人们实际上如何在现实生活™中组织和初始化UJS代码?
My two-part question is something that seems very basic, but which I have not seen any discussion of in introductions, guides, or overviews to either UJS or jQuery. I have read through countless other topics/questions to try and find examples I could just copy the style or feel of, but the toy examples that ask 'How do I do X in jQuery?' and the like, never really explain or demonstrate the manner in which their solutions fit into a larger application.
In this way, my question is structural or architectural.
The 'tl;dr' versions of the questions basically ask "How do people actually organize and initialize Unobtrusive JavaScript code in large applications?" Specifically:
"When writing JavaScript in an Unobtrusive style, how can the code be organized across files? Should it even be done?" (I understand there are advantages to only sending one file to the client, which is then cached and all subsequent loads are faster).
"If relevant JS is no longer explicitly loaded with a page, how does one make sure the right code is properly initialized/called/used on the proper page(s) and time isn't wasted on running selectors that are irrelevant to the current page?"
Long Version:
After months of hand-rolling my own brittle, good-enough-for-now JavaScript in our Rails application, I am finally converting to the way of UJS and jQuery. I've now read all of http://jqfundamentals.com, save for "Advanced Topics" section. I'm getting the hang of jQuery quite easily, and my pages are doing what I want them to, in isolation.
As for "Unobtrusive JavaScript", I'm less clear but the idea seems to be primarily separating JS entirely external files, and strapping JS functionality into the page after the fact by somehow determining what page you are on and attaching functions after the document is ready. Please correct me if this is inaccurate or incomplete.
But what I don't understand is how to organize my code and make sure that the right JavaScript is included and attached to the right pages. All of the methods I can imagine seem either exceedingly inefficient, clunky, or invalidate some of the tenets of UJS.
The current state of affairs is that I have a massive and unwieldy application.js file, into which is crammed all sorts of selectors that are waiting to place functionality onto page elements that don't exist on 90% of my pages. That is, Page 1 has items A, B and C, whereas Page 2 has items A, D and E. This is frequently causing things to break. I feel like I am left with two unpalatable options:
Since I want to have the server only send one, large .js file, have my entire JS code in application.js somehow wrapped in a switch statement that figures out (?) which page it is on and makes only document.ready calls for elements it knows should be on that page. The obvious ways I could accomplish this feel very ugly, such as placing a single global JS variable inside the page and checking for it, which breaks the entire zen of UJS that drew me in the first place.
Split up my JavaScript into different files and selectively load them. That is to say, "I am in view01.html.erb, therefore I need to include view01.js," which deals with only elements on view01.html.erb. This seems better at first, but gets clunky fast when it comes to partials, mixing-and-matching .js files, and trying to stay DRY.
I went down this road of UJS because it had this very clean, zen-like appeal, but applied to a larger project it seems to be more trouble than it's worth. Yet people much smarter than me insist otherwise, and I completely agree that in-line and script-tag-bound JavaScript is clunky and ugly, which leads me to believe I am doing something wrong.
What I have done so far
(aka Even Longer Version, probably not worth your time to read):
Phase 1: I'm working on a Rails application, and so I began by taking the route presented in a number of RailsCasts tutorials that demonstrate code in application.js which finds an item via a selector and attaching some functionality to it. Things worked great and I had 2 or 3 pages doing all the nifty jQuery, Web2.0-ish, AJAX-y stuff that is hot with the kids these days. Awesome!
Phase 2: Then I made one more page, and things started to break. I had all my code in application.js accessing things via $('selector'). This new page didn't have any of the same elements as the other pages, and so when my code from the other pages tries to get the element with id='special_area', the call to $('#special_area')[0] breaks because it is empty. Null-checking the result of every single selector felt wrong, so I eventually bumbled my way onto the solution of not using the DOM elements I know and love, but working with these fancy jQuery Objects that can be chained. Neat.
Phase 3: I realize that with these Chained jQuery Objects, as long as all my IDs are unique, or I place some sort of unique DIV on each page, any elements that don't exist on the page will return an empty set and (hopefully) not break. I don't know enough to say for certain yet that they will not break regardless of what I want to do with them because they are empty, thus I'm not going to assume that is the case. But somehow I can see it marginally working out by brute-forcing ALL the elements in my entire app and somehow intelligently ignoring elements that don't exist on the current page.
Furthermore, jQuery Fundamentals guide discusses, repeatedly, how expensive it is to keep running selectors. And if I just brute-force run ALL my selectors and let it not apply those that don't exist, I'm placing a HUGE static overhead on each and every page, since as the size of my app grows, so does the single .js file, and each page has to check more and more elements that don't actually appear on it!
Phase 4: I learned about how namespaces are done in JS, and while I plan on using them, I don't see it as solving this particular problem. If they can, please explain. Beyond this point, my ability to articulate my disappointment and confusion has broken down, and I'm wondering How do people actually organize and initialize UJS code in the Real Life™?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
tl;dr 答案
require()
并执行相应的模块更长的答案
使用 AMD 加载程序,例如 RequireJS 允许您将代码分成单独的模块。您还可以将共享代码(例如 jQuery)存储在公共模块中,然后将其用作视图模块的先决条件。
您可以阅读异步模块定义、RequireJS 和 将其与 jQuery 一起使用,但是这个这就是我的处理方法:
查看模块
您的模块将如下所示(我们也使用 jQuery 作为 AMD 模块):
>部分视图
在部分视图中,您将加载模块并调用 init 函数:
tl;dr Answer
require()
and execute the appropriate moduleLonger Answer
Using an AMD loader such as RequireJS allows you spearate your code into individual modules. You can also store shared code (e.g. jQuery) in a common module and then use it as a prerequisite for your view modules.
You can read up on Asynchronous Module Definition, RequireJS and using it with jQuery, but this is how I'd approach it:
View Module
Your module will look something like this (we're using jQuery as an AMD module, too):
Partial View
In your partial view, you'll load the module and call the init function: