Как стать автором
Обновить

Комментарии 97

В принципе, этот пост покрывает 90% того, что нужно знать про RequireJS. Спасибо!
Думаю, про асинхронную загрузку и оптимизатор бы стоило ещё упомянуть.
Спасибо за дополнение. Постараюсь упомянуть в следующий раз, если будет к месту.
Или можно отредактировать этот пост.
Очень полезная штука в случае многофункционального приложения на JS. Жаль только, что большая часть библиотек полагаются на глобальную область видимости — приходится делать обертки, или переписывать, чтобы общая логика работы с файлами при разработке не менялась.

Кроме плюсов, у использования AMD есть и минусы. В основном они связаны с тем, что это не настолько распространенная технология, как хотелось бы. Например, написание unit-тестов с помощью известных тестовых фремворков и их исполнение при работе с AMD требует требует допиливания запускаторов (использую JSTestDriver, для которого написан адаптер, но все равно периодически глючит).

Ещё не стоит забывать, что технологии AMD построены поверх JavaScript, и чудес тут не будет.
Технология AMD очень молодая, ей всего около года (описание API на GitHub и первые релизы RequireJS). Надеюсь, со временем её подхватят контрибьюторы плагинов и библиотек, как это было с jQuery. А Underscore.js в релизе 1.3.0, напротив, отказались от AMD.
Вместо Underscore могу порекомендовать lodash.
поддерживаю, заявлено много багфиксов и ускорение. ускорение по тестам вроде есть, в реальной жизни пока не понял — все и так летает.
В YUI уже года четыре применяется
Как показывает практика, всем плевать на YUI. Уже как четыре года…
Технология? Решение в 200 строчек кода теперь называется технологией? Вы серьезно?
Согласен, громковато сказано. «Техника» было бы уместнее.
Технология (от др.-греч. τέχνη — искусство, мастерство, умение; λόγος — мысль, причина; методика, способ производства) — в широком смысле — совокупность методов, процессов и материалов, используемых в какой-либо отрасли деятельности, а также научное описание способов технического производства; в узком — комплекс организационных мер, операций и приемов, направленных на изготовление, обслуживание, ремонт и/или эксплуатацию изделия с номинальным качеством и оптимальными затратами, и обусловленных текущим уровнем развития науки, техники и общества в целом.

(http://ru.wikipedia.org/)

AMD — технология поддержки модульности кода. Технически она реализована с помощью функций, замыканий и пр. конструкций, предлагаемых языком. С т.з. пользователя, это обычная модульная технология, и он ждет от неё таких же возможностей, как и от пакетов в Java или C# и т.д., но у технической реализации есть ограничения.

Пассаж про количество строк я не понял. ;)
Уже два года назад RequireJS можно было нормально пользоваться.
Если не верите, можете, например, историю коммитов посмотреть.
Ага, спасибо. Я не ту испорию коммитов смотрел, видимо.
Underscore.js прекрасно подхватывается с помощью shim в конфиге, как описано в посте.
кстати, да, я так и не понял, что такое «unsupported», работает на ура
Наверное, имеется ввиду, что она все равно выдает _ в глобальный неймспейс. Но подгрузка работает вполне себе.
В случае jQuery на самом сайте RequireJS предлагают использовать гибрид ужа и ежа require-jquery.js. Что позволяет без проблем использовать плагины без поддержки AMD (которых 99.99%)
А разве jQuery из коробки не поддерживает AMD?!
Поддерживает и прекрасно работает. Проверено.
Речь не о самом jQuery, а о плагинах
НЛО прилетело и опубликовало эту надпись здесь
большое спасибо за такое развернутое дополнение!
НЛО прилетело и опубликовало эту надпись здесь
Недавно копался в require.js, возможно, кому-то сэкономят время ссылки на AMD underscore 1.3.3 и backbonejs 9.2 github.com/amdjs/underscore github.com/amdjs/backbone
НЛО прилетело и опубликовало эту надпись здесь
browserify намного удобнее чем RequireJS, в нем из коробки поддерживается CommonJS Modules 1.1
НЛО прилетело и опубликовало эту надпись здесь
Вас не напрягает постоянно писать подобную обертку и менять пути во всех файлах в случае изменения его места или его зависимостей?
define(["exports", "./sniff", "./_base/lang", "./dom", "./dom-style", "./dom-prop"],
function (exports, has, lang, dom, style, prop) {
    // code
});

