问题

这个答案以前有过回答,但都是旧的,不是最新的.我在一个文件中有2000多行代码,我们都知道这是一种不好的做法,尤其是当我浏览代码或添加新功能时.我想更好地组织我的代码,无论是现在还是将来.

我应该提到的是,我正在构建一个工具(不是一个简单的网站),其中包含许多按钮、UI元素、拖放、动作侦听器/处理程序,以及全局范围内的函数,其中多个侦听器可能使用相同的函数.

示例代码

$('#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

将代码分成多个模块化单元是非常好的第一步.把那些"一起"起作用的东西集中起来,放在他们自己的封闭的小单元里.现在不用担心格式,要保持内联. struct 是稍后的一点.

假设你有这样一个页面:

在此处输入图像描述

为了便于维护(而不必筛选1000行),进行划分以使所有与头相关的事件处理程序/绑定器都在其中是有意义的.

然后可以使用Grunt等工具将JS重新构建回单个单元.

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!

如果您想进一步模块化并减少重复,围绕实现MVC(模型-视图-控制器)方法的有很多很棒的框架.我最喜欢的是脊椎/脊椎,不过,还有棱角分明的,Yii,... list 不胜枚举.

A Model represents your data.

View代表你的加价和所有与之相关的事件

Controller代表您的业务逻辑——换句话说,控制器告诉页面要加载哪些视图以及要使用哪些模型.

这将是一个重要的学习步骤,但这个奖是值得的:它更喜欢干净、模块化的代码,而不是意大利面.

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);

            }

        }

    });
 });

最好写成:

$("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相关问答推荐

在 Mastodon 中将 jQuery 添加到 Rails 6

在不同的行插入不同的值

Jquery $(this) 子 Select 器

如何使用 jQuery 的 drop 事件上传从桌面拖动的文件?

JavaScript 吸管(告诉鼠标光标下像素的 colored颜色 )

确定 JavaScript 值是否为整数?

调用 e.preventDefault() 后提交表单

jQuery计算所有文本字段中的值的总和

单击后如何禁用提交按钮?

在jQuery中将逗号添加到数字

为什么使用 jQuery on() 而不是 click()

JQuery:如果 div 可见

Rails 5:如何将 $(document).ready() 与 turbo-links 一起使用

.apply jQuery 函数是什么?

将片段添加到 URL 而不导致重定向?

类方法中的Typescript this

jQuery $(".class").click(); - 多个元素,点击事件一次

在 bootstrap 弹出窗口中包含表单?

jQuery .each() 索引?

JavaScript 在 DOM 中移动元素