Pull to refresh

Comments 62

Это аналог AMD/RequireJS. Полностью повторяет структуру CommonJS модулей. Позволяет использовать их в браузере.
Это не ответ.

Вот, нашел статью по этому вопросу: esa-matti.suuronen.org/blog/2013/03/22/journey-from-requirejs-to-browserify/

Вольный перевод заключения:

Преимущества Browserify перед RequireJS

  • Всегда делает конкатенацию в один файл. Отпадают проблемы со сборкой в дальнейшем.
  • Позволяет использовать пакеты из npm.
  • Проще синтаксис: module.exports = ... .
  • Позволяет использовать фишки NodeJS.
  • Source maps, вот том числе с поддержкой CoffeeScript (хотя RequireJS, возможно, уже имеет эти возможности).
  • Замечательный API плагинов.


Недостатки Browserify перед RequireJS

  • Всегда делает конкатенацию в один файл. Отладка браузеров, не имеющих поддержки source maps, может стать мукой.
  • Для работы с Browserify необходимо запускать в консоли watcher, который будет пересобирать код по мере его редактирования. RequireJS можно пользоваться в браузере, вообще без консольной утилиты.
  • Есть люди, выступающие против того, чтобы помещать в репозиторий npm пакеты, предназначенные только для браузера. Я не знаю, какова позиция мейнтейнеров Node/npm на этот счет, но в принципе вы не ограничены npm. При желании можете использовать, например, Bower.
  • Документация могла бы быть и получше Предполагается, что вы знаете многие нюансы из мира Node. См. пост по ссылке для разъяснения некоторых из них.
  • Отсутствует коммьюнити, если не считать таковым коммьюнити самого Node. Хотелось бы mailing list и IRC-канал.


Актуальность данной информации — март 2013.
Спасибо за ответ. Думаю, многим он будет полезен.

Что касается минусов, то могу сказать так:
— Браузеров без поддержки source maps всё меньше и меньше, а такие гиганты как Mozilla и Google уже давно ввели их поддержку.
— Мммм, да, но в большинстве случаев у вас и так висит watch для каких-нибудь препроцессоров или т.п.
— Это не минус а одно из мнений. Тем более что вам мешает создавать пакеты отдельно и паблишить их в npm, откуда потом через package.json вы их и будете тянуть командой npm install?

Последние два пункта хоть и верны, но для меня, лично, спорны. Вы видели requirejs community, например?
Есть еще один минус. Довольно существенный:

нельзя сделать подгрузку модулей по необходимости, имеется ввиду загрузку приложения по частям
в том числе нельзя сделать асинхронную подгрузку модулей.
Да, но в большинстве случаев, дабы минимизировать просадку на лишние запросы к серверу, все файлы всё равно минимизируются в один. В require.js это сделано с помощью r.js, здесь это работает по умолчанию.
Но если приложение огромное, то загрузка по частям наоборот комфортнее, чем загрузка сразу всего.
С require можно сделать и так и так и смешанный вариант — т.е. объеденить несколько модулей и загружать их кусками, а browserify альтернативы не дает.
Насколько же оно должно быть огромное… У нас весь код LMS (а она, поверьте, не маленькая) умещался в 230Кб, которые кешировались на клиенте после первой же загрузки. Какой профит вы с этого получаете? На какие чанки вы собираетесь разбивать проект? По сколько кб/файл, чем это обусловлено / продиктовано?

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

У нас только лишь js одного сингл-пейдж раздела весит 1МБ + CSS с инлайновыми картинками 2 МБ. Тоже будете весь проект разом грузить?)
Господи, что же за проект, где SPA весит 1Мб?! Это получается в районе 1 048 576 символов (минифицированного кода, как я понимаю).

Как связаны CSS и модульная система JS? Хотя base64 в CSS вы всё равно зря кладёте. Браузер рендерит страницу только тогда, когда загрузились стили, а если стили весят 2Мб, то вы намеренно увеличиваете время загрузки страницы. Объедините изображения в 2 спрайта: ретина/не ретина и живите счастливо.

P.S. Отвечая на ваш вопрос: да, я буду загружать один файл, потому что, как я уже сказал выше, он будет кеширован сразу после первой загрузки. Подключение сайта по частям не даст нормального результата: пользователь будет ждать каждый раз ответа от CDN, что в результате получится больше по времени(суммарном), чем обычная загрузка 1 файла с того же CDN. И это не говоря уже о постоянном ощущении «подлагивания» при переключении страниц.
Сам движок на Ангуляре весит примерно как у вас. Плюс закешированные html-шаблоны, плюс всякие плагины. Плюс jQuery, селектайзы и т.п, которые естественно, не на каждой странице нужны.

