Общепринятые рекомендации по организации кода в 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? Есть ли лучший вариант?

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

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

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

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




Хороший руководитель 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, чтобы сделать наши дни программирования более управляемыми.




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

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

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




Вдохновленный более ранними сообщениями, я сделал копию каталогов 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
};

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




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.




В моем последнем проекте -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, я знаю, что должны быть лучшие способы справиться с этим, но до сих пор он отлично работает для нас.







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

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

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



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




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.




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

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

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

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




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




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

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

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




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

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



Related