Реализации JavaScript становятся все более и более сложными, так как прекрасный зверь, которого мы называем Интернетом, развивается с каждым годом. Многие из нас сейчас работают с модулями JavaScript — независимо функционирующими компонентами, которые объединяются в единое целое, но могут с радостью заменить любой компонент, не вызывая армагеддона. Многие из нас используют шаблон модуля AMD и RequireJS для достижения этой цели.
В прошлом году Browserify появился на сцене и вызвал много волнений. Когда пыль начинает оседать, я хотел написать обзор о том, что такое Browserify, как он работает, и несколько вариантов его добавления в рабочий процесс.
Что такое Browserify?
Browserify позволяет использовать в браузере модули стиля node.js. Мы определяем зависимости, а затем Browserify объединяет все это в один аккуратный и аккуратный файл JavaScript. Вы включаете необходимые файлы JavaScript с помощью операторов require('./yourfancyJSfile.js')
а также можете импортировать общедоступные модули из npm. Для Browserify также довольно просто сгенерировать исходные карты, чтобы вы могли отлаживать каждый файл JS по отдельности, несмотря на то, что он все объединен в один.
Зачем импортировать модули узла?
Импорт модулей — это благословение — вместо того, чтобы посещать различные сайты для загрузки библиотек для вашего JavaScript, просто включите их с помощью операторов require()
, убедитесь, что модули установлены и вы готовы к работе. Часто используемые библиотеки JavaScript, такие как jQuery , Underscore , Backbone и даже Angular (как неофициальный дистрибутив), доступны для работы. Если вы работаете над сайтом, на котором уже работает узел, вы упрощаете работу еще больше с помощью одного общего способа структурировать весь свой JS. Мне действительно нравится эта концепция.
Что вам нужно
Чтобы начать работу с Browserify, вам понадобится минимум:
- Node.js
- npm — по умолчанию устанавливается с узлом.
- Browserify — я объясню, как установить этот.
- Пакет модулей JavaScript, которые вы готовы приручить!
Начиная
Для начала вам понадобится узел и npm, установленные на вашем компьютере. Перейдите по ссылкам выше, если вы ищете руководство по их установке. Если вы полностью застряли, попробуйте эти инструкции по установке Node.js через менеджер пакетов . Вам не нужно будет выполнять какие-либо работы с узлами, чтобы использовать Browserify. Мы устанавливаем узел исключительно потому, что с него запускается npm. Получив npm, вы можете установить Browserify с помощью следующей команды:
npm install -g browserify
Здесь мы используем npm для глобальной установки Browserify на ваш компьютер ( -g
указывает npm установить глобально модуль).
Если вы получаете ошибку, которая начинается со следующего:
Error: EACCES, mkdir '/usr/local/lib/node_modules/browserify'
Тогда у вас есть проблема с разрешением. Вы можете sudo
команду sudo
, но я рекомендую проверить этот пост.
Создание вашего первого файла Browserify
Давайте начнем с создания файла Browserified JavaScript, который импортирует чрезвычайно популярный модуль Underscore . Мы будем использовать Underscore, чтобы выследить Супермена. Я назвал свой файл JS main.js
и поместил его в папку js
в моем проекте.
Мы начнем с присвоения переменной _
Underscore с помощью оператора Browserify require()
в нашем JavaScript:
var _ = require('underscore');
Далее мы будем использовать функции each()
и find()
из Underscore. Мы проверим два массива имен и запустим console.log
чтобы узнать, видит ли он Супермена или нет. Высокотехнологичные вещи, о которых Лекс Лютор мог только мечтать Наш окончательный код JavaScript будет выглядеть так:
var _ = require('underscore'), names = ['Bruce Wayne', 'Wally West', 'John Jones', 'Kyle Rayner', 'Arthur Curry', 'Clark Kent'], otherNames = ['Barry Allen', 'Hal Jordan', 'Kara Kent', 'Diana Prince', 'Ray Palmer', 'Oliver Queen']; _.each([names, otherNames], function(nameGroup) { findSuperman(nameGroup); }); function findSuperman(values) { _.find(values, function(name) { if (name === 'Clark Kent') { console.log('It\'s Superman!'); } else { console.log('... No superman!'); } }); }
Мы хотим убедиться, что Browserify сможет найти модуль npm, когда попытается добавить его в наш проект. Основные основы для этого включают в себя открытие терминала, переход к папке, в которой находится ваш проект JavaScript, а затем выполнение этой команды для установки Underscore в эту папку:
npm install underscore
Для тех, кто не node_modules
как работают node и npm, в вашем проекте создается папка с именем node_modules
в которой содержится код для вашего модуля Underscore. Команда извлекает последнюю версию Underscore из репозитория npm по адресу https://registry.npmjs.org/underscore
. С этим модулем в нашей папке node_modules, теперь Browserify может найти его и использовать.
Запуск Browserify в первый раз
Когда мы запустим Browserify, он захочет создать новый файл JavaScript со всеми нашими подключенными модулями. В этом случае он создаст файл JavaScript с Underscore внутри. Нам нужно будет выбрать имя для этого нового файла, я пошел с findem.js
. Я запускаю эту команду из корневой папки моего проекта:
browserify js/main.js -o js/findem.js -d
Эта команда читает ваш файл main.js
и выводит его в файл findem.js
определенный опцией -o
. Я включил параметр -d
чтобы он также генерировал исходную карту для нас, чтобы мы могли отлаживать main.js
и underscore
как отдельные файлы.
Использование вывода Browserify
Оттуда это так же просто, как включить файл на вашей странице, как и любой другой файл JS:
<script src="js/findem.js"></script>
Импорт ваших собственных файлов JavaScript
Маловероятно, что все ваше приложение будет исходить из узловых модулей. Чтобы включить свой собственный JavaScript, вы можете использовать ту же функцию require()
. Следующая строка JavaScript импортирует файл JS с именем your_module.js
в переменную your_module.js
:
greatestModuleEver = require('./your_module.js');
Чтобы импортировать наш JavaScript таким образом, нам просто нужно структурировать наш JavaScript как модуль. Для этого мы должны определить module.exports
. Один из способов сделать это показан ниже.
module.exports = function(vars) { // Your code }
Примечание!
Если у вас есть несколько библиотек JavaScript, которых нет в npm, и вы ищете более простой способ вставить их в Browserify, вы можете использовать модуль Browserify-shim npm, чтобы конвертировать эти файлы для вас. Мы не будем использовать это в этой статье, но некоторые разработчики могут быть заинтересованы в этом.
Наш пример с модулем
Чтобы дать простой пример того, как это работает, мы возьмем массивы из предыдущего примера поиска супергероев и заменим их отдельным модулем JS, который возвращает массив имен. Модуль выглядит так:
module.exports = function() { return ['Barry Allen', 'Hal Jordan', 'Kara Kent', 'Diana Prince', 'Ray Palmer', 'Oliver Queen', 'Bruce Wayne', 'Wally West', 'John Jones', 'Kyle Rayner', 'Arthur Curry', 'Clark Kent']; }
Далее мы импортируем этот модуль в наш код, используя names = require('./names.js')
:
var _ = require('underscore'), names = require('./names.js'); findSuperman(names()); function findSuperman(values) { _.find(values, function(name) { if (name === 'Clark Kent') { console.log('It\'s Superman!'); } else { console.log('... No superman!'); } }); }
Наша переменная names
ссылается на экспортированную функцию из нашего модуля. Поэтому мы используем findSuperman()
выше переменную names
как функцию с квадратными скобками, когда передаем массив имен нашей функции findSuperman()
.
Запустите эту команду browserify
из командной строки еще раз, чтобы скомпилировать ее, открыть ее в браузере, и она должна работать так, как ожидается, просматривая каждое значение в массиве и регистрируя, видит ли он Супермена или нет:
Передача переменных и разделение модулей через наше приложение
Чтобы добавить немного большей сложности этому довольно простому охотничьему приложению Супермена, давайте превратим нашу findSuperman()
в модуль. Таким образом, мы могли бы теоретически найти Супермена в различных частях нашего JavaScript, и мы всегда могли легко заменить наш модуль поиска Супермена на более эффективный в будущем.
Мы можем передать переменные нашему модулю и использовать их в нашей функции module.exports
, поэтому мы создадим модуль в файле с именем findsuperman.js
который ожидает получить массив имен:
module.exports = function (values) { var foundSuperman = false; _.find(values, function(name) { if (name === 'Clark Kent') { console.log('It\'s Superman!'); foundSuperman = true; } else { console.log('... No superman!'); } }); return foundSuperman; }
Я добавил возвращаемое значение для нашей функции findSuperman()
. Если он найдет Супермена, он вернет истину. В противном случае он вернет false. Это зависит от кода, который использует этот модуль, чтобы решить, для чего он использует это значение true / false. Тем не менее, есть одна вещь, которую мы пропустили в модуле выше. Мы используем Underscore в нашей функции, но не объявили это. Мы можем объявить это и в самом модуле вверху примерно так:
var _ = require('underscore'); module.exports = function (values) { ...
При использовании Browserify он просматривает все импортированные вами файлы JS и импортирует только каждый упомянутый модуль. Таким образом, нам требуется подчеркивание в нашем основном файле JS, и мы требуем его в findsuperman.js
но когда Browserify упаковывает все это, он помещает его в наш окончательный файл JS только один раз. Довольно аккуратно, верно?
Наше настоящее приложение JavaScript теперь будет использовать наш новый модуль с его новым возвращенным значением true / false. В демонстрационных целях мы просто придерживаемся простого document.write
чтобы сказать, нашел ли он Супермена по нашим именам:
var _ = require('underscore'), names = require('./names.js'), findSuperman = require('./findsuperman.js'); if (findSuperman(names())) { document.write('We found Superman'); } else { document.write('No Superman...'); }
Нам даже не нужно больше импортировать Underscore в наш основной файл JS, так что вы можете удалить его без всякой драмы. В конце концов он все равно будет импортирован путем включения его в файл findsuperman.js
.
Управление зависимостями Browserify от npm с помощью package.json
Скажем, у вас есть хороший друг, который хотел бы также использовать ваш код. Было бы немного трудно ожидать, что они знают, что им нужно сначала установить модуль подчеркивания npm. Решением этой проблемы является создание файла с именем package.json
в корневом каталоге вашего проекта. Этот файл дает вашему проекту имя (убедитесь, что в названии нет пробелов), описание, автор, версия и, самое главное, в нашем случае — список зависимостей npm. Для тех, кто разработал с помощью узла, мы используем те же самые вещи здесь:
{ "name": "FindSuperman", "version": "0.0.1", "author": , "description": "Code designed to find the elusive red blue blur", "dependencies": { "underscore": "1.6.x" }, "devDependencies": { "browserify": "latest" } }
Список зависимостей в настоящее время ограничен нашим единственным "underscore": "1.6.x"
, где первая часть зависимости — это имя, а вторая часть — версия. latest
или *
получит последнюю версию, которую имеет npm. Кроме того, вы можете ввести числа, такие как 1.6
(для версии 1.6) и 1.6.x
(для версий от 1.6.0 до, но не включая 1.7).
Мы также можем включить в качестве зависимости сам Browserify, однако это не зависимость для запуска проекта — любой пользователь нашего приложения может найти Superman без необходимости запуска Browserify. Это одна из наших devDependencies
— модулей, необходимых разработчикам для обновления этого приложения.
Теперь у нас есть файл package.json
, нам не нужно, чтобы наш друг запускал npm install underscore
. Они могут просто запустить npm install
и все необходимые зависимости будут установлены в их папку node_modules
.
Автоматизация процесса Browserify
Запуск browserify
в командной строке каждый раз, когда вы меняете файл, раздражает и совсем не удобен. К счастью, есть несколько вариантов, позволяющих автоматизировать работу Browserify.
НПМ
Сам npm может запускать сценарии командной строки так же, как те, которые вы вводили вручную. Для этого просто поместите секцию scripts
в ваш package.json
следующим образом:
"scripts": { "build-js": "browserify js/main.js > js/findem.js" }
Чтобы запустить это, вы можете ввести в командной строке следующее:
npm run build-js
Но это не достаточно удобно. Нам все еще нужно запускать эту команду каждый раз вручную. Это раздражает. Поэтому лучше использовать модуль npm, который называется watchify . Watchify — это просто, это легко и очень экономит время. Он будет следить за изменениями в вашем JS и автоматически перезапускать Browserify.
Чтобы добавить это в наш package.json
, мы добавим его в наши devDependencies
и devDependencies
новый скрипт для просмотра нашего JS (оставляйте build-js
там, когда мы действительно хотим собрать JS без необходимости изменения файла).
"devDependencies": { "browserify": "latest", "watchify": "latest" }, "scripts": { "build-js": "browserify js/main.js > js/findem.js", "watch-js": "watchify js/main.js -o js/findem.js" }
Чтобы запустить это, просто введите следующую команду.
npm run watch-js
Он будет работать и творить свою магию. Это не говорит много, хотя, чтобы дать вам знать, что происходит, что может сбить с толку. Если вы хотите, чтобы он сообщал вам подробности о том, что он делает, добавьте -v
к вашей команде watchify следующим образом:
"watch-js": "watchify js/main.js -o js/findem.js -v"
Это даст вам обратную связь, как это каждый раз, когда он работает:
121104 bytes written to js/findem.js (0.26 seconds) 121119 bytes written to js/findem.js (0.03 seconds)
Создание исходных карт в npm
Чтобы сгенерировать исходные карты с помощью npm, добавьте -d
после команды browserify
или watchify
:
"scripts": { "build-js": "browserify js/main.js > js/findem.js -d", "watch-js": "watchify js/main.js -o js/findem.js -d" }
Чтобы иметь -d
для отладки и -v
для подробного вывода в watchify
вы можете объединить их так:
"watch-js": "watchify js/main.js -o js/findem.js -dv"
хрюкать
Многие люди (включая меня) уже некоторое время используют Grunt и довольно привыкли к этому. К счастью, для этих видов Browserify прекрасно работает и со сборками Grunt!
Нам нужно изменить наш файл package.json
, чтобы использовать grunt. Мы больше не будем использовать раздел scripts
, а вместо этого будем полагаться на Grunt. Вместо этого мы добавим несколько новых devDependencies
:
{ "name": "FindSuperman", "version": "0.0.1", "author": , "description": "Code designed to find the elusive red blue blur", "dependencies": { "underscore": "1.6.x" }, "devDependencies": { "browserify": "latest", "grunt": "~0.4.0", "grunt-browserify": "latest", "grunt-contrib-watch": "latest" } }
Мы добавили в наши зависимости:
- grunt — чтобы убедиться, что Grunt установлен для проекта.
- grunt-browserify — модуль, который позволит вам запускать Browserify внутри Grunt.
- grunt-contrib-watch — модуль, который будет следить за нашими файлами и запускать Browserify каждый раз, когда они меняются.
Затем мы создаем файл с именем gruntFile.js
в корне нашего проекта. Внутри этого файла Grunt у нас будет следующее:
module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-browserify'); grunt.registerTask('default', ['browserify', 'watch']); grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), browserify: { main: { src: 'js/main.js', dest: 'js/findem.js' } }, watch: { files: 'js/*', tasks: ['default'] } }); }
Мы начинаем с нашего файла Grunt, загружая модули npm, которые нам нужны в нашем файле package.json
:
grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-browserify');
Мы регистрируем нашу единственную группу задач, которую мы хотели бы выполнить, как задачу по default
( watch
и watch
):
grunt.registerTask('default', ['browserify', 'watch']);
Мы настроили наш объект Grunt initConfig
(все файлы Grunt ищут это):
grunt.initConfig({
В рамках этого мы указываем, где находится наш файл package.json
:
pkg: grunt.file.readJSON('package.json'),
Далее следуют наши настройки Browserify, и они в основном устанавливают, где находится наш исходный файл JS с нашим кодом Browserified и файлом, для которого мы хотим, чтобы он был построен:
browserify: { main: { src: 'js/main.js', dest: 'js/findem.js' } },
Затем мы настраиваем задачу watch
для повторного запуска нашей задачи Browserify всякий раз, когда что-либо изменяется в папке js
:
watch: { files: 'js/*', tasks: ['default'] }
Из-за наших новых devDependencies
(у нас не установлен Grunt в нашем проекте, и у нас нет ни одного из этих модулей), нам нужно сначала npm install
. После того, как вы разрешите ему запускать и устанавливать любые модули, вы можете запустить очень простую команду grunt
чтобы начать наблюдать за вашим проектом.
Создание исходных карт в Grunt
В версии 2.0.1 grunt-browserify
изменился способ определения исходных карт, из-за чего многие руководства в Интернете оказались неверными! Правильный способ заставить Grunt и Browserify сгенерировать исходные карты для вас — это добавить debug: true
внутри bundleOptions
в такие options
:
browserify: { main: { options: { bundleOptions: { debug: true } }, src: 'js/main.js', dest: 'js/findem.js' } },
Эта сложная настройка параметров предназначена для включения будущих опций Browserify удобным и легко совместимым способом.
Глоток
Глоток — любитель бульдозеров Брауфрии. Статьи по всему Интернету встречаются довольно часто: Browserify и Gulp — передовой процесс сборки JavaScript, созданный на небесах. Я бы не сказал, что поклонники Browserify должны использовать Gulp, это в основном личное предпочтение между различными синтаксисами. Вы можете (как вы видели выше) довольно счастливо использовать npm или Grunt для создания вашего файла Browserify. Я лично фанат чистого и простого процесса сборки npm для небольших проектов.
Чтобы сделать это в Gulp, мы начнем с глобальной установки Gulp:
npm install -g gulp
Мы обновим наш файл package.json
devDependencies
в него несколько новых devDependencies
:
"devDependencies": { "browserify": "latest", "watchify": "latest", "gulp": "3.7.0", "vinyl-source-stream": "latest" }
Мы добавили следующее:
- watchify — мы использовали это выше в примере с npm. Тот же модуль.
- глоток — довольно важный модуль, чтобы дать нам все это глоток добра!
- винил-источник-поток — это модуль, который будет принимать входные данные и возвращать файл для нас, чтобы положить куда-то.
Browserify имеет потоковый API для вывода, который мы можем использовать непосредственно в Gulp. Несколько руководств предложат использовать плагин gulp gulp-browserify
, однако Browserify не рекомендует этого и предпочитает использовать потоковый API-интерфейс Browserify. Мы используем vinyl-source-stream
чтобы взять этот вывод Browserify и поместить его в файл, чтобы мы могли где-то вывести.
Затем мы создаем файл с именем gulpfile.js
в корне нашего проекта. Вот куда пойдет вся функциональность Gulp:
var browserify = require('browserify'), watchify = require('watchify'), gulp = require('gulp'), source = require('vinyl-source-stream'), sourceFile = './js/main.js', destFolder = './js/', destFile = 'findem.js'; gulp.task('browserify', function() { return browserify(sourceFile) .bundle() .pipe(source(destFile)) .pipe(gulp.dest(destFolder)); }); gulp.task('watch', function() { var bundler = watchify(sourceFile); bundler.on('update', rebundle); function rebundle() { return bundler.bundle() .pipe(source(destFile)) .pipe(gulp.dest(destFolder)); } return rebundle(); }); gulp.task('default', ['browserify', 'watch']);
Мы начнем с импорта в наши модули npm, что само собой разумеется. Затем мы устанавливаем три переменные для нашей сборки:
-
sourceFile
— расположение и имя файла нашего файла Browserified (в данном случаеjs/main.js
) -
destFolder
— расположение папки, в которую мыdestFolder
окончательный файл -
destFile
— имя файла, который мы хотим, чтобы наш конечный файл имел
Я объясню код более подробно ниже.
Как Browserify и Gulp работают вместе
Нашей первой задачей является наша задача просмотра, которую мы определяем так:
gulp.task('browserify', function() {
Сначала он передает наш файл main.js в модуль Browserify npm:
return browserify(sourceFile)
Затем мы используем потоковый API Browserify для возврата читаемого потока с нашим содержимым JavaScript:
.bundle()
Оттуда мы передаем его в файл с именем findem.js
и затем направляем его в Gulp для помещения в нашу папку js
.
.pipe(source(destFile)) .pipe(gulp.dest(destFolder));
Мы в основном принимаем наш вклад через различные этапы, которые переходят в наш окончательный проект, который должен быть новым блестящим файлом JavaScript!
Сочетание Watchify и Gulp
Как выяснилось ранее, использование Browserify немного раздражает, так как гораздо проще запускать его автоматически при обновлении файла. Для этого мы снова используем модуль watchify
npm.
Мы начнем с настройки задачи под названием watch
(вы можете назвать ее watchify
если хотите… это действительно ваше дело здесь):
gulp.task('watch', function() {
Мы присваиваем модуль watchify переменной bundler
как мы будем использовать его дважды:
var bundler = watchify(sourceFile);
Затем мы добавляем обработчик события, который запускает функцию rebundle()
каждый раз, когда вызывается событие update
. По сути, всякий раз, когда watchify видит изменение файла, он запускает rebundle()
:
bundler.on('update', rebundle);
Так что же такое rebundle()
? Это в значительной степени именно то, что наша задача browserify
делала выше:
function rebundle() { return bundler.bundle() .pipe(source(destFile)) .pipe(gulp.dest(destFolder)); } return rebundle(); });
Было бы возможно объединить и browserify
и watchify
вместе в некоторой острой оптимизации JavaScript, но я решил оставить их отдельно в этой статье, чтобы упростить задачу. Для впечатляющего и более сложного примера этого, посмотрите стартовый файл Дэна Телло Gulp .
Чтобы завершить наш gulpfile.js
, мы определяем нашу задачу по умолчанию, которая работает так же, как задача по умолчанию в grunt.
gulp.task('default', ['browserify', 'watch']);
Для запуска приведенного выше кода Gulp у вас есть три варианта. Самый простой способ — запустить заданную по умолчанию задачу, для которой требуется только одно слово в командной строке:
gulp
Это запустит задачу browserify
один раз, и задача наблюдения начнет watch
файлы на предмет изменений.
Вы также можете специально запустить задачу browserify
:
gulp browserify
Или ваша задача watch
:
gulp watch
Создание исходных карт с использованием Gulp и Browserify
Чтобы создать исходную карту для вашего JavaScript, включите {debug:true}
в обе функции bundle()
.
Наша задача browserify
будет выглядеть так:
gulp.task('browserify', function() { return browserify(sourceFile) .bundle({debug:true}) .pipe(source(destFile)) .pipe(gulp.dest(destFolder)); });
Функция rebundle()
в нашей задаче watch
будет выглядеть так:
function rebundle() { return bundler.bundle({debug:true}) .pipe(source(destFile)) .pipe(gulp.dest(destFolder)); }
Вывод
Это все еще довольно ранние дни для Browserify, и он, безусловно, будет развиваться и взрослеть с течением времени. В своем нынешнем состоянии это уже очень удобный инструмент для структурирования вашего модульного JavaScript, и он особенно хорош для тех, кто использует Node на своей базе. Код становится намного чище для разработчиков Node при использовании модулей npm как в передней, так и в задней части проекта. Если вы еще не сделали Browserify, попробуйте в следующем проекте JavaScript и посмотрите, не потрясет ли он ваш мир.
Другие источники
Есть множество других ресурсов Browserify. Несколько полезных деталей, которые вы можете попробовать:
- Справочник Browserify — очень ценный справочник Джеймса Холлидея о начале работы с Browserify. Определенно стоит прочитать!
- Gulp + Browserify: The Everything Post от Дэна Тельо — действительно полная статья, показывающая более продвинутые способы использования.
- И точно так же, как сейчас выходят Grunt и RequireJS, сейчас речь идет о Gulp и Browserify — Мартин Женев рассказывает о своем внезапном переходе на Browserify и Gulp с примером.
- Введение в Gulp.js — больше информации о том, как использовать Gulp от Крейга Баклера.