随着像jQuery这样的JavaScript框架使客户端web应用程序更丰富,功能更强大,我开始注意到一个问题…

你到底是怎么组织起来的?

把所有的处理程序放在一个地方,并为所有事件编写函数? 创建函数/类来包装您的所有功能? 疯狂地写作,只希望结果是最好的? 放弃,找一份新工作?

我提到了jQuery,但它实际上是一般的JavaScript代码。我发现,当一行一行开始堆积时,管理脚本文件或找到您要找的内容变得越来越困难。我发现的最大问题可能是,做同一件事有太多的方法,很难知道哪一种是目前普遍接受的最佳实践。

有什么通用的建议可以让你的.js文件像你的应用程序的其他部分一样漂亮整洁吗?或者这只是IDE的问题?还有更好的选择吗?


EDIT

这个问题主要是关于代码组织,而不是文件组织。有一些合并文件或拆分内容的好例子。

我的问题是:目前普遍接受的组织实际代码的最佳实践方式是什么?您的方法是什么,甚至推荐的方法是什么,以与页面元素交互并创建互不冲突的可重用代码?

有些人列出了名称空间,这是个好主意。还有什么其他方法,更具体地说,处理页面上的元素并保持代码的组织和整洁?


当前回答

在我的上一个项目- viajeros.com -我使用了几种技术的组合。我不知道如何组织一个网页应用程序——Viajeros是一个面向旅行者的社交网站,它有明确定义的部分,所以很容易将每个部分的代码分开。

我根据站点部分使用名称空间模拟和延迟加载模块。在每个页面加载我声明一个“vjr”对象,并总是加载一组常用函数到它(vjr.base.js)。然后每个HTML页面用简单的语句决定哪些模块需要:

vjr.Required = ["vjr.gallery", "vjr.comments", "vjr.favorites"];

Vjr.base.js从服务器获取并执行每一个gzip文件。

vjr.include(vjr.Required);
vjr.include = function(moduleList) {
  if (!moduleList) return false;
  for (var i = 0; i < moduleList.length; i++) {
    if (moduleList[i]) {
      $.ajax({
        type: "GET", url: vjr.module2fileName(moduleList[i]), dataType: "script"
      });
    }
  }
};

每个“模块”都有这样的结构:

vjr.comments = {}

