backbone.js 组织jQuery/JavaScript代码的最佳方式(2013)[已关闭]

2ic8powd  于 2022-11-10  发布在  jQuery
关注(0)|答案(8)|浏览(130)

很难说您在问什么。此问题模棱两可、含糊、不完整、过于宽泛或过于修辞,无法以目前的形式合理地回答。若要协助澄清此问题以便重新开启,请visit the help center
9年前就关门了。
问题所在
这个答案以前已经回答过了,但是已经过时了。我在一个文件中有超过2000行的代码,我们都知道这是不好的做法,特别是当我浏览代码或添加新功能的时候。我想更好地组织我的代码,无论是现在还是将来。
我应该提到的是,我正在构建一个工具(不是一个简单的网站),其中包含大量按钮、UI元素、拖放、操作侦听器/处理程序和全局范围内的函数,其中多个侦听器可能使用同一个函数。

示例代码

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

... // Around 75 more of this

function update_html(){ .... }

...

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

  • 谢谢-谢谢
pb3s4cty

pb3s4cty1#

我将介绍一些简单的事情,它们可能对你有帮助,也可能没有帮助。有些可能是显而易见的,有些可能是非常神秘的。

第1步:划分代码

把你的代码分成多个模块化的单元是非常好的第一步。把那些“一起”工作的东西集中起来,把它们放在自己的小单元里。现在不要担心格式,保持它的内联。结构是以后的重点。
因此,假设您有这样的页面:

为了便于维护(并且不必筛选1000行),划分所有与标头相关的事件处理程序/绑定程序是有意义的。
然后,您可以使用Grunt等工具将JS重新构建为单个单元。

步骤1a:依赖关系管理

使用RequireJS或CommonJS之类的库来实现AMD。异步模块加载允许你明确地说明代码依赖于什么,然后允许你将库调用卸载到代码。你可以直接说“This needs jQuery”,AMD就会加载它,并在jQuery可用时**执行你的代码。
这也有一个隐藏的宝石:库的加载将在DOM准备好的那一刻完成,而不是之前。这不再会停止加载你的页面!

步骤2:模块化

看到线框了吗?我有两个广告单元。它们很可能有共享的事件监听器。
在这一步中,你的任务是识别代码中的重复点,并尝试将所有这些合成为模块。模块现在将包含所有内容。我们将在沿着中对内容进行拆分。
这一步的整个思路是从第一步开始,删除所有的copy-pastas,用松散耦合的单元来代替它们。
ad_unit1.js

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

ad_unit2.js

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

我将拥有:
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();

这允许你在你的事件和你的标记之间进行划分,除了消除重复。这是一个相当不错的步骤,我们将在后面进一步扩展。

第3步:选择一个框架!

如果你想进一步模块化和减少重复,有很多很棒的框架实现MVC(模型-视图-控制器)方法。我最喜欢的是Backbone/Spine,但是,还有Angular,Yii,......还有很多。

Model表示您的数据。
视图表示您的标记以及与之关联的所有事件
控制器表示您的业务逻辑-换句话说,控制器告诉页面要加载哪些视图以及要使用哪些模型。

这将是一个重要的学习步骤,但奖品是值得的:它更喜欢干净、模块化的代码而不是意大利面条。
还有很多其他的事情你可以做,那些只是指导方针和想法。

特定于代码的更改

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

$('.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);
          }
     });
});

在代码的前面:

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

突然间,你有了一种方法,可以在代码的任何地方创建一个标准层,而无需复制粘贴。你在五个不同的地方做了这件事。我刚刚为你保存了五个复制粘贴。
还有一个:
//动作的规则集 Package 函数

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

如果你有非标准的事件或者创建事件,这是一个非常有效的注册规则的方法。当与发布/订阅通知系统结合使用,或者绑定到你创建元素时触发的事件时,这也是一个非常棒的方法。触发并忘记模块化事件绑定!

