此答案之前已经得到解答,但已经过时而且不是最新的。我在一个文件中有超过2000行代码,而且我们都知道这是不好的做法,特别是当我查看代码或添加新功能时。我想更好地组织我的代码,无论是现在还是将来。
我应该提一下,我正在构建一个工具(不是一个简单的网站),它有许多按钮,UI元素,拖放,动作监听器/处理程序和全局范围内的功能,其中几个监听器可以使用相同的功能。
$('#button1').on('click', function(e){
// Determined action.
update_html();
});
... // Around 75 more of this
function update_html(){ .... }
...
我真的需要组织这些代码以便最好地使用,而不是重复自己,并能够添加新功能并更新旧功能。我将自己做这件事。一些选择器可以是100行代码,其他的是1.我在require.js
看了一下,发现它有点重复,并且实际上编写了比需要更多的代码。我对任何符合此标准的可能解决方案持开放态度,并且链接到资源/示例总是一个加号。
感谢。
答案 0 :(得分:93)
我会回顾一些可能会或可能不会帮助你的简单事情。有些可能是显而易见的,有些可能是非常神秘的。
将您的代码分成多个模块化单元是非常好的第一步。将“合在一起”的作品整理成自己的小包装单元。不要担心现在的格式,保持内联。结构是后来的一点。
所以,假设你有一个这样的页面:
划分区域是有意义的,以便所有与标题相关的事件处理程序/绑定器都在那里,以便于维护(而不必筛选1000行)。
然后,您可以使用Grunt等工具将JS重新构建回单个单元。
使用诸如RequireJS或CommonJS之类的库来实现名为 AMD 的内容。异步模块加载允许您明确说明代码所依赖的内容,然后允许您将库调用卸载到代码中。你可以简单地说“这需要jQuery”,AMD会加载它,并在jQuery可用时执行你的代码 。
这也有一个隐藏的宝石:库加载将在DOM准备好的第二步完成,而不是之前。这不再会停止页面的加载!
看线框?我有两个广告单元。他们很可能会共享事件监听器。
此步骤中的任务是确定代码中的重复点,并尝试将所有这些内容合成到模块中。现在,模块将包含所有内容。我们会随着时间的推移拆分。
这一步的整个想法是从步骤1开始,删除所有复制贴面,用松散耦合的单元替换它们。所以,而不是:
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();
除了摆脱重复之外,您还可以在事件和标记之间进行细分。这是一个相当不错的步骤,我们稍后会进一步扩展它。
如果你想进一步模块化和减少重复,那么实现MVC(模型 - 视图 - 控制器)方法的方法就有很多很棒的框架。我最喜欢的是Backbone / Spine,然而,还有Angular,Yii,......这个名单还在继续。
模型代表您的数据。
查看代表您的加价以及与之相关的所有事件
控制器代表您的业务逻辑 - 换句话说,控制器告诉页面要加载哪些视图以及使用哪些模型。
这将是一个重要的学习步骤,但这个奖项是值得的:它有利于清洁,模块化代码而不是意大利面条。
您还可以做很多其他事情,这些只是指导方针和想法。
以下是对您的代码的一些具体改进:
$('.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) {
}
}
突然间,您可以从代码中的任何位置创建标准图层,而无需复制粘贴。你在五个不同的地方做这件事。我刚刚给你保存了五份复印件。
还有一个:
//用于操作的规则集包装器
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);
如果您有非标准事件或创建事件,这是一种非常有效的注册规则的方法。当与发布/订阅通知系统结合使用时,这也是非常严重的问题,当您绑定到每次创建元素时触发的事件时。 Fire'n'forget模块化事件绑定!
答案 1 :(得分:13)
这是使用require.js将当前代码库拆分为多个文件的简单方法。 我将向您展示如何将代码拆分为两个文件。之后添加更多文件将非常简单。
步骤1)在代码的顶部,创建一个App对象(或您喜欢的任何名称,如MyGame):
var App = {}
步骤2)将所有顶级变量和函数转换为属于App对象。
而不是:
var selected_layer = "";
你想:
App.selected_layer = "";
而不是:
function getModified(){
...
}
你想:
App.getModified = function() {
}
请注意,此时您的代码将无法正常工作,直到您完成下一步。
步骤3)转换所有全局变量和函数引用以通过App。
更改以下内容:
selected_layer = "."+classes[1];
为:
App.selected_layer = "."+classes[1];
和
getModified()
为:
App.GetModified()
步骤4)在此阶段测试您的代码 - 它应该全部有效。一开始你可能会遇到一些错误,因为你遗漏了一些错误,所以在继续之前要修复它们。
步骤5)设置requirejs。我假设您有一个网页,由Web服务器提供,其代码位于:
www/page.html
和
中的jquerywww/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。
e.g。
$('a').click(function() { App.foo() }
将其放入www/js/foo.js
在该文件的最顶部,输入:
require(['jquery', 'app'], function($, App) {
在底部放:
})
然后将www / js / main.js的最后一行更改为:
require(['jquery', 'app', 'foo']);
就是这样!每次要将代码放在自己的文件中时都要这样做!
答案 2 :(得分:10)
对于您的问题和评论,我假设您不愿意将代码移植到像Backbone这样的框架,或者使用像Require这样的加载程序库。您只是希望以最简单的方式更好地讨论已经拥有的代码。
我理解滚动2000多行代码来查找您想要处理的部分很烦人。解决方案是将代码拆分为不同的文件,每个文件对应一个功能。例如sidebar.js
,canvas.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']
}
}
如果您想使用Yeoman,它会为您提供所有这些的样板代码。
然后,对于每个文件本身,您需要确保遵循最佳实践,并且所有代码和变量都在该文件中,并且不依赖于其他文件。这并不意味着你不能从其他人调用一个文件的函数,关键是要封装变量和函数。类似于命名空间的东西。我假设您不希望将所有代码移植到面向对象,但如果您不介意重构,我建议添加与所谓的模块模式等效的东西。它看起来像这样:
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();
...
这将使您拥有更易于维护的代码,而无需过多地重写代码。
答案 3 :(得分:6)
使用javascript MVC Framework以标准方式组织javascript代码。
可用的最佳JavaScript MVC框架是:
选择JavaScript MVC框架需要考虑很多因素。阅读以下比较文章,该文章将帮助您根据对项目重要的因素选择最佳框架: http://sporto.github.io/blog/2013/04/12/comparison-angular-backbone-can-ember/
您还可以在框架中使用RequireJS来支持Asynchrounous js文件&amp;模块加载。
请看下面的内容,开始使用JS模块加载:
http://www.sitepoint.com/understanding-requirejs-for-effective-javascript-module-loading/
答案 4 :(得分:4)
对您的代码进行分类。这个方法对我有很大的帮助,并且适用于任何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:___________________
答案 5 :(得分:3)
我建议:
在您的情况下,Jessica将界面划分为页面或屏幕。页面或屏幕可以是对象,也可以从某些父类扩展。使用PageManager类管理页面之间的交互。
答案 6 :(得分:2)
我建议你使用像Backbone这样的东西。 Backbone是RESTFUL支持的JavaScript库。 Ik使您的代码更清晰,更易读,与requirejs一起使用时功能强大。
Backbone不是一个真正的图书馆。它旨在为您的javascript代码提供结构。它可以包括其他库,如jquery,jquery-ui,google-maps等。在我看来,Backbone是面向对象和模型视图控制器结构的最接近的javascript方法。
还有关于您的工作流程。如果您使用Laravel库在PHP中构建应用程序。当与RESTfull原则一起使用时,它将与Backbone完美配合。在Laravel Framework的链接下面,以及有关构建RESTfull API的教程:
http://maxoffsky.com/code-blog/building-restful-api-in-laravel-start-here/
以下是nettuts的教程。 Nettuts有很多高质量的教程:
http://net.tutsplus.com/tutorials/javascript-ajax/understanding-backbone-js-and-the-server/
答案 7 :(得分:0)
也许是时候开始使用yeoman http://yeoman.io/等工具开始实施整个开发工作流程了。这将有助于控制所有依赖项,构建过程以及自动测试。它的开展很多工作,但一旦实施将使未来的变化更容易。