Не кажется ли, что nodejs-way модули и зависимости, которые лежат вне модуля более естественны? Плюс приходит консистентность модулей на клиенте и сервере.
var exports = require("exports"),
    has = require("./sniff"),
    lang = require("./_base/lang"),
    dom = require("./dom"),
    style = require("./dom-style"),
    prop = require("./dom-prop");

PS Спрашиваю не ради холивара.
НЛО прилетело и опубликовало эту надпись здесь
На данный момент я разрабатываю альтернативу AMD/RequireJS — LMD. В нем писать такие модули можно из коробки. В данном топике я пытаюсь получить фидбэк от активных пользователей RequireJS и сделать LMD еще лучше.
А вы browserify видели?
Если вкратце, в чем преимущества вашего подхода перед browserify?
Да, конечно. В первую очередь я не позиционирую LMD как адаптр Node.js модулей(не пытаюсь перенести окружение Node.js среды в Dom.js). LMD — сборщик, оптимайзер, загрузчик, профайлер. Он ближе к RequireJS.

Концептуальное различие в том, что browserify использует «детектива» для статического поиска зависимостей — следовательно browserify не умеет из коробки подгружать модули динамически, не может делать так require('pewpew-ololo' + someVar);. Соответственно LMD умеет рабоать с модулями динамически, но приходится писать конфиг, который делает жизнь ни чуть не сложнее.

Кроме концептуальных особенностей они различаются набором фичей (на которые просто нужно время) — browserify может работать с .coffee, LMD — нет. Есть и несколько других штук, которые LMD не умеет (SourceMap) и возможно не будет уметь(jade-компилятор).

LMD, в свою очередь, имеет встроенный прозрачный кэшер в localStorage, ленивую инициализацию модулей, встроенный code coverage tool (для динамических файлов без участия сервера) и профайлер модулей. Все это предельно просто включается.
Не обязательно писать полные пути до файлов. Можно в конфиге RequireJS указать пути:
requirejs.config({
       <...>
	paths: {
		jquery: 'third-party/jquery.min',
		underscore: 'third-party/underscore.min',
		backbone: 'third-party/backbone.min',
                <...>
	},
});
И если даже что-то изменится, то достаточно будет поменять путь к файлу в загрузчике.
Спасибо. Как правило у среднего проекта бывает штук 20 файлов и тогда придется прописывать все аллиасы в одном месте. Те рано или поздно настанет такой момент, когда этот список станет неподдерживаемым. Есть ли возможность сделать наследование конфигов?
gist.github.com/3806235
у нас paths вот такой и ничего. удобно
Удобно понятие в данном случае относительное :) А как вы делаете production/development/testing сборки, когда необходимо заменить один из элементов такого списка?
про тесты сказать не могу ничего. пока не могу.
про продакшн — в билде на node.js + r.js я указываю какие файлы не билдить, какие подменить
а вообще у нас пока этой проблемы нет. файлы которые на деве — те же и на проде.
меняется только конфигурационный файл, который мы выключили из билда и на каждом сервере он свой
Я вот так делал в paths:
'dataAccess': devMode ? 'dataAccessMock' : 'dataAccessWs'

