Лучший способ организовать код jQuery/JavaScript (2013)
Проблема
На этот ответ был дан ответ, но он старый, а не обновленный. У меня более 2000 строк кода в одном файле, и, как мы все знаем, это плохая практика, особенно когда я просматриваю код или добавляю новые функции. Я хочу лучше организовать свой код, пока и на будущее.
Я должен упомянуть, что я создаю инструмент (а не простой сайт) с большим количеством кнопок, элементов пользовательского интерфейса, перетаскивания, капель, обработчиков/обработчиков действий и функций в глобальной области, где несколько слушателей могут использовать одну и ту же функцию.
Пример кода
$('#button1').on('click', function(e){
// Determined action.
update_html();
});
... // Around 75 more of this
function update_html(){ .... }
...
Другие примеры кода
Заключение
Мне действительно нужно организовать этот код для наилучшего использования, а не повторять себя и быть в состоянии добавить новые функции и обновить старые. Я буду работать над этим сам. Некоторые селектора могут быть 100 строк кода, остальные - 1. Я немного посмотрел на require.js
и нашел его несколько повторяющимся, и на самом деле написал больше кода, чем нужно. Я открыт для любого возможного решения, которое соответствует этим критериям, и ссылка на ресурс/примеры всегда является плюсом.
Спасибо.
Ответы
Ответ 1
Я рассмотрю некоторые простые вещи, которые могут или не могут помочь вам. Некоторые из них могут быть очевидны, некоторые могут быть чрезвычайно загадочными.
Шаг 1: Разделите свой код
Разделение кода на несколько модульных блоков - очень хороший первый шаг. Совершайте то, что работает "вместе", и помещайте их в свою собственную небольшую единицу. не беспокойтесь о формате на данный момент, держите его в очереди. Структура - это более поздняя точка.
Итак, предположим, что у вас есть такая страница:
![enter image description here]()
Было бы целесообразно разделять так, чтобы все обработчики/связующие события, связанные с заголовком, были там, для удобства обслуживания (и не нужно просеивать через 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 (Model-View-Controller). Моим любимым является Backbone/Spine, однако там также Angular, Yii,... Список продолжается.
A Модель представляет ваши данные.
A Вид представляет вашу разметку и все связанные с ней события
A Контроллер представляет вашу бизнес-логику - другими словами, контроллер сообщает странице, какие виды загружать и какие модели использовать.
Это будет значительный шаг в обучении, но выигрыш стоит того: он предпочитает чистый модульный код поверх спагетти.
Есть много других вещей, которые вы можете сделать, это только рекомендации и идеи.
Изменения, специфичные для кода
Вот некоторые конкретные улучшения вашего кода:
$('.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 notification и при привязке к событию, которое вы запускаете при создании элементов. Fire'n'forget модульного привязки событий!
Ответ 2
Вот простой способ разделить текущую базу кода на несколько файлов, используя require.js.
Я покажу вам, как разделить код на два файла. После этого добавится больше файлов.
Шаг 1). В верхней части кода создайте объект приложения (или любое другое имя, которое вы предпочитаете, например 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. Я предполагаю, что у вас есть веб-страница, обслуживаемая с веб-сервера, код которого находится в:
www/page.html
и jquery в
www/js/jquery.js
Если эти пути не являются точно, как показано ниже, это не будет работать, и вам придется изменить пути.
Загрузите requirejs и поместите require.js в каталог www/js
.
в page.html
, удалите все теги script и вставьте тег script, например:
<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
, который ссылается на $и приложение.
например.
$('a').click(function() { App.foo() }
Поместите его в www/js/foo.js
В самом верху этого файла поместите:
require(['jquery', 'app'], function($, App) {
Внизу положить:
})
Затем измените последнюю строку www/js/main.js на:
require(['jquery', 'app', 'foo']);
Что это! Делайте это каждый раз, когда вы хотите поместить код в свой собственный файл!
Ответ 3
Для вашего вопроса и комментариев предположим, что вы не хотите переносить свой код на фреймворк, например 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 -->
В вашем файле Grunt:
useminPrepare: {
html: 'app/index.html',
options: {
dest: 'dist'
}
},
usemin: {
html: ['dist/{,*/}*.html'],
css: ['dist/styles/{,*/}*.css'],
options: {
dirs: ['dist']
}
}
Если вы хотите использовать Yeoman, он даст вам шаблонный код для всего этого.
Затем для каждого файла вам необходимо убедиться, что вы следуете лучшим практикам и что все код и переменные находятся в этом файле и не зависят от других файлов. Это не означает, что вы не можете вызывать функции одного файла из другого, то есть иметь инкапсулированные переменные и функции. Что-то похожее на namespacing. Я предполагаю, что вы не хотите, чтобы весь код был привязан к объектно-ориентированным, но если вы не возражаете против рефакторинга, я бы рекомендовал добавить что-то эквивалентное тому, что называется шаблоном модуля. Это выглядит примерно так:
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();
...
Это позволит вам иметь гораздо более удобный код, не переписывая ваш код слишком много.
Ответ 4
Используйте javascript MVC Framework, чтобы упорядочить код javascript стандартным способом.
Доступны лучшие рамки JavaScript MVC:
Выбор JavaScript-структуры MVC требует учета многих факторов. Прочтите следующую сравнительную статью, которая поможет вам выбрать наилучшую структуру на основе факторов, важных для вашего проекта:
http://sporto.github.io/blog/2013/04/12/comparison-angular-backbone-can-ember/
Вы также можете использовать RequireJS с помощью фреймворка для поддержки загрузки и загрузки Asynchrounous js.
Посмотрите ниже, чтобы начать загрузку модуля JS:
http://www.sitepoint.com/understanding-requirejs-for-effective-javascript-module-loading/
Ответ 5
Я бы предложил:
- шаблон издателя/подписчика для управления событиями.
- ориентация объекта
- namespacing
В вашем случае Jessica разделите интерфейс на страницы или экраны. Страницы или экраны могут быть объектами и расширены из некоторых родительских классов. Управляйте взаимодействиями между страницами с классом PageManager.
Ответ 6
Классифицируйте свой код. Этот метод очень помогает мне и работает с любой инфраструктурой 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:___________________
Ответ 7
Я предлагаю вам использовать что-то вроде Backbone. Backbone - это RESTFUL поддерживаемая библиотека javascript. Ik делает ваш код более чистым и читаемым и является мощным при использовании вместе с requirejs.
http://backbonejs.org/
http://requirejs.org/
Магистраль не является реальной библиотекой. Он предназначен для создания структуры вашего javascript-кода. Он может включать в себя другие библиотеки, такие как jquery, jquery-ui, google-maps и т.д. Backbone - это, на мой взгляд, самый близкий подход к JavaScript для объектно-ориентированных и модельных контроллеров.
Также в отношении вашего рабочего процесса. Если вы создаете свои приложения на PHP, используйте библиотеку Laravel. Он будет работать безупречно с Backbone при использовании с принципом RESTfull. Ниже ссылки на Laravel Framework и руководство по созданию 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/
Ответ 8
Возможно, настало время начать реализацию целого процесса разработки, используя такие инструменты, как yeoman http://yeoman.io/. Это поможет контролировать все ваши зависимости, процесс сборки и, если хотите, автоматическое тестирование. Его много работы, чтобы начать с того, что было реализовано, сделают будущие изменения намного проще.