Лучший способ организовать код 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/. Это поможет контролировать все ваши зависимости, процесс сборки и, если хотите, автоматическое тестирование. Его много работы, чтобы начать с того, что было реализовано, сделают будущие изменения намного проще.