Теперь, при запросе модуля 'dataAccess/userData', будет подгружен модуль из папки, соответствующей текущему контексту.
В приложении, над которым я работаю, порядка 600-700 отдельных JS файлов. Но для продакшена они склеиваются в один, режутся комментарии, сжимаются. Для отладки склеиваются в пару десятков, помодульно. Над проектом в данный момент с клиентской стороны работают 7 человек. И никаких проблем с путями. Обычно они прописываются один раз и навсегда.
Вот не вижу как наследование конфигов может решить сложность зависимостей. Скорее только усугубит — надо будет бегать по конфигам, чтобы найти откуда растут ноги и в каком месте возникает конфликт.
Можно используя development сборку красиво унаследовать от нее production (переписать конфиг, добавить плагинов, включить сжатие). Так же я не представляю как можно писать адекватные конфиги для локализированных сборок (копипаст либо препроцессоры), а наследование может гибко изменить особенность каждый сборки (не только локаль).
Пути менять не придётся, если описать в require.config( { paths: [] } ); алиасы к библиотекам. А по поводу обёртки да, такой формат будет удобнее, если миллион зависимостей. Вот в документации пишут про него.
А не надо писать в обсолютных путях. Вы прописываете в настройке baseUrl и потом в зависимостях спокойно пляшете от него, без вводного слэша:
define([ "exports", "sniff", "_base/lang" ], function(exports,has,lang){ ... })
Это спасает, если вдруг вы переместили всю папку со скриптами, а структура не поменялась. Если же внезапно меняется структура проекта, то это довольно странно. Как если бы в Java класс внезапно сменил пакет, его тоже пришлось бы вручную переподключать везде, где он импротится.
Предположим вам необходимо, чтобы какой-то модуль лениво инициализировался (он должен быть загружен, быть в сборке, но не выполнен). Используя схему define() он будет инициализирован в начале — не подходит. А если не включить его в define() и вызывать через require(), то он по умолчанию не попадет в сборку(возможно я ошибаюсь). Как решается эта проблема?
define может возвращать не уже готовый модуль, а функцию его инициализирующую, например:
define(function(){
  return function(){
    var A = function(){}
    A.prototype = { ... }
    return A;
  }
})
Т.е. нам надо будет позаботиться о том, чтобы наш разработчик сам стартанул такой модуль и сделал это только 1 раз. Внутри модуля придется писать обвязку, а пользователю такого модуля не забывать ставить () — var require('lazy')();
Должно сработать:
define(function(){
  var Mod;
  Mod = function(){
    var A = function(){}
    A.prototype = { ... }
    Mod = A;
    return A;
  }
  return Mod;
})

Надо только уточнить, не потечем ли где-нибудь по памяти при такой работе.
Не, не сработает. Заврался.
я решаю следующим образом: мой сайт разбит на «модули». есть common, для него одна сборка, со всеми библиотеками и их расширениями, и есть конкретные, грубо говоря page1.js, page2.js, являющиеся страницами сайта.
в начале грузится common, потом в зависимости от урла — нужный модуль. маленький и красивый, ничего лишнего.
requirejs.org/docs/commonjs.html 4 параграф
В requirejs можно писать в стиле Ноды.
Да отчасти, но чтобы использовать такое в node.js (ну вдруг надо будет), то придется подключить какой-нибудь require-node.
define(function (require) {
    
});

Те мы фактически навешиваем еще один слой абстракции над уже существующей системой модулей. А хочется все-таки не писать обертку.
Ну requirejs все-таки, имхо, клиентский фреймворк. На ноде не пишу, так что не не знаю какие там реалии.
На самом деле Node-JavaScript и DOM-JavaScript крайне редко пересекаются (шаблоны, валидаторы, процессоры данных). Однако же для консистентности избавиться от такой обвязки было бы не плохо. Спасибо за ответ.
CommonJS Modules 1.1 != 2.0
RequireJS Использует 2 версию модулей и то коряво
Вот совпадение. Делаю как раз сейчас перевод этой статьи. Как считаете, стоит продолжать?
Так это не перевод.
Можно, например, подробнее осветить оптимизацию и другие вещи, которые я не упомянул.
Хмм, этой статьи не читал перед написанием. Там вперемешку с Backbone и про оптимизацию тоже не упомянули.
А можно забандлить все модули в один .js файл, что бы не делать десяток http-запросов?
Можно, в RequireJS есть утилита для сборки и оптимизации, которая это делает.
Только зачем это делать на клиенте?
так это не на клиенте делается, сбилдил все в один файл и запускай на продакшене
Можно собрать все файлы при помощи r.js

Вообще идеология — один файл=один модуль. Иначе теряется смысл асинхронной загрузки модулей. Но я делаю так: если какие-то модули нужны исключительно в одном большом модуле, то я пишу несколько define в одном файле и возвращаю только основной модуль, так что он подключает все свои зависимости, объявленные в этом же файле, а другие не могут. Для этого надо явно указывать в define имя модуля и вызывать зависимость не по пути, а по имени. Например модуль B.js:

define("A", function(){ ... });
define ("B", ["A"], function("B"){ ... });

Теоретически, после загрузки B все смогут подключать А по имени, но никто не знает, когда В загрузится, а до А напрямую достучаться нельзя.
Решает ли r.js проблему циклической зависимости?

define("A", ["B"], function(B){ ... }); 
define("B", ["A"], function(A){ ... });
нет, так работать не будет
Боюсь, что r.js такое правильно не соберет, но в принципе в requirejs предусмотрены циклические зависимости: requirejs.org/docs/api.html#circular
Хотя, если один кусок кода не работает без другого, а второй без первого, то они должны быть одним модулем, имхо.
Такое и в ноде не будет абсолютно корректно работать. В один из модулей второй подгрузится не до конца инициализировнным.
Интересно, месяц назад написал адаптацию классического паттерна призванного решать эти же проблемы. Но получил минусы в карму с аргументацией — «В Javascript это не нужно!».
И у вас, и в RequireJS, используется не Dependency Injection, а Service Locator.
Dependency Injection реализован в wire.js.
Не соглашусь. В Service Locator классы сами определяют свои зависимости, пусть даже и при помощи Service Locator. В моей реализации классы получают все зависимости извне, и они не знают, как и кем, они определяются и передаются. Безусловно, я написал там, что это не Auto DI как таковой, но по идеологии он намного ближе к нему, а не к Service Locator.
А никто еще не написал server-side решение для компиляции модулей ES Harmony в один файл с заменой неподдерживаемых сейчас инструкций?
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
по остальным пунктам все понятно, но второй…
По-умолчанию, requirejs шлет AJAX-реквесты, когда вызывается require и модуль не находится в кеше. Такой подход просто невероятно неэффективен в продакшене. Ведь круче сжать все в один файл и работать с синхронными модулями. Асинхронная дозагрузка может быть полезна только на штуках типа локализаций, но их можно прибить и к common.js тоже.

а как же optimize? все так как ты пишешь
НЛО прилетело и опубликовало эту надпись здесь
Можно и так, но зачем? Не лучше ли иметь окружение разработки (асинхронные модули) консистентное с окружением продакшена (конкатенированные синхронные модули)?

я не понял, а в чем разница моего и твоего подхода?

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

И этот аргумент умрет с повсеместным распространением source maps (конец этого года).
ждем-посмотрим:)
я тут написал обзор методов модуляризации. думаю он будет небезынтересен тут присутствующим.

nin-jin.github.com/etc/modules/index.xml

пишите тут в коментах плюсы и минусы упомянутых подходов. об ошибках и неточностях тоже. есть ли подходы, которые я не упомянул, а стоило бы?
JAM — Что не нравится: статический анализ сильно ограничивает, возможен overhead по коду, по соглашениям получается какая-то Java, модули ничем не ограничены, нет ленивой инициализации(все сразу интерпретируется).

В require() на самом деле больше плюсов, чем минусов: он явный(документация зависимостей по коду, IDE не ругается на глобалы), с помощью него возможна ленивая инициализация модуля. Это полезный слой абстракции, который может изменить способ загрузки модуля (.coffee -> .js), а так же позволяет собирать статистику без наглых хаков.
> статический анализ сильно ограничивает

а что именно? для динамической загрузки (необходимость которой сильно преувеличена) можно использовать хоть тот же requireJS. статический анализ на самом деле даёт гораздо больше возможностей. можно даже статически превращать модули хоть в LMD, хоть в AMD, хоть в CJS

> возможен overhead по коду

имеется ввиду, что рядом с каждым модулем нужно писать имя пакета? он не большой и, кстати, легко минимизируется статически. у меня даже был минификатор, который находил пары пакет-модуль и заменял их на короткие алиасы во всех файлах (скрипты, стили, шаблоны и даже серверные скрипты), выхлоп от этого был мизерный, а отлаживать минифицированный код — то ещё удовольствие. Ещё у меня была версия с заворачиванием кода в with( $jam ) with( $wc ) { $Component(… ) } то есть, модули используются без указания пакета, но пакеты в которых нужно искать модули перечисляются вначале файла, но из-за этого возникала неоднозначность вида «а это модуль из какого пакета?», что вносило лишь путаницу и проблемы при переносе кода между файлами (а подключён ли нужный пакет? а не используется ли модуль из не того пакета?). В результате я остановился на варианте: вырезаем коментарии и отступы, склеиваем и зипуем. А одинаковые последовательности — очень хорошо зипуются)

