Общепринятые рекомендации по организации кода в JavaScript


Answers

Я стараюсь избегать включения javascript с HTML. Весь код инкапсулируется в классы, и каждый класс находится в собственном файле. Для разработки у меня есть отдельные теги <script> для включения каждого js-файла, но они объединяются в один более крупный пакет для производства, чтобы уменьшить накладные расходы HTTP-запросов.

Как правило, у меня будет один «главный» js-файл для каждого приложения. Итак, если бы я писал приложение «опрос», у меня был бы файл js под названием «survey.js». Это будет содержать точку входа в код jQuery. Я создаю ссылки jQuery во время создания экземпляра, а затем передаю их в свои объекты в качестве параметров. Это означает, что классы javascript являются «чистыми» и не содержат ссылок на идентификаторы CSS или имена классов.

// file: survey.js
$(document).ready(function() {
  var jS = $('#surveycontainer');
  var jB = $('#dimscreencontainer');
  var d = new DimScreen({container: jB});
  var s = new Survey({container: jS, DimScreen: d});
  s.show();
});

Я также считаю, что соглашение об именах важно для удобства чтения. Например: я добавляю «j» ко всем экземплярам jQuery.

В приведенном выше примере существует класс под названием DimScreen. (Предположим, что это затухает экран и всплывает окно предупреждения.) Ему нужен элемент div, который он может увеличить, чтобы закрыть экран, а затем добавить окно предупреждения, поэтому я передаю объект jQuery. jQuery имеет концепцию подключаемого модуля, но он кажется ограниченным (например, экземпляры не являются постоянными и не могут быть доступны) без реального потенциала. Таким образом, класс DimScreen будет стандартным классом javascript, который просто использует jQuery.

// file: dimscreen.js
function DimScreen(opts) { 
   this.jB = opts.container;
   // ...
}; // need the semi-colon for minimizing!


DimScreen.prototype.draw = function(msg) {
  var me = this;
  me.jB.addClass('fullscreen').append('<div>'+msg+'</div>');
  //...
};

Используя этот подход, я создал несколько довольно сложных приложений.

Question

Поскольку JavaScript-фреймворки, такие как jQuery, делают клиентские веб-приложения более насыщенными и функциональными, я начал замечать одну проблему ...

Как вы это делаете в мире?

  • Поместите все ваши обработчики в одно место и напишите функции для всех событий?
  • Создать функцию / классы, чтобы обернуть всю вашу функциональность?
  • Напишите, как сумасшедший, и просто надейтесь, что это сработает лучше?
  • Откажитесь от новой карьеры?

Я упоминаю jQuery, но это действительно любой код JavaScript в целом. Я нахожу, что по мере того, как линии на линиях начинают накапливаться, становится сложнее управлять файлами сценариев или находить то, что вы ищете. Вполне возможно, что самые большие искажения, которые я нашел, так много способов сделать то же самое, трудно понять, какая из них является общепринятой лучшей практикой.

Есть ли какие-либо общие рекомендации относительно наилучшего способа сохранить ваши .js- файлы такими же красивыми и аккуратными, как и остальная часть вашего приложения? Или это только вопрос IDE? Есть ли лучший вариант?

РЕДАКТИРОВАТЬ

Этот вопрос должен был быть больше связан с организацией кода, а не с организацией файлов. Были некоторые действительно хорошие примеры слияния файлов или разделения контента вокруг.

Мой вопрос: каков нынешний общепринятый наилучший способ организации вашего фактического кода? Каков ваш путь или даже рекомендуемый способ взаимодействия с элементами страницы и создать повторно используемый код, который не конфликтует друг с другом?

Некоторые люди перечислили пространства имен, что является хорошей идеей. Каковы некоторые другие способы, более конкретно касающиеся элементов на странице и сохранения кода и аккуратности?




Вдохновленный более ранними сообщениями, я сделал копию каталогов Rakefile и поставщиков, распространенных с WysiHat (RTE, упомянутых в WysiHat изменений), и сделал несколько изменений, включив проверку кода с JSLint и минимизацию с помощью YUI Compressor .

