注意:我现在已经创建了一个jQuery插件,这是我尝试解决这个问题。我确信它可以得到改进,我可能忽略了很多用例,所以如果有人想要提供反馈,请随意:-)https://github.com/WickyNilliams/ReadyBinder
我没有这样的问题,但我认为这将是一个有趣的讨论点,我希望大家对此有一些有趣的想法。
基本上,我在一个大型网站工作,我们越来越多地编写JavaScript。这很好,我喜欢JS的独特方法,我发现在一些黑暗的语言历史中的古怪是可爱的;-)然而,有一件事一直困扰着我,那就是如何管理文档准备事件,因为它们随着时间的推移变得越来越大(结果是不那么专注/特定于所服务的页面)
问题是,我们有一个JS文件(合并&缩小,虽然这对我的问题来说有点无关紧要)。大部分JS都是使用揭示模块模式编写的,jQuery是我们选择的框架。所有JS功能逻辑上分为方法,命名空间,然后在脚本文件底部有这个
$(function(){
//lots of code here, usually calling encapsulated methods
//on our namespaced revealing module
});
问题是这个文档就绪处理程序中的所有代码都不属于每个页面。例如,在一个页面上,只有10%的内容可能是相关的,而在另一个页面上,可能有80%的内容是相关的。对我来说,这感觉是非常错误的,我觉得我应该只执行每个页面需要的代码,主要是为了效率,但也是为了可维护性。
我已经在谷歌上搜索了这个问题的方法,但找不到任何东西,也许我只是在搜索错误的东西!
无论如何,我的问题是:- 有没有人想过这个问题?
- 这实际上是别人认为的问题吗?
- 你的代码中有一个大的、包纳一切的文档准备处理程序吗?还是它更专注于所服务的页面类型?
- 如果是后者,如何处理?在JS中切换的多个处理程序或动态地吐出文档准备处理程序服务器端?
期待人们对此事的看法。
Cheers
这就是我在rails mvc项目中所做的,我已经为js中的控制器创建了一个单独的命名空间,类似于rails控制器
class BusinessController
def new
end
def index
end
end
和
Var Business = {
init : function(action) {
//code common to the Business module
//even add the common jquery doc ready here, its clean
//and call the page specific action
this[action]();
},
new : function() {
// check jquery document ready code here, thats relevant to this action
//New rental page specific code here
},
index : function() {
// check jquery document ready code here, thats relevant to this action
//index rental page specific code here
}
}
和视图代码(服务器端)只需通过
初始化特定于页面的js代码<script type="text/javascript">
<%= controller_name %>.init('<%= controller.action_name %>');
//which will render to
// Business.init(index);
</script>
你可以很好地调整它使它在任何语言中工作。这种方法不关心你是有一个文件还是多个文件。
我认为解决这个问题最直观的方法就是减少加载时执行的工作量。
如果你的代码看起来像:
$(function () {
// Binds a single click event (which will never be triggered if the hypothetical
// widget is never used), regardless of how many widgets are used on the page.
$(document).delegate('.rating-widget', 'click', namespace.rating.handler);
// and so on for similar things which simply require event handler registration
// Initializes each of the forms on the page, letting the initializer take care
// of any details (datepicker widgets, validation, etc) based on the root element
$('form.fancy').each(namespace.fancyform.initializer);
// and so on for similar things that require initialization
// ... (for each type of thing on the page requiring initial setup,
// find the most general/high level pattern that sufficient)
});
的东西应该是相对可维护的,即使有几十行。在处理组件代码时,这一层没有复杂/有趣的逻辑需要更新/记住,而且因为这一层的所有内容都很琐碎,所以很容易阅读。在这一点上,没有必要发明一些复杂的注册系统;它不会比.delegate
和.each
简单。
还需要注意的是,上面所有的代码都可以很好地处理组件不被使用的情况。在这些情况下,很少或不做任何工作,也不需要条件逻辑。
关于如何实现处理程序/初始化器的有趣想法,您可能想看看,例如,Doug Neiner上周在jQuery Boston上所做的"实践中的上下文jQuery"演讲。问得好。我实际上是通过使用自定义页面加载事件来处理这个问题的。在core .js文件中,我有这样一个类:
var Page = {
init: function(pageName) {
switch (pageName)
{
case: 'home': {
// Run home page specific code
}
case: 'about': {
// Run about page specific code
}
...
}
}
}
你可以调用这一堆的方式,无论是在一个特定的页面$(document).ready()
或从核心脚本使用某种URL解析器(字面上任何事情都可能与URL解析器):
$(document).ready(function() {
// http://www.mydomain.com/about
var currentPage = window.location.pathname; // "about"
Page.init(currentPage);
});
window.location
DOM参考:https://developer.mozilla.org/en/DOM/window.location
首先,我将特定的类放在body或特定的容器上,例如articles
, form-validation
, password-meter
, ... .如果我有一个MVC应用程序,我更喜欢将控制器名称放入body类中。这不会造成太大的伤害,而且对于样式化也很有用。
然后在每个文档上。ready块中检查是否存在这样的类,如果不存在则从该函数返回。这种方法更简单,因为它没有将主代码放在if子句中。这种方法类似于用于检查函数先决条件的断言。
的例子:
$(function(){
if($('body').hasClass('articles') === false){ return; }
//body of the articles code
});
$(function(){
if($('body').hasClass('password-meter') === false){ return; }
//include password meter in page
});
这种方法的好处是没有不需要的代码可以潜入ready回调中,这使得它更容易重用。缺点是你有很多回调块,如果你不注意,重复的代码很容易潜入你的应用程序。
我知道你是从哪里来的。让用户下载一个充满不相关代码的庞大JS文件(JQuery API就是一个糟糕的开始!)
我所做的,这可能是对的或错的,但肯定似乎是工作是包括所需的功能在标签之前逐页的基础上。(这个位置在很大程度上绕过了文档。准备好问题)。但我还是觉得这种方法太简单了。
我对听到其他答案很感兴趣,所以这是个好问题,+1。
"问题是我们有一个JS文件"
我不知道你怎么能避免它,只要你包括相同的(合并)JS文件的所有页面。您可以使用一些服务器端逻辑来合并不同的部分以适应不同的页面,但这样您可能会失去客户端JS缓存的好处,所以…
我所从事的项目(通常)使用了多个JS文件,每个页面都有一对,而其他的则根据需要包含或不包含-一些JS文件只包含一个页面。这意味着几乎每个页面都包含了通用的库函数,不管他们是否使用它们,但是通用的JS文件会被缓存,所以这并不重要。特定于页面的代码确实是特定于页面的。我不能从你的问题中看出你是否意识到这一点,但是你可以有多个文档就绪处理程序(我相信jQuery按照它们被绑定的顺序执行它们?)所以如果你把JS拆分,每个JS文件都可以包含自己的文档。当然,这种方法有自己的开销,但我认为这比将所有内容放在同一个文件中要好。
首先,如果您正在使用jQuery,文档。getElementsByTagName不是必需的,您可以使用$("tagname")。查看jQuery选择器文档,了解更多神奇的内容!
你正在进行的解决方案是我要做的,但你不需要去定义额外的函数,获得bodyClasses,运行一个循环,或任何其他的麻烦…为什么不直接使用jQuery的。hasclass()函数呢?
$(function() {
if($("body").hasClass("article")) {
alert("some article specific code");
}
if($("body").hasClass("landingPage")) {
alert("some landing specific code");
}
});
Bam。你需要的一切。如果您想要更高效,您可以重用一个主体查询:
$(function() {
var body = $("body");
if(body.hasClass("article")) {
alert("some article specific code");
}
if(body.hasClass("landingPage")) {
alert("some landing specific code");
}
});
这是在jsfiddle: http://jsfiddle.net/W8nx8/6/
拥有更少的javascript文件最小化发送到服务器的请求数量和缓存文件使第二和第三页加载更快。通常,我认为js文件的某些部分可能会在接下来的页面中被需要(而不仅仅是当前页面)。这让我把我的js代码分为两类:代码放在我的"main.js"中,即使不使用,它也会被客户端下载(但不一定执行,我将解释),第二类代码放在单独的文件中。实际上,您可以创建按概率分组的多个文件。这取决于项目的细节。如果是一个大型项目,对用户行为进行一些统计分析可能会揭示出有趣的js代码分组方式。既然代码已经下载到客户端,我们不想执行所有代码。你应该做的是使用不同的布局或页面。在版面或页面中放置一个局部。在局部设置一个javascript变量。检索javascript代码中的javascript变量,并确定是否要执行某些代码。
即:
创建部分"_stats.html "。,把下面的代码放到里面:
<script type="text/javascript">
var collect_statistics = <%= collect_statistics %>;
</script>
将部分放置在想要收集统计信息的页面中:show.html.erb
<%= render "_stats", :collect_statistics => false %>
现在在你的js文件中,做以下操作:
$(document).load( function() {
if (typeof collect_statistics != "undefined" && collect_statistics) {
// place code here
}
});
我希望这对你有帮助!
我喜欢RameshVel方法。我还找到了Paloma gem来创建特定于页面的JavaScript。例如:
Paloma.controller('Admin/Users', {
new: function(){
// Handle new admin user
}
});