У нас деплой раз в неделю, кеширование не спасет. Тем более, неизмененные модули останутся в кеше, а загрузится только то, где были правки
Ну, видимо в вашем случае действительно подобный подход себя оправдывает. Всё ведь сугубо индивидуально, сами понимаете. Как я уже писал выше, browserify не является панацеей или единственно правильным решением, все исходит от самого проекта. Так или иначе, надеюсь, вы нашли статью занимательной :)
Во фронтенде не раз использовал three.js для панорам, который весит поболее 230кб (минифицированный 430кб), и грузить его надо, только когда дело до панорам доходит. Тут RequireJS себя с лучшей стороны показывает, предоставляя такие возможности.
Можно. Свой transform + свой browser-pack + чуть клиентского кода. Но из коробки Browserify этого не умеет.
Браузеров без поддержки source maps всё меньше и меньше

соурс-мапы не помогают получать адекватные стек-трейсы. особенно доставляет в модульных тестах.
Со стек-трейсами верно, но по поводу тестов не согласен: всё зависит от человека, который пишет тесты. Я не понимаю, в чём различие при тестировании конкатинированного файла?
когда тест валится трейсы показывают место возникновения ошибки. при тестировании сборки, исходники, которые надо править, приходится доискивать лапками.
— Браузеров без поддержки source maps всё меньше и меньше, а такие гиганты как Mozilla и Google уже давно ввели их поддержку.

По поддержке source maps все более менее только у chrome (возможно у Safari тоже). Firefox странновато с ними работает, у него есть баги в реализации (одни и те же sourse maps в лисе и хроме указывают на разные места, в хроме на верные, в лисе непонятно куда)
Firebug вообще не поддерживает source maps code.google.com/p/fbug/issues/detail?id=5765
1. Не все модули из NPM будут сразу работать с Browserify. Раньше Lodash не работал, например.
2. У вас ссылка потерялась.
Вы можете запустить команду, используя sudo, но я бы всё-таки рекомендовал вам сначало ознакомиться с этим постом.

3. Кто-нибудь может объяснить комментарий автора Lodash?
Why are you Browserifying Lo-Dash? It's not the target that Browserify is after.
3. — странно. Может, он предлагает оставить Lo-Dash в глобальной области видимости? :/
Или, может быть, он указывает на то, что Lo-Dash не нуждается в «браузерификации» в том смысле, что он «и без того работает в браузере».

Есть ведь и такая точка зрения на Browserify, что это прежде всего способ получить во браузере аналог целого ряда API Node.js, тогда как конкатенация скриптов — это вторичный (хотя и полезный) эффект.
Кто-нибудь использовал на практике? Мне интересна скорость сборки — понятно, что зависит от SSD/не-SSD и т.п., но скажите хотя бы порядок для ~150 файлов?
Если вы пользуетесь инкрементальной сборкой и собираете пакеты по мере необходимости, то вы не ощущаете время сборки 1 пакета. А для полной сборки 0.7-0.8 сек — мелочи. Рад, что смог ответить на ваш вопрос.
Теперь бы еще для чистоты узнать сколько займет сборка тех же файлов, но уже с помощью Require.js.
Здесь я уже понадеюсь на хабр и на читателей. Надеюсь, у кого-нибудь найдется подобная информация. Но, честно говоря, я не понимаю фетиша замера скорости для подобного рода модульных систем. В смысле, разница в скорости не будет настолько грандиозной, какая разница сколько ждать при деплое — пол секунды или секунду? :) Намного важнее, почему тот или иной инструмент удобнее/неудобнее.
Require.js. — ничего не собирает, она загружает по требованию
У require.js есть оптимизатор r.js, который занимается сборкой.
да, но это уже делает утилита, цель которой не собрать все в один файл, а просто оптимизировать загрузку модулей, если она, например видит, что модули сильно связаны, она их пытается объединить, но не факт, что все соберется в один файл
Вы не правы. r.js не находит лишь только те модули, имена который при require указываются динамически. Но в таком случае мы в build-файле указываем в include те модули, которые точно нужно включить в сборку. Ниже я писал, что сам require.js можно даже не включать в сборку, а заменить его более легковесным almond.
Вот пример build-конфига:
({
    paths: {
        requireLib: '../third-party/almond' // Указываем путь к require.js-плейсхолдеру almond.js
    },
    include: ['requireLib', 'router/front-page-router', 'router/challenge-page-router'], // Инклудим и almond, и любые другие модули.
    baseUrl: '.',
    mainConfigFile: 'loader.js', // Это основной файл с конфигом, в котором прописаны настройки require.js
    name: 'loader',
    out: 'loader-min.js',
})
Если вы считаете, что основная цель optimizer собрать в один файл, грубо говоря, цель — «конкатенация», то тогда я с вами соглашусь, что неправ, но мне видится другая цель, а именно, сборка модулей по принципу связанности.
Нет, оптимайзер делает не простую конкатенацию. Он запишет в сборку только те модули, которые будут связаны. Если модуль нигде не используется, то он пропускается(точнее, он даже не читается). Выше я писал, что проблема может быть, если мы делаем что-то вроде require(fp ? 'router1' : 'router2'), и чтобы этим модули были в сборке, то мы просто их указываем в списке include в конфигурации сборки.