> по соглашениям получается какая-то Java

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

> модули ничем не ограничены

а чем они должны быть ограничены? там вся и соль, что PMS и JAM в частности не мешают реализовывать приложение так как хочется. Они лишь помогают работать с зависимостями.

> нет ленивой инициализации(все сразу интерпретируется).

ну, время интерпретации — это такой мизер, что его даже измерить толком не получается. вот тут я стенал по этому поводу: nin-jin.ya.ru/replies.xml?item_no=76
так что временем интерпретации можно пренебречь. а вот что модуль будет делать при старте, а что по необходимости — это уже зависит от разработчика этого модуля)

> В require() на самом деле больше плюсов, чем минусов: он явный(документация зависимостей по коду,

если в этом есть необходимость, в PMS несложно сделать вывод списка зависимостей для каждого модуля, также как сейчас это делается для пакетов. так что посмотреть от чего зависит не сложно. а вот захламлять код портянкой require() и заставлять за ними следить вручную — плохо. в результате получается объявление зависимостей в двух местах («по факту использования» и «по факту включения»), которые обязаны быть синхронизированными.

> IDE не ругается на глобалы),

тут стоило бы уточнить что за ide. komodo вот, например, не ругается. видимо потому, что в глобальной области видимости слишком много всего-всего и с каждым релизом браузеров становится всё больше и больше. вести реестр этих свойств — неблагодарное занятие. а та ide, что следит за глобальными перемеными, могла бы для приличия хотябы просканировать файлы проекта на предмет определения глобальных переменных.

> с помощью него возможна ленивая инициализация модуля.

а если нужно не весь модуль инициализировать лениво, а только часть? опять же, нет смысла смешивать зависимости и ленивые вычисления. это перпендикулярные понятия, которые лучше оставить независимыми.

> Это полезный слой абстракции, который может изменить способ загрузки модуля (.coffee -> .js),

MPS как бы из коробки поддерживает различные форматы. научить его ещё и варить кофе — не проблема. другое дело, что этот синтаксический сахар не стоит тех проблем с отладкой, которые он вызывает.

> а так же позволяет собирать статистику без наглых хаков.

о какой статистике идёт речь?
>а что именно?

var r = require;

r("pewpew-ololo");


> а чем они должны быть ограничены

тем, что они не должны, по крайней мере, лежать в одном скоупе

> нет ленивой инициализации

кроме времени инициализации, которое нужно замерять в доисторических системах и мобильниках, существуют еще и ресурсы, события, вычисления, которые тянет каждый модуль.

> заставлять за ними следить вручную

Лучше явно чем как-то так автоматически. require() дает возможноть использовать любые имена импорта втч короткие. Максимум документации должно быть в коде — код должен быть самодокументируемым. То, что доки где-то лежат вне файла многим наплевать(не удобно туда-сюда прыгать).

> а только часть

делим модуль на 2 части во время оптимизации

> нет смысла смешивать зависимости и ленивые вычисления

в require() ленивая загрузка — бонус архитектуры

> не стоит тех проблем с отладкой, которые он вызывает

Согласен, просто как вариант. С require() возможно автоматическое применение Code Coverage инструкций без участия бэкэнда.

> а так же позволяет собирать статистику без наглых хаков

Статистика подключений — вызовы require(), профилирование времени инициализации модуля, сбор динамической статистики.

> var r = require;
> r(«pewpew-ololo»);

ну, я там много распинался по поводу того, что именование одной сущности по разному в разных местах — это плохо как для машины, так и для человека. в этом случае любой принцип именования «нас ограничивает», так же как перила ограничивают нас в возможности спрыгнуть с моста и стрелять себе в ноги, чтобы снизить скорость падения)

> тем, что они не должны, по крайней мере, лежать в одном скоупе

