Как структурировать приложения JavaScript в разработке и производстве

Я создаю свое первое (не спагетти) большое приложение JavaScript. В то время как введение RequireJS и другие структуры управления зависимостями для JavaScript упрощают разделение файлов, Я не понимаю, как это сделать как подтолкнуть большую базу кода к производству. Что бы я хотел, это способ объединить и минимизировать/убрать мои Java-скрипты для создания, используя что-то вроде Ready.js и UglifyJS. Или какой-то другой подход, если это имеет смысл.

Как разработчики с большими приложениями JavaScript в производстве обрабатывают свою структуру в разработке и в производстве?

Я мог бы, например, использовать RequireJS в разработке, а затем использовать Ready/Uglify для агрегирования/минимизации. Но тогда мой код имел бы бессмысленный require()'s, разбросанный по всему миру. Я уверен, что есть лучший подход.

Я также смущен включением jQuery в эти файлы. Должен ли я обертывать каждый отдельный файл jQuery (например, представления Backbone, которые используют jQuery) в пределах своего собственного $(document).ready(function(){...})? Это кажется очень не сухим.

Ответы

Ответ 1

Вы можете использовать RequireJS optimizer. Требования не являются бессмысленными даже в сжатом приложении, потому что вам всегда нужно получить ссылку на модуль. В документе-оптимизаторе также говорится, что он не будет включать модуль, который был загружен переменной типа

var mods = someCondition ? ['a', 'b'], ['c', 'd'];
require(mods);

Я думаю, что RequireJS должен ждать, пока DOM будет готов, и все модули загружены, поэтому вам не нужно обертывать каждый файл.

Тем не менее, мой любимый менеджер пакетов по-прежнему StealJS. Он может вызывать ненужные вызовы в сборке производства, и модуль всегда инкапсулируется в закрытие, которое передает переданный объект jQuery и ждет, пока DOM будет готов и все сценарии будут загружены. К сожалению, он еще не совместим с спецификациями модуля CommonJS.

Ответ 2

Я нашел YUI Builder хорошо работает для меня. Я не уверен, насколько это полезно, если вы не используете YUI 3, но у вас есть хороший шанс адаптировать его к вашим потребностям.

С другой стороны, взглянули ли вы на RequireJS Optimizer?

Относительно document.ready обработки; Я считаю хорошей практикой не допускать, чтобы код в модулях ничего не делал до тех пор, пока они не будут инициализированы или не вызваны. Таким образом, у меня будет один $(document).ready() в теге <script> в нижней части самой страницы, который "склеивает" модули, которые необходимы на этой странице.

Ответ 3

Способ защиты от спагетти

Чтобы эффективно развивать и легко поддерживать приложения JavaScript, в отличие от целого ряда сценариев, специальных или нерегулярности непрозрачной автоматизации, вы можете использовать родное приложение Qooxdoo. Невозможно охватить Qooxdoo, не слишком много пишу, но в случае собственного приложения (не путайте термин с C или Java, Qooxdoo - это чистый JavaScript), он описывается как:

Для приложений с использованием пользовательских графических интерфейсов на основе HTML/CSS вместо слоя виджетов qooxdoo.

Таким образом, такое приложение не использует ни одного слоя пользовательского интерфейса Qooxdoo, а просто средства структуры кода и инструменты сборки. Код в Qooxdoo организован в классах, по одному на файл, как на Java. Я могу выглядеть так:

/**
 * @use(website.library.MosaicFlow)
 */