Рассмотрим другой пример. У нас есть package1, у которого есть зависимость jQuery, и есть package2, который так же имеет зависимость jQuery. Мы можем собрать весь проект в один файл. А можем оставить проект модульным пофайлово, но все же оптимизировать отдельные packages. Естественно, есть смежные модули, такие как jQuery, код которых не имеет смысла включать в оба оптимизированных пакета, мы будем его грузить как обычно, отдельным файлом.
Тогда build-конфиг будет примерно такого вида:
({
    mainConfigFile: 'loader.js', // Это основной файл с конфигом, в котором прописаны настройки require.js
	modules: [
		{
			name: "modules-package-01",
			exclude: ["jquery"]
		}
	],
	dir: './deploy/'
})


При данной конфигурации не будет создан единый loader.min.js. R.js поместит все найденные модули в папку ./deploy, модули, которые мы указали, будут оптимизированны(будет произведена конкатенация зависимостей), однако, jQuery конкатенирован не будет, и при загрузке пакета(набора модулей) на клиенте модуль с jQuery будет запрашиваться отдельно. В итоге мы получаем склеенный package, в котором есть куча уникальных зависимостей, и он будет грузиться одним файлом(там будет набор моделей, вьюх, роутер, и все другое, что для других пакетов не нужно). И jQuery все еще будет грузиться из кеша отдельным файлом(хоть с CDN гугла, нам не важно).
Как по мне — это невероятно крутая фича, и при правильном применении можно собирать проект очень грамотно. И я сильно сомневаюсь, что подобный функционал кто-то предоставляет еще.

Если интересно, читайте доки оптимизатора require.js, или можете быстро пробежаться по примеру build-конфига, где все подробно расписано с комментариями.
Я написал, что require js ничего не собирает, имея в виду конкретно модуль require js, а не r.js — оптимизатор, который собирает, и то не всегда в один файл и цель, которого не простая конкатенация, вы меня тут отправляете к докам, пишите про build конфиг, зачем?.. r.js — это addon, его можно использовать или нет. Основное назначение Require js, повторю, отложенная загрузка по требованию, вот это мой посыл, а вы его видимо не понимаете и пишите, про то, что есть еще r.js, который собирает и какой он крутой, я вам об одном пишу, а вы о другом.
Ок, я вас понял. Вы правы, а я нет, если вам так угодно. Однако, r.js — часть проекта require.js, и он собирает так, как его попросишь, хоть в один файл, хоть в несколько.
Почитайте, пожалуйста здесь историю создания проекта и самое главное мотивацию, т.е. основным мотивом было асинхронная отложенная загрузка модулей на клиенте, в браузере, собственно в третий раз, настаиваю на том, что это главное — это котлета, теперь попытаюсь отделить её от мух, которые вы решили заметить на котлете. В процессе развития и использования проекта скорее всего встал вопрос, а что, то если разработчики слишком например сильно будут дробить свои проекты на множество модулей, тогда возникает ситуация, когда на клиенте может возникнуть множество запросов на сервер по загрузке этих модулей, и естественным образом возникло желание сократить количество таких запросов, попытавшись оптимизировать загрузку путем объединения нескольких модулей в один, по степени связности, и вот возник оптимизатор, который так, на всякий случай, может используя стиль подтягивания модулей и плюс ваш конфигурационный файл объединить все в один, и это только частый случай работы оптимизатора, а вы его выставляете, этот частный случай, за котлету, если этот частный случай вы повсеместно используете в своих проектах, то тогда, извините за то, что я вас побеспокоил. В других проектах ваш частный случай вообще может не рассматриваться. Оптимизация, по определению носит сугубо эврестический характер, и никогда не является панацеей, некоторые разработчики в процессе написания модулей оптимизируют их контент сами, без использования дополнительных утилит. Ну если использовать require js, только для того, чтобы потом все объединить в один файл и грузить на клиент, просто потому, что нравиться стиль определения и подхватывания модулей, а потом сборки, то тогда действительно можно рассматривать его в качестве альтернативы другим подходам и библиотекам. Спасибо за понимание.
Гарантирую, что на порядок дольше. Там все же несколько иная система сборки, так как может быть использован более сложный синтаксис.
На проектах я делаю сборку только для финального тестирования, проблема была только 1 раз, из-за минифицированной библиотеки, я просто заменил ее на несжатую, и проект собрался. А если еще и хотите избавиться от самого require.js из собранного проекта, то можно использовать almond, правда с некоторыми ограничениями в проекте. Так же, если используете загрузку темплейтов через require.js (плагин text), то темплейт тоже будет включен в файл сборки, r.js сделает его инлавновым и выглядеть он будет как обычный модуль, примерно вот так:

define("text!tpl2.txt",[],function(){return"template text"})
require.js надо собирать только для продакшена — тут время сборки особой роли не играет. время сборки существенно при разработке, но в этом случае ничего собирать не надо.
Я использую. Работает быстро, для разработки есть еще watchify (https://github.com/substack/watchify). Он висит в фоне, следит за изменениями в зависимостях и очень быстро пересобирает бандл.
К асинхронной загрузке модулей, на клиент, browserfy не имеет никого отношения, сравнивать его с require js не стоит, это разные по назначению библиотеки. Browserify собирает модули в один, используя стиль require node js. Require JS позволяет асинхронно загружать модули на клиент по мере их необходимости, и это принципиально и даже концептуально их отличает. Browserify можно немного сравнить с Grunt с точки зрения основного назначения — а именно, получение целевого модуля собранного из других, и вот тут можно сравнить те или иные подходы по их использованию.
А кто-нибудь читал, что RequireJS тоже умеет делать оптимизацию?
Единственное преимущество — возможность использовать NPM-пакеты, хотя все равно стоит почитать про browserify, статье уже год и разработчики наверняка расширили библиотеку.
основных преимуществ два. во первых, возможность использовать один и тот же код, как node.js, так и в браузерах (так называемый isomorphic javascript). во-вторых, CommonJS немного лаконичнее, чем AMD.
Пользуясь случаем, хочу заодно попиарить своё экспериментальное решение, о котором я уже писал на хабре: это загрузчик, который позволяет загружать одни и те же модули (в том числе на лету) и под вебом, и под Node.js вообще без каких-либо преобразований-сборок-итп. В моём понимании именно это есть изоморфный JavaScript (оттранслировать то можно что угодно куда угодно, так что Browserify это уже читерство, а не изоморфный JS ;-) ). Правда, самый существенный недостаток — это, конечно, свой (изоморфный) формат модулей, в который нужно вручную переделывать требуемые библиотеки. Впрочем, я попытался сделать его насколько возможно простым.
чем переделка в свой формат отличается от трансляции?
Просто библиотек почти нету в этом формате, поэтому нужно переделать формат модуля в тот момент, когда хочется новой библиотекой воспользоваться. Но никакой трансляции не требуется «на этапе сборки» (между изменением кода и его запуском под вебом или в ноде).

Вообще я имел в виду, что «изоморфный JavaScript» на мой взгляд другое означает :-)
Для тех, кому не нравится каждый раз пересобирать проект — есть библиотека smoothie. Её удобно подключать во время разработки, а уже при сборке — использовать browserify.
Чем она лучше watchify?
Ничем, эти библиотеки решают разные задачи: watchify — пересобирает проект при изменениях, а smoothie — клиентская библиотека для работы (синхронного/асинхронного подключения) с commonjs модулями.
Ага, понял. Т.е. смысл как раз что бы файлы отдельно лежали.