а почему бы и нет? изоляция нужна в случае ада вида «два модуля зависят от двух несовместимых версий другого модуля». но мы же не хотим грузить клиенту несколько версий одного модуля, правда? оупенсорс же в конце концов: если что-то не устраивает — почини. и себе жизнь упростишь и остальным.

> кроме времени инициализации, которое нужно замерять в доисторических системах и мобильниках, существуют еще и ресурсы, события, вычисления, которые тянет каждый модуль.

которые вовсе не обязательно выполнять при старте, если в том нет необходимости. хотя да, если либа изначально не ленива, то загрузчик с эвалом в нутрях сделает её ленивой. правда цена — координаты строк будут указывать в стратосферу. и не надо рассказывать про светлое будущее с sourcemap — нам сейчас мучиться)

> Лучше явно чем как-то так автоматически.

автоматически на основе _явной_ декларации использования. $jam.Component — не менее явно, чем require( 'jam/Component' ).

> Максимум документации должно быть в коде — код должен быть самодокументируемым. То, что доки где-то лежат вне файла многим наплевать(не удобно туда-сюда прыгать).

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

> в require() ленивая загрузка — бонус архитектуры

а у меня бонус фреймворка — модуль $jam.Lazy, который позволяет сделать ленивым не только модуль, но и любую функцию)

> Согласен, просто как вариант. С require() возможно автоматическое применение Code Coverage инструкций без участия бэкэнда.

и всётаки мы скатились до бесполезного спора «а у нас в квартире газ, а нас свой водолаз»). в конце концов всегда можно нагенерировать аннотацию со списком зависимостей, если она всё-таки нужна. у всех разные приоритеты — на том и порешим)

> Статистика подключений — вызовы require(), профилирование времени инициализации модуля, сбор динамической статистики.

ну, ничто не мешает статически трансформировать JAM модули в AMD и обретать все его плюсы и минусы и переключаться между сборками в зависимости от того, что важнее)

основной-то посыл был всё-таки такой:
1. собирать нужно не только js, но и прочие прилагающиеся ресурсы. авторы CJS, AMD и других стандартов упорно про это забывают.
2. не за чем плодить рутину там, где достаточно простого соглашения и нехитрой автоматики.

ты всё-таки попробуй поиграться с примером — просто создаёшь файл, пишешь там код и обновив страницу получаешь его исполнение. при этом грузится только то, что реально используется. это очень удобно и позволяет сконцентрироваться на реализации не отвлекаясь на зависимости.
По поводу «собирать нужно не только js» — согласен.
По поводу рутины — не соглашусь — должно быть явное разделение того, что входит в сборку и что используется.

На крайняк можно заюзать wildcard "*": "*.js" и включить в сборку все модули.

Еще каждый модуль может требовать наличие какого-то плагина, что в случае с «бесконфигной» архитектуры влечет за собой всякое колдовство в коде и хтрый его анализ.

И еще я считаю, что модули должны быть гибко абстрагируемы от файловой системы. У тебя завязывается на соглашения по зависимостям, которые, как я понял, ложатся на FS.

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

> ты всё-таки попробуй поиграться с примером
— спасибо, я уже с browserify наигрался =)

$jam.Lazy — «а у нас в квартире газ, а нас свой водолаз» :) Все подобные фичи это дело времени.

Скажи как ты собираешься организовывать локализацию на разные языки, которые могут влеч за собой отключение каких-то фичей? Ну и как делать версии для dev/prod/test? Желательно без копипаста.
> должно быть явное разделение того, что входит в сборку и что используется.

почему должно? одно без другого не имеет смысла. включить модуль в сборку, но нигде им не воспользоваться… зачем? обычно это не надо. а когда надо — всегда можно «воспользоваться» им в *.meta.tree. использовать модуль, не подгрузив его… зачем? код же не будет работать, а будет сыпать ошибками. разве что какой-то хитрый вариант типа «нужный модуль будет подгружен потом другим модулем, чтобы этот модуль мог им воспользоваться» необходимость которого я с трудом представляю.

> На крайняк можно заюзать wildcard "*": "*.js" и включить в сборку все модули.