9udxz4iz

9udxz4iz2#

这里有一个简单的方法,可以使用require.js将当前的代码库拆分成多个文件。我将向您展示如何将代码拆分成两个文件。之后添加更多的文件将非常简单。

**步骤1)**在代码的顶部,创建一个App对象(或您喜欢的任何名称,如MyGame):

var App = {}

**步骤2)**转换所有顶级变量和函数,使其属于App对象。

而不是:
var selected_layer = "";
您需要:
App.selected_layer = "";
而不是:

function getModified(){
...
}

您需要:

App.getModified = function() {

}

请注意,此时您的代码将无法工作,直到您完成下一步。

**步骤3)**转换所有全局变量和函数引用以通过应用程序。

更改如下内容:

selected_layer = "."+classes[1];

至:

App.selected_layer = "."+classes[1];

以及:

getModified()

至:

App.GetModified()

**第4步)**在这个阶段测试你的代码--它应该都能工作。你可能会在一开始得到一些错误,因为你错过了一些东西,所以在继续之前修复这些错误。
**第5步)**设置requirejs。我假设您有一个Web页面,由Web服务器提供,其代码位于:

www/page.html

和jquery

www/js/jquery.js

如果这些路径不是完全像这样,下面的操作将不起作用,您必须修改路径。
下载requirejs并将require.js放入您的www/js目录。
page.html中,删除所有脚本标记并插入如下脚本标记:

<script data-main="js/main" src="js/require.js"></script>

使用内容创建www/js/main.js

require.config({
 "shim": {
   'jquery': { exports: '$' }
 }
})

require(['jquery', 'app']);

然后将您在步骤1-3中修复的所有代码(其唯一的全局变量应该是App)放入:

www/js/app.js

在该文件的最顶部,放置:

require(['jquery'], function($) {

最下面放着:

})

然后在您的浏览器中加载page.html。您的应用程序应该可以工作了!

**步骤6)**创建另一个文件

这是你的工作得到回报的地方,你可以一遍又一遍地做这件事。
www/js/app.js中取出一些引用$和App的代码。
例如:

$('a').click(function() { App.foo() }

放入www/js/foo.js
在该文件的最顶部,放置:

require(['jquery', 'app'], function($, App) {

最下面放着:

})

然后将www/js/main.js的最后一行更改为:

require(['jquery', 'app', 'foo']);

就是这样!每次你想把代码放到它自己的文件中时都要这样做!

pjngdqdw

pjngdqdw3#

对于你的问题和评论,我假设你不愿意把你的代码移植到像Backbone这样的框架中,或者使用像Require这样的加载器库。你只是想用一种更好的方式来组织你已经拥有的代码,以尽可能简单的方式。
我知道要滚动2000多行代码来找到你想处理的部分是很烦人的。解决办法是把你的代码分成不同的文件,每个文件对应一个功能。例如sidebar.jscanvas.js等。然后你可以用Grunt把它们连接在一起进行生产,再加上Usemin,你可以得到这样的东西:
在您的html中:

<!-- build:js scripts/app.js -->
<script src="scripts/sidebar.js"></script>
<script src="scripts/canvas.js"></script>
<!-- endbuild -->

在您的Gruntfile中:

useminPrepare: {
  html: 'app/index.html',
  options: {
    dest: 'dist'
  }
},
usemin: {
  html: ['dist/{,*/}*.html'],
  css: ['dist/styles/{,*/}*.css'],
  options: {
    dirs: ['dist']
  }
}

如果你想使用约曼,它会给予你一个样板代码的所有这一切。
然后对于每个文件本身,你需要确保你遵循最佳实践,并且所有的代码和变量都在那个文件中,并且不依赖于其他文件。这并不意味着你不能从一个文件调用另一个文件的函数,关键是要封装变量和函数。类似于命名空间。我假设你不想把你所有的代码都移植成面向对象的,但是如果您不介意进行一些重构,我建议您添加一些与Module模式等价的东西。
sidebar.js

var Sidebar = (function(){
// functions and vars here are private
var init = function(){
  $("#sidebar #sortable").sortable({
            forceHelperSize: true,
            forcePlaceholderSize: true,
            revert: true,
            revert: 150,
            placeholder: "highlight panel",
            axis: "y",
            tolerance: "pointer",
            cancel: ".content"
       }).disableSelection();
  } 
  return {
   // here your can put your "public" functions
   init : init
  }
})();

然后,您可以像这样加载这段代码:

$(document).ready(function(){
   Sidebar.init();
   ...

这将允许您拥有更易于维护的代码,而不必过多地重写代码。

cidc1ykv

cidc1ykv4#

使用javascript MVC框架,以便以标准方式组织javascript代码。
可用的最佳JavaScript MVC框架包括:

选择一个JavaScript MVC框架需要考虑很多因素。阅读下面的比较文章,它将帮助您根据对项目重要的因素选择最佳框架:http://sporto.github.io/blog/2013/04/12/comparison-angular-backbone-can-ember/
您还可以将RequireJS与框架一起使用,以支持异步js文件和模块加载。
查看以下内容开始JS模块加载:
http://www.sitepoint.com/understanding-requirejs-for-effective-javascript-module-loading/

osh3o9ms

osh3o9ms5#

分类你的代码。这个方法对我帮助很大,并且可以在任何js框架下工作:

(function(){//HEADER: menu
    //your code for your header
})();
(function(){//HEADER: location bar
    //your code for your location
})();
(function(){//FOOTER
    //your code for your footer
})();
(function(){//PANEL: interactive links. e.g:
    var crr = null;
    $('::section.panel a').addEvent('click', function(E){
        if ( crr) {
            crr.hide();
        }
        crr = this.show();
    });
})();

在你喜欢的编辑器中(最好的是Komodo Edit),你可以通过折叠所有条目来折叠,你将只看到标题:

(function(){//HEADER: menu_____________________________________
(function(){//HEADER: location bar_____________________________
(function(){//FOOTER___________________________________________
(function(){//PANEL: interactive links. e.g:___________________
lp0sw83n

lp0sw83n6#

我建议:
1.事件管理发布者/订阅者模式。
1.面向对象
1.命名空间
在Jessica的例子中,将界面划分为页面或屏幕。页面或屏幕可以是对象,并从一些父类扩展而来。使用PageManager类管理页面之间的交互。

fiei3ece

fiei3ece7#

我建议你使用类似Backbone的东西。Backbone是一个RESTFUL支持的javascript库。IK使你的代码更干净,更可读,当与requirejs一起使用时,它是强大的。
http://backbonejs.org/
http://requirejs.org/
Backbone并不是一个真实的的库。它的作用是给你的javascript代码给予结构。它可以包含其他的库,比如jquery,jquery-ui,google-maps等等。在我看来,Backbone是最接近于面向对象和模型视图控制器结构的javascript方法。
同样关于你的工作流程..如果你用PHP构建你的应用程序,使用Laravel库。当它与RESTfull原则一起使用时,它将完美地与Backbone一起工作。下面是Laravel框架的链接和一个关于构建RESTfull API的教程:
http://maxoffsky.com/code-blog/building-restful-api-in-laravel-start-here/
http://laravel.com/
下面是一个来自nettuts的教程。Nettuts有很多高质量的教程:
http://net.tutsplus.com/tutorials/javascript-ajax/understanding-backbone-js-and-the-server/

zi8p0yeb

zi8p0yeb8#

也许是时候开始使用像yeoman http://yeoman.io/这样的工具来实现整个开发工作流程了。这将有助于控制所有的依赖项,构建过程,如果你想的话,还可以自动化测试。这是一个很大的工作开始,但一旦实现,将使未来的变化变得容易得多。

相关问题