vjr.comments.submitComment = function() { // do stuff }
vjr.comments.validateComment = function() { // do stuff }

// Handlers
vjr.comments.setUpUI = function() {
    // Assign handlers to screen elements
}

vjr.comments.init = function () {
  // initialize stuff
    vjr.comments.setUpUI();
}

$(document).ready(vjr.comments.init);

鉴于我有限的Javascript知识,我知道一定有更好的方法来管理这个,但到目前为止,它为我们工作得很好。

其他回答

受之前帖子的启发,我制作了一个Rakefile和供应商目录的副本,并与WysiHat (changelog提到的RTE)一起分发,并进行了一些修改,包括使用JSLint进行代码检查和使用YUI Compressor进行缩小。

这个想法是使用Sprockets(来自WysiHat)将多个JavaScripts合并到一个文件中,在发布之前用JSLint检查合并文件的语法,并用YUI Compressor缩小它。

先决条件

Java运行时 红宝石和耙子宝石 您应该知道如何将JAR放入Classpath

现在做的

下载Rhino并将JAR(“js.jar”)放到类路径中 下载YUI Compressor并将JAR (build/yuicompressor-xyz.jar)放到类路径中 下载WysiHat并将“vendor”目录复制到你的JavaScript项目的根目录 下载JSLint for Rhino,并将其放到“vendor”目录中

现在,在JavaScript项目的根目录中创建一个名为“Rakefile”的文件,并向其添加以下内容:

require 'rake'

ROOT            = File.expand_path(File.dirname(__FILE__))
OUTPUT_MERGED   = "final.js"
OUTPUT_MINIFIED = "final.min.js"

task :default => :check

desc "Merges the JavaScript sources."
task :merge do
  require File.join(ROOT, "vendor", "sprockets")

  environment  = Sprockets::Environment.new(".")
  preprocessor = Sprockets::Preprocessor.new(environment)

  %w(main.js).each do |filename|
    pathname = environment.find(filename)
    preprocessor.require(pathname.source_file)
  end

  output = preprocessor.output_file
  File.open(File.join(ROOT, OUTPUT_MERGED), 'w') { |f| f.write(output) }
end

desc "Check the JavaScript source with JSLint."
task :check => [:merge] do
  jslint_path = File.join(ROOT, "vendor", "jslint.js")

  sh 'java', 'org.mozilla.javascript.tools.shell.Main',
    jslint_path, OUTPUT_MERGED
end

desc "Minifies the JavaScript source."
task :minify => [:merge] do
  sh 'java', 'com.yahoo.platform.yui.compressor.Bootstrap', '-v',
    OUTPUT_MERGED, '-o', OUTPUT_MINIFIED
end

如果你正确地完成了所有操作,你应该能够在控制台中使用以下命令:

rake merge——将不同的JavaScript文件合并为一个 Rake检查——检查代码的语法(这是默认任务,因此只需输入Rake) rake minify——准备JS代码的缩小版本

关于源合并

使用Sprockets, JavaScript预处理器可以包括(或要求)其他JavaScript文件。使用以下语法来包含初始文件(名为“main.js”,但你可以在Rakefile中更改)中的其他脚本:

(function() {
//= require "subdir/jsfile.js"
//= require "anotherfile.js"

    // some code that depends on included files
    // note that all included files can be in the same private scope
})();

然后……

看看WysiHat提供的Rakefile来设置自动化单元测试。不错的东西:)

现在揭晓答案

这并没有很好地回答最初的问题。我知道,对此我很抱歉,但我把它贴在这里,因为我希望它能对其他人整理他们的混乱有所帮助。

我解决这个问题的方法是尽可能多地进行面向对象建模,并将实现分离到不同的文件中。那么处理程序应该尽可能短。List singleton的例子也是一个很好的例子。

和名称空间……它们可以被更深层次的物体结构所模仿。

if (typeof org === 'undefined') {
    var org = {};
}

if (!org.hasOwnProperty('example')) {
    org.example = {};
}

org.example.AnotherObject = function () {
    // constructor body
};

我不太喜欢模仿,但是如果您有很多想要移出全局作用域的对象,这可能会很有帮助。

Dojo从一开始就使用模块系统。事实上,它被认为是Dojo的基石,是将所有Dojo结合在一起的粘合剂:

dojo。要求——官方文件。 理解dojo.declare, dojo。Require和dojo. provider。 引入Dojo。

使用模块Dojo可以实现以下目标:

Namespaces for Dojo code and custom code (dojo.declare()) — do not pollute the global space, coexist with other libraries, and user's non-Dojo-aware code. Loading modules synchronously or asynchronously by name (dojo.require()). Custom builds by analyzing module dependencies to create a single file or a group of interdependent files (so-called layers) to include only what your web application needs. Custom builds can include Dojo modules and customer-supplied modules as well. Transparent CDN-based access to Dojo and user's code. Both AOL and Google carry Dojo in this fashion, but some customers do that for their custom web applications as well.

好的OO + MVC原则对于管理一个复杂的javascript应用程序肯定大有帮助。

基本上,我将我的应用程序和javascript组织成以下熟悉的设计(从我的桌面编程到Web 2.0一直存在)

图像上数值的说明:

Widgets representing the views of my application. This should be extensible and separated out neatly resulting good separation that MVC tries to achieve rather than turning my widget into a spaghetti code (equivalent in web app of putting a large block of Javascript directly in HTML). Each widget communicate via others by listening to the event generated by other widgets thus reducing the strong coupling between widgets that could lead to unmanageable code (remember the day of adding onclick everywhere pointing to a global functions in the script tag? Urgh...) Object models representing the data that I want to populate in the widgets and passing back and forth to the server. By encapsulating the data to its model, the application becomes data format agnostics. For example: while Naturally in Javascript these object models are mostly serialized and deserialized into JSON, if somehow the server is using XML for communication, all I need to change is changing the serialization/deserialization layer and not necessarily needs to change all the widget classes. Controller classes that manage the business logic and communication to the server + occasionally caching layer. This layer control the communication protocol to the server and put the necessary data into the object models Classes are wrapped neatly in their corresponding namespaces. I am sure we all know how nasty global namespace could be in Javascript.

In the past, I would separate the files into its own js and use common practice to create OO principles in Javascript. The problem that I soon found that there are multiple ways to write JS OO and it's not necessarily that all team members have the same approach. As the team got larger (in my case more than 15 people), this gets complicated as there is no standard approach for Object Oriented Javascript. At the same time I don't want to write my own framework and repeat some of the work that I am sure smarter people than I have solved.

jQuery是非常好的Javascript框架,我喜欢它,但随着项目越来越大,我显然需要额外的结构为我的web应用程序,特别是促进标准化OO实践。就我自己而言,经过几次试验,我发现YUI3 Base和Widget (http://yuilibrary.com/yui/docs/widget/和http://yuilibrary.com/yui/docs/base/index.html)基础设施完全满足了我的需要。我使用它们的几个原因。

It provides Namespace support. A real need for OO and neat organization of your code It support notion of classes and objects It gives a standardize means to add instance variables to your class It supports class extension neatly It provides constructor and destructor It provides render and event binding It has base widget framework Each widget now able to communicate to each other using standard event based model Most importantly, it gives all the engineers an OO Standard for Javascript development

与许多观点相反,我不一定要在jQuery和YUI3之间做出选择。这两者可以和平共处。虽然YUI3为我复杂的web应用程序提供了必要的OO模板,但jQuery仍然为我的团队提供了我们都喜欢和熟悉的易于使用的JS抽象。

使用YUI3,我成功地创建了MVC模式,通过分离将Base扩展为模型的类,将Widget扩展为视图的类,当然还有进行必要逻辑和服务器端调用的控制器类。

Widget可以使用基于事件的模型相互通信,并根据预定义的接口侦听事件并执行必要的任务。简单地说,把OO + MVC结构的JS是一个乐趣对我来说。

声明一下,我不是为雅虎工作的!仅仅是一个建筑师在试图解决最初的问题所带来的问题。我认为如果有人找到了等效的OO框架,这也可以。基本上,这个问题也适用于其他技术。感谢所有提出OO原则+ MVC的人,使我们的编程更易于管理。

"Write like crazy and just hope it works out for the best?", I've seen a project like this which was developed and maintained by just 2 developers, a huge application with lots of javascript code. On top of that there were different shortcuts for every possible jquery function you can think of. I suggested they organize the code as plugins, as that is the jquery equivalent of class, module, namespace... and the whole universe. But things got much worse, now they started writing plugins replacing every combination of 3 lines of code used in the project. Personaly I think jQuery is the devil and it shouldn't be used on projects with lots of javascript because it encourages you to be lazy and not think of organizing code in any way. I'd rather read 100 lines of javascript than one line with 40 chained jQuery functions (I'm not kidding). Contrary to popular belief it's very easy to organize javascript code in equivalents to namespaces and classes. That's what YUI and Dojo do. You can easily roll your own if you like. I find YUI's approach much better and efficient. But you usualy need a nice editor with support for snippets to compensate for YUI naming conventions if you want to write anything useful.

你可以使用jquery mx(在javascriptMVC中使用),这是一组允许你使用模型、视图和控制器的脚本。我曾在一个项目中使用它,并帮助我创建结构化javascript,由于压缩,脚本大小最小。以控制器为例:

$.Controller.extend('Todos',{
  ".todo mouseover" : function( el, ev ) {
   el.css("backgroundColor","red")
  },
  ".todo mouseout" : function( el, ev ) {
   el.css("backgroundColor","")
  },
  ".create click" : function() {
   this.find("ol").append("<li class='todo'>New Todo</li>"); 
  }
})

new Todos($('#todos'));

如果对视图和模型部分不感兴趣,也可以只使用querymx的控制器端。