组织jQuery / JavaScript代码的最佳方法(2013) [英] Best way to organize jQuery/JavaScript code (2013)

查看:150
本文介绍了组织jQuery / JavaScript代码的最佳方法(2013)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题



此答案之前已经回答过,但不是最新的。我在一个文件中有超过2000行代码,我们都知道这是不好的做法,特别是当我查看代码或添加新功能时。我想更好地组织我的代码,现在和未来。



我应该提到,我正在建立一个工具(不是一个简单的网站),带有很多按钮,UI元素,拖放,删除,动作侦听器/处理程序和功能几个听众可能使用相同功能的全局范围。



示例代码



  $('#button1')。on('click',function(e){
//确定的动作
update_html();
});

... //这个

$ b中的其他75个更新功能update_html(){...}

...

更多示例代码



结论



我真的需要组织这个代码,以便最好的使用,而不是重复自己并能够添加新功能并更新旧功能。我将自己在这个工作。有些选择器可以是100行代码,其他的是1.我已经看了一下 require.js ,发现它有点重复,实际上写了比所需的更多的代码。我对任何符合这一标准的可能解决方案都是开放的,链接到资源/示例总是一个加分。



谢谢。

解决方案

我会介绍一些可能或可能不帮助你的简单的东西。一些可能是显而易见的,有些可能是非常神秘的。



步骤1:区分你的代码



分离你的代码进入多个模块单元是一个非常好的第一步。综合起来,共同合作,把它们放在自己的小包装单位里。不要担心现在的格式,保持内联。结构是一个稍后的一点。



所以,假设你有一个这样的页面:





分区是有意义的,所有与标题相关的事件处理程序/装订器都在那里,以便于维护(而不必筛选1000行)。



然后可以使用一个工具作为Grunt重新构建你的JS回到一个单位。



步骤1a:依赖管理



使用一个库,例如RequireJS或CommonJS来实现一个叫做 AMD 的东西。异步模块加载允许您明确地声明代码所依赖的,然后允许您将库调用卸载到代码中。你可以直接说这需要jQuery,AMD将加载它,并在jQuery可用时执行你的代码



这也是有一个隐藏的宝石:图书馆加载将完成第二个DOM准备好,而不是之前。这不再停止您的页面的加载!



步骤2:模块化



查看线框?我有两个广告单元他们很可能有共享的事件监听器。



您在此步骤中的任务是确定代码中的重复点,并尝试将其全部合成为模块即可。现在,模块将涵盖所有内容。



这一步的整个想法是从第1步开始,删除所有的复制面,将其替换为单位松散耦合。所以,而不是:



ad_unit1.js

  $(#au1)。click(function(){...}); 

ad_unit2.js

  $(#au2)。click(function(){...}); 

我会有:



ad_unit.js

  var AdUnit = function(elem){
this.element = elem ||新的jQuery();
}
AdUnit.prototype.bindEvents = function(){
...事件到这里
}

page.js

  var AUs = new AdUnit($(#au1,#au2)); 
AUs.bindEvents();

您可以在事件标记之间进行区分除了摆脱重复。这是一个相当体面的一步,我们稍后会进一步介绍。



步骤3:选择一个框架



如果您想进一步模块化和减少重复,那么在实现MVC(模型 - 视图 - 控制器)方法的过程中会出现一堆令人敬畏的框架。我最喜欢的是Backbone / Spine,但是,还有Angular,Yii,...列表继续。



一个模型表示你的数据



视图表示您的标记和与之相关的所有事件



A 控制器表示您的业务逻辑 - 换句话说,控制器告诉页面要加载的视图以及要使用的模型。



这将是一个重要的学习步骤,但奖品是值得的:它赞成干净,模块化的代码超过意大利面。



还有很多其他事情你可以做,只是指导和想法。



特定于代码的更改



以下是对代码的一些具体改进: / p>

  $('。new_layer')。click(function(){

dialog层,输入图层名称,_输入,{

'OK':function(){

var rep ly = $('。dialog_input')。val();

if(reply!= null&& reply!=){

var name =ln _+ reply.split('').join '_');
var parent =;

if(selected_folder!=){
parent = selected_folder +.content;
}

$ R.find(。layer)。clone()
.addClass(name).html(回复)
.appendTo(# layer_groups+ parent);

$ R.find(。layers_group)。clone()
.addClass(name).appendTo('#canvas'+ selected_folder);

}

}

});
});

这更好地写成:



<$ p (click,new_layer,function(){
对话框(创建新图层,输入图层名称), _input,{
OK:function(){
//如果是标准库,必须有一种从这里获取输入的方法,如果你自己编写,值可以使用除类选择器之外的其他东西(可怕的性能+范围+多个实例问题)

//这是视图进入的地方,而不是克隆,将渲染绑定到JS原型中,并且实例化它,这意味着你只需要在一个地方修改东西,你不会冒险克隆事件,你可以测试你的图层独立的
var newLayer = new Layer();
newLayer
.setName(name)
.bindToGroup(parent);
}
});
});

您的代码早期

  window.Layer = function(){
this.instance = $(< div>);
//此处生成的标记为
};
window.Layer.prototype = {
setName:function(newName){
},
bindToGroup:function(parentNode){
}
}

突然间,您可以从代码中的任何位置创建标准图层,而无需复制粘贴。你在五个不同的地方这样做。我刚刚保存了五份复印纸。



另外一个:



//操作规则集包装

  var PageElements = function(ruleSet){
ruleSet = ruleSet || [];
this.rules = [];
(var i = 0; i< ruleSet.length; i ++){
if(ruleSet [i] .target&& ruleSet [i] .action){
this .rules.push(规则集[I]);
}
}
}
PageElements.prototype.run = function(elem){
for(var i = 0; i< this.rules.length; i ++){
this.rules [i] .action.apply(elem.find(this.rules.target));
}
}

var GlobalRules = new PageElements([
{
target:.draggable,
action :function(){this.draggable({
cancel:div#scrolling,.content,
containment:document
});
}

{
target:。resizable,
action:function(){
this.resizable({
handle:all ,
zIndex:0,
遏制:document
});
}
}

]);

GlobalRules.run($(body));

//如果稍后需要添加元素,可以调用GlobalRules.run(yourNewElement);

如果您的事件不符合标准或者创建事件,这是非常有效的注册规则的方法。当与pub / sub通知系统结合使用时,当您创建元素时,此事件被绑定到一个事件时,这也是严重的踢脚踢。 Fire'n'forget模块化事件绑定!


The Problem

This answer has been answered before but are old and not up to date. I have over 2000 lines of code in a single file, and as we all know this is bad practice, especially when i'm looking through code or adding new features. I want to better organize my code, for now and for the future.

I should mention that I'm building a tool (not a simple website) with lots of buttons, UI elements, drag, drops, action listeners/handlers and function in the global scope where several listeners may use the same function.

Example code

$('#button1').on('click', function(e){
    // Determined action.
    update_html();
});

... // Around 75 more of this

function update_html(){ .... }

...

More example code

Conclusion

I really need to organize this code for best use and not to repeat myself and be able to add new features and update old ones. I will be working on this by myself. Some selectors can be 100 lines of code others are 1. I have looked a bit at require.js and found it kinda repetitive, and actually writing more code than needed . I'm open to any possible solution that fit this criteria and link to resource / examples are always a plus.

Thanks.

解决方案

I'll go over some simple things that may, or may not, help you. Some might be obvious, some might be extremely arcane.

Step 1: Compartmentalize your code

Separating your code into multiple, modular units is a very good first step. Round up what works "together" and put them in their own little encased unit. don't worry about the format for now, keep it inline. The structure is a later point.

So, suppose you have a page like this:

It would make sense to compartmentalize so that all the header-related event handlers/binders are in there, for ease of maintenance (and not having to sift through 1000 lines).

You can then use a tool such as Grunt to re-build your JS back to a single unit.

Step 1a: Dependency management

Use a library such as RequireJS or CommonJS to implement something called AMD. Asynchronous Module Loading allows you to explicitely state what your code depends on, which then allows you to offload the library-calling to the code. You can just literally say "This needs jQuery" and the AMD will load it, and execute your code when jQuery is available.

This also has a hidden gem: the library loading will be done the second the DOM is ready, not before. This no longer halts load-up of your page!

Step 2: Modularize

See the wireframe? I have two ad units. They'll most likely have shared event listeners.

Your task in this step is to identify the points of repetition in your code and to try to synthesise all this into modules. Modules, right now, will encompass everything. We'll split stuff as we go along.

The whole idea of this step is to go from step 1 and delete all the copy-pastas, to replace them with units that are loosely coupled. So, instead of having:

ad_unit1.js

 $("#au1").click(function() { ... });

ad_unit2.js

 $("#au2").click(function() { ... });

I will have:

ad_unit.js:

 var AdUnit = function(elem) {
     this.element = elem || new jQuery();
 }
 AdUnit.prototype.bindEvents = function() {
     ... Events go here
 }

page.js:

 var AUs = new AdUnit($("#au1,#au2"));
 AUs.bindEvents();

Which allows you to compartmentalize between your events and your markup in addition to getting rid of repetition. This is a pretty decent step and we'll extend this further later on.

Step 3: Pick a framework!

If you'd like to modularize and reduce repetitions even further, there are a bunch of awesome frameworks around that implement MVC (Model - View - Controller) approaches. My favourite is Backbone/Spine, however, there's also Angular, Yii, ... The list goes on.

A Model represents your data.

A View represents your mark-up and all the events associated to it

A Controller represents your business logic - in other words, the controller tells the page what views to load and what models to use.

This will be a significant learning step, but the prize is worth it: it favours clean, modular code over spaghetti.

There are plenty of other things you can do, those are just guidelines and ideas.

Code-specific changes

Here are some specific improvements to your code:

 $('.new_layer').click(function(){

    dialog("Create new layer","Enter your layer name","_input", {

            'OK' : function(){

                    var reply = $('.dialog_input').val();

                    if( reply != null && reply != "" ){

                            var name = "ln_"+reply.split(' ').join('_');
                            var parent = "";

                            if(selected_folder != "" ){
                            parent = selected_folder+" .content";
                            }

                            $R.find(".layer").clone()
                            .addClass(name).html(reply)
                            .appendTo("#layer_groups "+parent);

                            $R.find(".layers_group").clone()
                            .addClass(name).appendTo('#canvas '+selected_folder);

            }

        }

    });
 });

This is better written as:

$("body").on("click",".new_layer", function() {
    dialog("Create new layer", "Enter your layer name", "_input", {
         OK: function() {
             // There must be a way to get the input from here using this, if it is a standard library. If you wrote your own, make the value retrievable using something other than a class selector (horrible performance + scoping +multiple instance issues)

             // This is where the view comes into play. Instead of cloning, bind the rendering into a JS prototype, and instantiate it. It means that you only have to modify stuff in one place, you don't risk cloning events with it, and you can test your Layer stand-alone
             var newLayer = new Layer();
             newLayer
               .setName(name)
               .bindToGroup(parent);
          }
     });
});

Earlier in your code:

window.Layer = function() {
    this.instance = $("<div>");
    // Markup generated here
};
window.Layer.prototype = {
   setName: function(newName) {
   },
   bindToGroup: function(parentNode) {
   }
}

Suddenly, you have a way to create a standard layer from anywhere in your code without copy pasting. You're doing this in five different places. I've just saved you five copy-pastes.

One more:

// Ruleset wrapper for actions

var PageElements = function(ruleSet) {
ruleSet = ruleSet || [];
this.rules = [];
for (var i = 0; i < ruleSet.length; i++) {
    if (ruleSet[i].target && ruleSet[i].action) {
        this.rules.push(ruleSet[i]);
    }
}
}
PageElements.prototype.run = function(elem) {
for (var i = 0; i < this.rules.length; i++) {
    this.rules[i].action.apply(elem.find(this.rules.target));
}
}

var GlobalRules = new PageElements([
{
    "target": ".draggable",
    "action": function() { this.draggable({
        cancel: "div#scrolling, .content",
        containment: "document"
        });
    }
},
{
    "target" :".resizable",
    "action": function() {
        this.resizable({
            handles: "all",
            zIndex: 0,
            containment: "document"
        });
    }
}

]);

GlobalRules.run($("body"));

// If you need to add elements later on, you can just call GlobalRules.run(yourNewElement);

This is a very potent way to register rules if you have events that are not standard, or creation events. This is also seriously kick-ass when combined with a pub/sub notification system and when bound to an event you fire whenever you create elements. Fire'n'forget modular event binding!

这篇关于组织jQuery / JavaScript代码的最佳方法(2013)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