Идея заключается в использовании Sprockets (от WysiHat), чтобы объединить несколько JavaScripts в один файл, проверить синтаксис объединенного файла с JSLint и минимизировать его с помощью компрессора YUI перед дистрибутивом.

Предпосылки

  • Java Runtime
  • рубин и грабли
  • Вы должны знать, как поставить JAR в Classpath

Теперь сделаем

  1. Загрузите Rhino и поместите JAR ("js.jar") в свой путь к классам
  2. Загрузите YUI Compressor и поместите JAR (build / yuicompressor-xyz.jar) в свой путь к классу
  3. Загрузите WysiHat и скопируйте каталог «vendor» в корень вашего проекта JavaScript
  4. Загрузите JSLint для Rhino и поместите его в каталог «vendor»

Теперь создайте файл с именем «Rakefile» в корневом каталоге проекта JavaScript и добавьте к нему следующий контент:

require 'rake'

ROOT            = File.expand_path(File.dirname(__FILE__))
OUTPUT_MERGED   = "final.js"
OUTPUT_MINIFIED = "final.min.js"

task :default => :check

desc "Merges the JavaScript sources."
task :merge do
  require File.join(ROOT, "vendor", "sprockets")

  environment  = Sprockets::Environment.new(".")
  preprocessor = Sprockets::Preprocessor.new(environment)

  %w(main.js).each do |filename|
    pathname = environment.find(filename)
    preprocessor.require(pathname.source_file)
  end

  output = preprocessor.output_file
  File.open(File.join(ROOT, OUTPUT_MERGED), 'w') { |f| f.write(output) }
end

desc "Check the JavaScript source with JSLint."
task :check => [:merge] do
  jslint_path = File.join(ROOT, "vendor", "jslint.js")

  sh 'java', 'org.mozilla.javascript.tools.shell.Main',
    jslint_path, OUTPUT_MERGED
end

desc "Minifies the JavaScript source."
task :minify => [:merge] do
  sh 'java', 'com.yahoo.platform.yui.compressor.Bootstrap', '-v',
    OUTPUT_MERGED, '-o', OUTPUT_MINIFIED
end

Если все сделано правильно, вы должны иметь возможность использовать следующие команды в консоли:

  • rake merge - объединить разные файлы JavaScript в один
  • rake check - проверить синтаксис вашего кода (это задание по умолчанию , поэтому вы можете просто набрать rake )
  • rake minify - подготовить мини-версию вашего JS-кода

При слиянии источников

Используя Sprockets, предварительный процессор JavaScript, вы можете включать (или require ) другие файлы JavaScript. Используйте следующий синтаксис для включения других сценариев из исходного файла (с именем «main.js», но вы можете изменить это в Rakefile):

(function() {
//= require "subdir/jsfile.js"
//= require "anotherfile.js"

    // some code that depends on included files
    // note that all included files can be in the same private scope
})();

А потом...

Взгляните на Rakefile, снабженный WysiHat, чтобы настроить автоматическое тестирование устройства. Хороший материал :)

И теперь для ответа

Это не очень хорошо отвечает на исходный вопрос. Я знаю, и я сожалею об этом, но я разместил его здесь, потому что надеюсь, что кому-то может быть полезно организовать беспорядок.

Мой подход к проблеме состоит в том, чтобы сделать столько объектно-ориентированного моделирования, которое я могу, и отдельные реализации в разные файлы. Тогда обработчики должны быть как можно короче. Пример со List singleton также хорош.

И пространства имен ... ну, они могут быть имитированы более глубокой структурой объекта.

if (typeof org === 'undefined') {
    var org = {};
}

if (!org.hasOwnProperty('example')) {
    org.example = {};
}

org.example.AnotherObject = function () {
    // constructor body
};

Я не большой поклонник имитаций, но это может быть полезно, если у вас есть много объектов, которые вы хотели бы выйти из глобальной области.




В моем последнем проекте -Viajeros.com- я использовал комбинацию из нескольких методов. Я бы не знал, как организовать веб-приложение - Viajeros - это социальный сетевой сайт для путешественников с четко определенными разделами, поэтому легко отделить код для каждой области.