а если не все из них используются? у меня вот есть условно 2 типа пакетов: библиотеки и приложения. когда собирается приложение — его модули грузятся все, а вот из библиотек только те, что реально используются.

> Еще каждый модуль может требовать наличие какого-то плагина, что в случае с «бесконфигной» архитектуры влечет за собой всякое колдовство в коде и хтрый его анализ.

ничего не влечёт. я плохо акцентировал на этом внимание… автоматика сильно упрощает работу, но она не творит чудеса) если требуется плагин, который регистрируется как, например, функция в инстансе jq. то есть факт его использования в общем случае статически определить сложно, то эту зависимость надо будет определить явно. в meta.tree или же в самом jam.js в комментарии (то есть опуститься до уровня cjs ;). другой вариант — добавить алиас, чтобы обращение к нему удовлетворяло принципам jam:
$jd( '.fancybutton' ).$jq_fancybutton()
но тогда, разумеется, появляется некоторая избыточность обращения.

> И еще я считаю, что модули должны быть гибко абстрагируемы от файловой системы.

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

> каждый объект скоупа увеличивает цену вызова функции, лежащей в этом скоупе

ну там же хэш-таблица. разница незначительна.

> спасибо, я уже с browserify наигрался =)

ну это же совсем не то: о второй уровень из 4 по моей шкале х)

> как ты собираешься организовывать локализацию на разные языки, которые могут влеч за собой отключение каких-то фичей?

хороший вопрос) я пока делал только подмену текстов, но там был отдельный формат для локалей, который собирался также как и все остальные, но на выходе получались собранные бандлы типа index.locale=ru.xml из файлов типа ya_search.locale=ru.xml
думаю можно реализовать аналогичную поддержку «плоскостей сборки» для всех форматов. а ковыряться в конфигах, прописывая каждую локализованную версию файла — совсем не хочется.

> Ну и как делать версии для dev/prod/test?

а это зачем? на мой взгляд разница должна быть настолько минимальной, на сколько это возможно. а если нужны какие-то дополнительные инструменты для дебага — грузить их отдельным пакетом.
> нужный модуль будет подгружен потом другим модулем, чтобы этот модуль мог им воспользоваться

У тебя архитектура предполагает факт того, что логика приложения может динамически расширятся?

> гибко абстрагируемы от файловой системы

хороший тому пример "i18n": "ru.json" и "i18n": "en.json"

> ну там же хэш-таблица. разница незначительна

При каждом вызове функции скоуп функции пересоздается. Это, конечно, копейки, но в некоторых случаях и копейка важна — тормозные браузеры, горячие функции, время-зависимые приложения (спиддиал например)

> прописывая каждую локализованную версию файла

И тут в тред врывается абстракция над файловой системой и наследование конфигов! ;-)

> разница должна быть настолько минимальной, на сколько это возможно

Тут минимум dev — один хост бэкэнда, test — другой. Ну и всякие хитрые оптимизации production-сборки.
> У тебя архитектура предполагает факт того, что логика приложения может динамически расширятся?

сама по себе? нет) вся логика должна быть задекларирована программистом, протестирована тестировщиком и раскатана по cdn-у) это если говорить абстрактно. а конкретно — не должно быть никаких висячих зависимостей. глупо делать так, чтобы фреймворк зависел от опционального плагина. или я что-то не улавливаю?

> хороший тому пример «i18n»: «ru.json» и «i18n»: «en.json»

пример чего? я могу отвечать только раз в сутки, так что переспрашивать не очень удобно

> Это, конечно, копейки, но в некоторых случаях и копейка важна

сферическая копейка в вакууме конечно важна, но на практике бутылочные горлышки находятся в совсем других местах

> спиддиал например

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

> И тут в тред врывается абстракция над файловой системой и наследование конфигов! ;-)

фс — сама по себе уже достаточно неплоха абстракция древовидной структуры и незачем лепить сверху практически то же самое, но со своими праилами блекджека. линух на этом основан. а у меня нет конфигов и нечего не надо наследовать: Р

> Тут минимум dev — один хост бэкэнда, test — другой. Ну и всякие хитрые оптимизации production-сборки.

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

> чтобы фреймворк зависел от опционального плагина
Тут я про линивую загрузку модулей.

> пример чего
Пример абстракции над файловой системой