qx.Class.define('website.controller.Gallery', {

  extend : website.controller.Abstract,

  members : {

    _baseUrl : 'https://picasaweb.google.com/data/feed/api',


    _render : function(photos)
    {
      q('.preloader').remove();

      q.template.get('gallery-template', {'photos': photos}).appendTo('#gallery-container');
      var gallery = window.jQuery('#gallery-container .gallery').mosaicflow({
        'minItemWidth'    : 256,
        'itemSelector'    : '.photo',
        'autoCalculation' : false
      });
      gallery.trigger('resize');
    },

    _convert : function(item, index)
    {
      try
      {
        return {
          'url'     : item.content.src,
          'summary' : item.summary.$t,
          'thumb'   : item.media$group.media$thumbnail[0]
        };
      }
      catch(ex)
      {
        this.debug('failed to convert', index, item);
        return null;
      }
    },

    _onLoadSuccess : function(event)
    {
      var request  = event.getTarget();
      var response = request.getResponse();
      if(!qx.lang.Type.isObject(response) || !('feed' in response))
      {
        request.debug('Malformed response received');
      }
      else
      {
        this._render(response.feed.entry.map(this._convert, this).filter(function(item)
        {
          return !!item;
        }));
      }
    },

    _onLoadFail : function()
    {
      this.debug('Picasa search failed');
    },

    main : function()
    {
      var query   = /^\/gallery\/(\w+)$/.exec(window.location.pathname);
      var request = new qx.io.request.Jsonp(qx.lang.String.format('%1/all', [this._baseUrl]));
      request.setRequestData({
        'q'           : query[1],
        'thumbsize'   : 300,
        'max-results' : 20,
        'alt'         : 'json'
      });
      request.setTimeout(16000);
      request.setCache(false);
      request.addListener('fail',    this._onLoadFail,    this);
      request.addListener('success', this._onLoadSuccess, this);
      request.send();
    }

  }

});

Объектная модель Qooxdoo использует оба мира. В нем есть качества зрелых платформ, таких как Java, в то же время они современные и динамические, предоставляющие классы, наследование, интерфейсы, mixins, события, свойства, привязки данных и многое другое. Поскольку каждый класс имеет определенное имя и находится в дереве пространства имен, генератор Qooxdoo может воспользоваться им. Он анализирует ваши классы и строит их синтаксические деревья. Затем он разрешает зависимости. То есть когда вы ссылаетесь на другой класс, например website.controller.Abstract. Это приводит к графику зависимости, который используется для загрузки скриптов в правильном порядке. Обратите внимание, что все это автоматически и прозрачно для разработчика, и файлы загружаются как есть. Там нет необходимости, как в случае с CommonJS, нет уродливого шаблона, чтобы обернуть вам код, похожий на AMD.

Как вы можете видеть в примере выше, можно иметь дело с внешними библиотеками, отличными от qooxdoo. Вам просто нужно написать фиктивную оболочку для библиотеки, чтобы включить ее в процесс сборки.

Разработка и производственная среда

Вы разрабатываете создание своего приложения (сборка необходима только тогда, когда новая зависимость вводится в код) с так называемой исходной целью. Ваши файлы приложений загружаются в порядке зависимости, один за другим. Файлы Framework могут загружаться один за другим или, что лучше, построены в нескольких больших кусках. В производственной среде ваш код приложения построен с целью сборки. У вас есть возможность создавать один выходной файл или иметь частичную сборку, где код разбивается на большие файлы (вы можете контролировать их размер). Частичная сборка может выглядеть так (оптимизированная/gzipped):

├── [127/64kB]  website.f6ffa57fc541.js
├── [100/33kB]  website.f86294b58d1a.js
└── [361/110kB] website.js

Обратите внимание, что части загружаются по требованию на требуемые страницы.

http://example.com/
└── website.js
http://example.com/article
└── website.js
http://example.com/form
└── website.js
    └── website.f86294b58d1a.js
http://example.com/gallery
└── website.js
    └── website.f6ffa57fc541.js
http://example.com/geo
└── website.js

Поскольку Qooxdoo не нацелен на полномасштабные сборки веб-сайтов, но предоставляет только платформу родного типа приложения, вам нужно закодировать запись в приложении и некоторые основы, такие как загрузка, маршрутизация URL и т.д. Я попробовал для решения этой проблемы с qooxdoo-website-skeleton, к которым относятся приведенные выше примеры. Вы можете использовать его или писать самостоятельно.

Наконец, обратите внимание, что это может быть не так легко начать, как со средней библиотекой JavaScript, но сложность пропорциональна возможной выгоде.