我将介绍一些简单的事情,这些事情可能对你有帮助,也可能对你没有帮助。有些可能很明显,有些可能非常神秘。
第 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 || 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、……不胜枚举。
模型代表您的数据。
视图代表您的标记以及与之关联的所有事件
控制器代表您的业务逻辑 - 换句话说,控制器告诉页面要加载哪些视图以及要使用哪些模型。
这将是一个重要的学习步骤,但奖励是值得的:与意大利面条相比,它更喜欢干净、模块化的代码。
你可以做很多其他的事情,这些只是指导方针和想法。
特定于代码的更改
以下是对您的代码的一些具体改进:
$('.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);
如果您有非标准事件或创建事件,这是注册规则的一种非常有效的方法。当与 pub/sub 通知系统结合使用时,当绑定到创建元素时触发的事件时,这也是非常糟糕的。Fire'n'forget 模块化事件绑定!