Я рассмотрю несколько простых вещей, которые могут вам помочь, а могут и не помочь. Некоторые из них могут быть очевидными, некоторые - чрезвычайно загадочными.
Шаг 1. Разделите код
Разделение кода на несколько модульных блоков - очень хороший первый шаг. Соберите все, что работает «вместе», и поместите их в отдельный маленький корпус. пока не беспокойтесь о формате, оставьте его встроенным. Структура - более поздний момент.
Итак, предположим, у вас есть такая страница:
Было бы разумно разделить на части, чтобы все обработчики / связыватели событий, связанные с заголовками, были там, для простоты обслуживания (и не нужно просеивать 1000 строк).
Затем вы можете использовать такой инструмент, как Grunt, чтобы воссоздать свой JS в виде единого модуля.
Шаг 1а: Управление зависимостями
Используйте библиотеку, такую как 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, ... Список можно продолжить.
Модель представляет данные.
View представляет свою наценку и все события , связанные с ним
Контроллер представляет бизнес - логику - другими словами, контроллер сообщает страницу , какие представления для нагрузки и какие модели использования.
Это будет важный этап обучения, но он того стоит: чистый модульный код предпочтительнее спагетти.
Есть много других вещей, которые вы можете сделать, это просто рекомендации и идеи.
Изменения, специфичные для кода
Вот некоторые конкретные улучшения вашего кода:
$('.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!