В принципе, у нас нормально watchify прижился с source maps во время разработки.
Да, верно. Посмотрите ещё в сторону gulp, возможно будете усложнять систему сборки и замените им watchify.
У нас и так gulp :-)

watchify у нас только как один из слоев, там еще reactify используется и так далее.
Самое веселое с require.js начинается в тот момент, когда хочется выйти за рамки возможного и написать свой «супер крутой плагин», который реализует в себе загрузку модулей как прослойка. В этот момент вы со сборщиком r.js один на один, потому что он не знает какие зависимости будет грузить плагин. Для этого приходится собственноручно прописывать в include все необходимые модули, что в обычном приложении можно опустить. Поэтому приходится описывать свой процесс сборки (плагины это позволяют), и вот тут начинается деление на dev/production окружение, которые живут своей жизнью и которые надо поддерживать отдельно. Browserify исключает двухфазность проекта ценой монолитной сборки, и этим можно пожертвовать ради однообразия вашего окружения, что сэкономит время и нервы при сборке. В следующих проектах никакого require.js, это для себя решил.
Кстати, есть еще Webpack как аналог для CommonJs модулей в браузере.
Вы сейчас описали мою боль. Однако я не собираюсь уходить от require.js, концепция монолитного проекта меня не очень привлекается, когда речь идет о чем-то действительно тяжелом. Я сейчас иду в сторону модулей, которые объединяются в своего рода пакет(не те пакеты, которые предлагает require.js). А вот уже сами пакеты я и оптимизирую. Прочитайте мой комментарий выше, возможно вам подойдет моя практика.
Я сейчас иду в сторону модулей, которые объединяются в своего рода пакет(не те пакеты, которые предлагает require.js). А вот уже сами пакеты я и оптимизирую

Я как раз такие модули и использую с Browserify (естественно уже не в чистом виде)
Т.е есть модуль, у него могут быть его субмодули, при работе модуль бандлится с субмодулями в один файл. При этом модуль может иметь зависимость от какой либо общей библиотеки (например то же jquery) и эта общая библиотека в бандл не включается.
Для модуля это выглядит как:
var subModule1 = require("./dir/sub1"); //будет включен в бандл
var subModule2 = require("./dir/sub1");//будет включен в бандл
var $ = require("jquery");// не будет включен в бандл, будет загружена отдельно асинхронно.
....


При этом модуль не пытается исполниться, пока его зависимости не загружены. Поэтому описание (var someModule= require(«someModule»)) выглядит очень приятно, по сравнению с AMD модулями.

Browserify мне понравился своей модульностью и нативным node.js.
Я могу заменить в Browserify любой компонент через его конфиг и самое приятное — я могу получить от Browserify свой модуль в разобраннов виде в JSON формате:
[
{
"id": "a1b5af78",
"source": "console.log(require('./foo')(5))",
"deps": { "./foo": "b8f69fa5" },
"entry": true
},
{
"id": "b8f69fa5",
"source": "module.exports = function (n) { return n * 111 }",
"deps": {}
}
]

И дальше можно с этим JSON что-то делать странное :)
В свою очередь r.js выглядит очень странно: 1 Мб js файл, с никакой информации об ошибках.
Я выбрал Browserify и очень этому рад. На следующих крупных проектах я тоже планирую использовать Browserify. На небольших проектах с полностью стандартным функционалом вероятнее всего буду использовать require.js или может модули из ES6.
А как масштаб проекта влияет на выбор формата модулей?
Ну, если работаете уже со всеми форматами модулей — достаточно разобраться и настроить один раз.

И если уже работаете с ES6-модулями, то зачем Browserify?
Так к тому и вопросы, что эти задачи решаются при использовании любого формата модулей и нет никакой очевидной необходимости менять их в зависимости от масштаба/сложности проекта. AMD по определению поддерживает асинхронную подгрузку. Бандлы, очевидно, можно создавать с помощью r.js. Что касается ES6-модулей, то там вообще круто разделено само определение модуля и способ его загрузки — «бандлить» можно как угодно и транс-компилировать в AMD (если нужно) или в CommonJS (для Browserify-подобных упаковщиков).
Есть еще такой инструмент как lmdjs, который, насколько я помню, умеет как раз подгружать отдельные модули (бандлы)
Sign up to leave a comment.

Articles