> Это, конечно, копейки, но в некоторых случаях и копейка важна
Недавно буквально наткнулись на такую проблему (плагины для браузра) без ленивой загрузки и без прогретого JIT он стартует 150 мс (ну очень тяжелый), а с ленивой загрузкой 20мс

> тоже занимался спиддиалом?
Консультировал по оптимизации визуальных закладок 2,0.

> а у меня нет конфигов и нечего не надо наследовать
ок

> тот хост с которого загружена страница
особенность архитектуры, часто не мы решаем какая она должна быть (адрес бэкнда другой у дева и продакшена)

Что-то у нас тред разросся :) Понаписали уже больше чем в статье.

> Тут я про ленивую загрузку модулей.

в том-то и дело, что ленивая нужна загрузка не модулей, а пакетов. иначе будет 100500 запросов.

> Пример абстракции над файловой системой

а, пример определения модуля i18n как алиаса на один из конкретных файлов? а что делать в случае, когда в какой-нибудь турецкой локали не хватает нужных строк? тут нужно не просто «всё решать одной абстракцией» — тут надо парсить локали и собирать сборную солянку. либо громко ругаться, что не хватает переводов. но уж точно не просто заменять один файлик на другой.

> Недавно буквально наткнулись на такую проблему (плагины для браузра) без ленивой загрузки и без прогретого JIT он стартует 150 мс (ну очень тяжелый), а с ленивой загрузкой 20мс

я.бар?) ну, вообще говоря, да, специфика плагинов в том, что подгрузка модулей из памяти не даёт такого пенальти, как подгрузка с сервера, зато есть куча левого функционала, который практически не используется. поэтому для мозиллы, например, я написал клёвый ленивый загрузчик (https://github.com/nin-jin/fenix-snippet#%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8F%D1%80%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F) — кстати, хороший пример, ленивой загрузки и изоляции модулей, оставаясь при этом в рамках PMS. правда ценой типовой шапки из нескольких строк: подключение загрузчика и подключение каждого необходимого пакета, модули из которых грузятся уже лениво.

> Консультировал по оптимизации визуальных закладок 2,0.

под какой браузер? и какие выводы?

> особенность архитектуры, часто не мы решаем какая она должна быть (адрес бэкнда другой у дева и продакшена)

и всё же, ошибки в архитектуре тоже надо исправлять, иначе есть риск погрязнуть в костылях.

> Что-то у нас тред разросся :) Понаписали уже больше чем в статье.

автор нас наверно уже проклял, сглазил и навёл порчу х)
Да нет, мне, наоборот, приятно, что мой скромный пост вызвал такую дискуссию.
Отдельные модули можно кэшировать в localStorage, а загрузка сборок — overhead.

> либо громко ругаться, что не хватает переводов. но уж точно не просто заменять один файлик на другой.
Так и делается. Берется сервис переводов(который ругается на непереведенные штуки) с него все сливаем в один файл, затем натравливаем локализатор и вуаля. Ну тут уже дело не в том как организуется этот процесс, а в том сколько нужно накопипастить без наследования конфигов.

> какие выводы
прогревать все функции (для JIT) и хранить в расшаренной области.

> ошибки в архитектуре тоже надо исправлять

это не ошибка, а особенность (не всегда бывает возможно стучаться к локалдомену)

оверхед — это 100500 хттп запросов и как следствие лишние задержки. а кеширование в ls — ещё один костыль, не добавляющий ни надежности ни поддерживаемости.

тут нужно наследование локалей. и не через какой-то там сервис, а прямо тут, при разработке. и не конфигов, а самих локализованных строк. или строки у тебя в конфигах? для каждого модуля?

для jit функции и сами прогреются когда надо будет. на то он и jit. ускорять работу одной фичи путём торможения работы всего браузера — не самая светлая идея. если все будут считать себя пупами земли и прогревать себя при старте — браузер будет стартовать как старичок нетскейп. ну или тупить первую минуту, как винда.

а в каких случаях таких возможностей нет?
Переводы получаются по ключу. Браузер прогревает сам некоторые функции расширений (сам видел, как это точно делается не скажу — все гдето в глубинах модели расширений ФФ).
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.