Я использую имитацию пространства имен и ленивую загрузку модулей в соответствии с разделом сайта. На каждой загрузке страницы я объявляю объект «vjr» и всегда загружаю ему набор общих функций (vjr.base.js). Затем каждая страница HTML решает, какие модули нужны с простым:

vjr.Required = ["vjr.gallery", "vjr.comments", "vjr.favorites"];

Vjr.base.js получает каждый gzipped с сервера и выполняет их.

vjr.include(vjr.Required);
vjr.include = function(moduleList) {
  if (!moduleList) return false;
  for (var i = 0; i < moduleList.length; i++) {
    if (moduleList[i]) {
      $.ajax({
        type: "GET", url: vjr.module2fileName(moduleList[i]), dataType: "script"
      });
    }
  }
};

Каждый «модуль» имеет такую ​​структуру:

vjr.comments = {}

vjr.comments.submitComment = function() { // do stuff }
vjr.comments.validateComment = function() { // do stuff }

// Handlers
vjr.comments.setUpUI = function() {
    // Assign handlers to screen elements
}

vjr.comments.init = function () {
  // initialize stuff
    vjr.comments.setUpUI();
}

$(document).ready(vjr.comments.init);

Учитывая мои ограниченные знания Javascript, я знаю, что должны быть лучшие способы справиться с этим, но до сих пор он отлично работает для нас.




Хороший руководитель OO + MVC определенно проделает долгий путь для управления сложным javascript-приложением.

В основном я организую свое приложение и javascript для следующего знакомого дизайна (который существует на всем пути от моего рабочего дня программирования до Web 2.0)

Описание для числовых значений на изображении:

  1. Виджеты, представляющие представления моего приложения. Это должно быть расширяемо и четко разделено на то, что MVC пытается добиться, а не превращать мой виджет в код спагетти (эквивалент в веб-приложении для размещения большого блока Javascript непосредственно в HTML). Каждый виджет общается через других, слушая событие, созданное другими виджетами, тем самым уменьшая сильную связь между виджетами, которые могут привести к неуправляемому коду (помните день добавления onclick всюду, указывая на глобальные функции в теге скрипта? Urgh ...)
  2. Объектные модели, представляющие данные, которые я хочу заполнить в виджетах, и передавать их обратно на сервер. Инкапсулируя данные в свою модель, приложение становится агностиком формата данных. Например: хотя, естественно, в Javascript эти объектные модели в основном сериализуются и десериализуются в JSON, если каким-то образом сервер использует XML для связи, все, что мне нужно изменить, это изменение уровня сериализации / десериализации, и необязательно необходимо изменить все классы виджетов ,
  3. Классы контроллера, которые управляют бизнес-логикой и связью с сервером + иногда кэшируют уровень. Этот уровень управляет протоколом связи с сервером и помещает необходимые данные в объектные модели
  4. Классы аккуратно завернуты в соответствующие им пространства имен. Я уверен, что все мы знаем, как может быть неприятное глобальное пространство имен в Javascript.

Раньше я отделял файлы от своих js и использовал обычную практику для создания принципов OO в Javascript. Проблема, которую я вскоре обнаружил, что существует несколько способов написания JS OO, и не обязательно, чтобы все члены команды имели одинаковый подход. По мере увеличения команды (в моем случае более 15 человек) это становится сложным, поскольку нет стандартного подхода для объектно-ориентированного Javascript. В то же время я не хочу писать свои собственные рамки и повторять часть работы, в которой я уверен, умнее людей, чем я решил.

jQuery невероятно хорош, как Javascript Framework, и мне это нравится, однако по мере того, как проект становится больше, мне явно нужна дополнительная структура для моего веб-приложения, особенно для упрощения стандартизации практики OO. Для меня, после нескольких экспериментов, я обнаружил, что инфраструктура YUI3 Base и Widget ( http://yuilibrary.com/yui/docs/widget/ и http://yuilibrary.com/yui/docs/base/index.html ) обеспечивает именно то, что мне нужно. Несколько причин, почему я их использую.

  1. Он обеспечивает поддержку пространства имен. Настоящая потребность в OO и аккуратная организация вашего кода
  2. Он поддерживает понятие классов и объектов
  3. Он дает стандартное средство для добавления переменных экземпляра в ваш класс
  4. Он поддерживает расширение класса аккуратно
  5. Он предоставляет конструктор и деструктор
  6. Он обеспечивает привязку визуализации и события
  7. Он имеет базовую структуру виджетов
  8. Каждый виджет теперь может общаться друг с другом с использованием стандартной модели, основанной на событиях
  9. Самое главное, это дает всем инженерам стандарт OO для разработки Javascript

Вопреки многим представлениям, мне не обязательно выбирать между jQuery и YUI3. Эти двое могут мирно сосуществовать. Хотя YUI3 предоставляет необходимый шаблон OO для моего сложного веб-приложения, jQuery по-прежнему предоставляет моей команде простую в использовании абстракцию JS, которую мы все любим и знакомы.

Используя YUI3, мне удалось создать шаблон MVC, разделив классы, расширяющие базу как модель, классы, которые расширяют виджетов как вид и вне курса, у вас есть классы контроллера, которые делают необходимую логику и вызовы на стороне сервера.

Виджет может взаимодействовать друг с другом с использованием модели на основе событий и прослушивания события и выполнения необходимой задачи на основе предопределенного интерфейса. Проще говоря, размещение структуры OO + MVC для JS - это радость для меня.

Просто отказ от ответственности, я не работаю для Yahoo! и просто архитектор, который пытается справиться с той же проблемой, что и исходный вопрос. Я думаю, что если кто-то найдет эквивалентную структуру OO, это тоже сработает. В основном этот вопрос относится и к другим технологиям. Слава Богу всем людям, которые придумали Принципы OO + MVC, чтобы сделать наши дни программирования более управляемыми.




Несколько дней назад ребята из 37Signals WysiHat с завихрением. Они создали библиотеку, которая связывает файлы javascript с использованием команд предварительного процессора.

Я использовал его с тех пор, чтобы разделить мои файлы JS, а затем в итоге объединить их как один. Таким образом, я могу разделить проблемы и, в конце концов, иметь только один файл, который проходит через трубу (gzipped, не менее).

В ваших шаблонах проверьте, находитесь ли вы в режиме разработки и включаете отдельные файлы, а если в процессе производства, включите последний (который вам придется «строить» самостоятельно).




Додзе имел модульную систему с первого дня. Фактически, это считается краеугольным камнем Доджо, клея, который держит его все вместе:

Использование модулей Dojo достигает следующих целей:

  • Пространства имен для кода Dojo и настраиваемого кода ( dojo.declare() ) - не загрязняют глобальное пространство, не сосуществуют с другими библиотеками и кодом, не поддерживающим Dojo.
  • Загрузка модулей синхронно или асинхронно по имени ( dojo.require() ).
  • Пользовательские сборки, анализируя зависимости модулей для создания одного файла или группы взаимозависимых файлов (так называемые слои), включают только то, что нужно вашему веб-приложению. Пользовательские сборки могут включать модули Dojo и модули, поставляемые заказчиком.
  • Прозрачный доступ на CDN к Dojo и код пользователя. И AOL, и Google несут Dojo таким образом, но некоторые клиенты делают это для своих пользовательских веб-приложений.



Lazy Загрузите код, который вам нужен по запросу. Google делает что-то подобное с помощью своего google.loader




Следуя хорошим принципам дизайна OO и шаблонам проектирования, вы проделаете долгий путь, чтобы сделать ваш код удобным для понимания и понимания. Но одна из лучших вещей, которые я обнаружил недавно, это сигналы и слоты, которые вы можете опубликовать / подписаться. Посмотрите на http://markdotmeyer.blogspot.com/2008/09/jquery-publish-subscribe.html для простой реализации jQuery.

Идея хорошо используется на других языках для разработки графического интерфейса. Когда что-то существенное происходит где-то в вашем коде, вы публикуете глобальное синтетическое событие, к которому могут подписаться другие методы в других объектах. Это дает отличное разделение объектов.

Я думаю, что Dojo (и Prototype?) Имеют встроенную версию этой техники.

см. также Что такое сигналы и слоты?




Мой босс все еще говорит о временах написания модульного кода (язык C) и жалуется на то, как дерьмовый код в наши дни! Говорят, что программисты могут писать сборку в любых рамках. Всегда существует стратегия преодоления организации кода. Основная проблема связана с парнями, которые рассматривают java-скрипт как игрушку и никогда не пытаются ее изучить.

В моем случае я пишу js-файлы в теме пользовательского интерфейса или на экране приложения, с соответствующим init_screen (). Используя правильное соглашение об именах имен, я уверен, что на уровне корневого элемента конфликтов имен нет. В unobstrusive window.load () я привязываю вещи вверх на основе идентификатора верхнего уровня.

Я строго использую закрытие Java-скриптов и шаблоны, чтобы скрыть все частные методы. После этого никогда не сталкивались с проблемой противоречивых определений свойств / функций / переменных. Однако при работе с командой часто бывает сложно обеспечить такую ​​же строгость.




I use a custom script inspired by Ben Nolan's behaviour (I can't find a current link to this anymore, sadly) to store most of my event handlers. These event handlers are triggered by the elements className or Id, for example. Пример:

Behaviour.register({ 
    'a.delete-post': function(element) {
        element.observe('click', function(event) { ... });
    },

    'a.anotherlink': function(element) {
        element.observe('click', function(event) { ... });
    }

});

I like to include most of my Javascript libraries on the fly, except the ones that contain global behaviour. I use Zend Framework's headScript() placeholder helper for this, but you can also use javascript to load other scripts on the fly with Ajile for example.




«Напиши, как сумасшедший, и просто надеюсь, что это сработает для лучшего?», Я видел такой проект, который был разработан и поддерживается только двумя разработчиками, огромным приложением с большим количеством кода javascript. Кроме того, были разные ярлыки для каждой возможной функции jquery, о которой вы можете думать. Я предложил организовать код как плагины, так как это эквивалент jquery класса, модуля, пространства имен ... и всего юниверса. Но все стало намного хуже, теперь они начали писать плагины, заменяя каждую комбинацию из трех строк кода, используемых в проекте. Personaly Я думаю, что jQuery - это дьявол, и он не должен использоваться в проектах с большим количеством javascript, потому что он поощряет вас быть ленивым и не думать об организации кода в любом случае. Я бы предпочел читать 100 строк javascript, чем одну строку с 40 связанными функциями jQuery (я не шучу). Вопреки распространенному мнению, очень легко организовать javascript-код в эквивалентах пространств имен и классов. Вот что делают YUI и Dojo. Вы можете легко перевернуть свои собственные, если хотите. Я нахожу подход YUI намного лучше и эффективнее. Но вам обычно нужен хороший редактор с поддержкой фрагментов, чтобы компенсировать соглашения об именах YUI, если вы хотите написать что-нибудь полезное.




You can use jquery mx (used in javascriptMVC) which is a set of scripts that allows you to use models, views, and controllers. I've used it in a project and helped me create structured javascript, with minimal script sizes because of compression. This is a controller example:

$.Controller.extend('Todos',{
  ".todo mouseover" : function( el, ev ) {
   el.css("backgroundColor","red")
  },
  ".todo mouseout" : function( el, ev ) {
   el.css("backgroundColor","")
  },
  ".create click" : function() {
   this.find("ol").append("<li class='todo'>New Todo</li>"); 
  }
})

new Todos($('#todos'));

You can also use only the controller side of jquerymx if you aren't interested in the view and model parts.




Для организации JavaScript использовались следующие

  1. Папка для всех ваших javascript
  2. Уровень страницы javascript получает свой собственный файл с тем же именем страницы. ProductDetail.aspx будет ProductDetail.js
  3. Внутри папки javascript для файлов библиотеки у меня есть папка lib
  4. Поместите связанные библиотечные функции в папку lib, которую вы хотите использовать во всем приложении.
  5. Ajax - единственный javascript, который я перемещаю вне папки javascript и получает его собственную папку. Затем я добавляю два подпапки клиента и сервера
  6. Клиентская папка получает все .js-файлы, в то время как папка сервера получает все файлы на стороне сервера.