Октябрь 2015

Том 30, номер 10

Visual Studio - Современные средства для веб-разработки: Bower

Адам Тьюлипер | Октябрь 2015

Продукты и технологии:

Bower, Node.js, NuGet, GitHub, ASP.NET 5, Visual Studio 2015

В статье рассматриваются:

  • установка и использование Bower;
  • управление зависимостями;
  • серверы сборки и контроль версий исходного кода;
  • поддержка Visual Studio.

Долгое-долгое время мы жили в красивом саду за высокими стенами. В этой защищенной экосистеме веб-разработки мы применяли изощренные технологии вроде ASP.NET и Visual Studio. Инструментарий остального мира считался неполноценным. Мы были частью империи, если хотите, и это было весьма славное место для жизни.

Со временем культуры, инструменты, ресурсы и прочие элементы разработки стали фрагментированными и даже хаотичными. Но за это время появились весьма привлекательные и основательные технологии, включая Bootstrap, AngularJS, Git, jQuery, Grunt, Gulp, Bower, и веб-разработчики, привыкшие к экосистеме Microsoft, получили возможность использовать эти инструменты.

В этой статье из двух частей я дам обзор Bower — диспетчера пакетов, предназначенного в основном для веб-разработки на клиентской стороне (но не ограниченного только ею). Во второй статье я расскажу о Grunt и Gulp, двух JavaScript-средствах запуска задач (task runners), которые можно использовать для выполнения задач всех видов, например для копирования файлов, уменьшения размера изображений (minification), конкатенации и даже компиляции.

Grunt, Gulp и Bower являются дополнительными средствами в вашем арсенале инструментария веб-разработки. Средства интеграции с ними встроены в Visual Studio 2015, а для Visual Studio 2012 и 2013 доступны через систему надстроек. Вам все равно потребуется устанавливать их.

Возможно, некоторые из вас спрашивают себя, уж не хочет ли Microsoft заставить нас изучать и использовать еще больше инструментов? Разве NuGet не справляется с пакетами, а msbuild не достаточно в качестве средства компиляции и сборки? Ответ на оба вопроса: да — во многих, но не во всех сценариях. И для тех случаев, где традиционных инструментов недостаточно, могу помочь Grunt, Gulp и Bower. В каком-то веб-проекте вам может понадобиться компилировать свой Sass-код всякий раз, когда CSS-файл изменяется. Или вам может потребоваться новейший выпуск Bootstrap или Angular, не дожидаясь, когда кто-нибудь в Microsoft создаст соответствующий NuGet-пакет. Вы не в состоянии выполнить ни одну из этих задач с помощью NuGet или msbuild.

NuGet — потрясающая технология, и она по-прежнему развивается, поддерживается и тесно интегрируется с Visual Studio. Продолжайте использовать ее в своих проектах, особенно в тех, где создаются двоичные файлы, и в тех, где нужно вносить изменения в ваши решения Visual Studio. Однако, когда выходит новая версия jQuery или Bootstrap, кто-то должен создать и выпустить NuGet-пакет для нее. Поскольку Bower способен задействовать семантический контроль версий (semantic versioning), как только какой-то инструмент выпускается и добавляется в GitHub, Bower может использовать его; нет нужды ждать, пока кто-то упакует его в NuGet-пакет.

Я буду использовать Node Package Manager (npm) для установки Bower и нескольких других средств в следующей статье. Поскольку npm не предоставляется в виде отдельного пакета для скачивания, просто установите Node.js с nodejs.org. Подробнее об установке и применении npm, см. страницу Microsoft Virtual Academy «Package Management and Workflow Automation» на bit.ly/1EjRWMx.

Bower — основной диспетчер пакетов, обычно применяемый для клиентской веб-разработки и, по-видимому, это единственный диспетчер пакетов только для клиентской части. Большинство пакетов, используемых в клиентской веб-разработке, например Bootstrap, jQuery и AngularJS, можно устанавливать либо через npm, либо через Bower, но во многих случаях управление зависимостями может оказаться чуть легче при использовании Bower (хотя кто-то со мной не согласится).

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

Установка и использование Bower

Bower обычно запрашивает репозитарий Git, поэтому вам понадобится установить msysgit (msysgit.github.io) и выбрать вариант запуска из командной строки, как показано на рис. 1.

Установите msysgit с поддержкой командной строки
Рис. 1. Установите msysgit с поддержкой командной строки

При использовании npm вы устанавливаете Bower глобально, и тогда с ним можно работать где угодно в вашей системе. В этом случае Bower устанавливается лишь раз, а не для каждого проекта индивидуально:

npm install -g bower

Теперь вы готовы к работе с Bower. Откройте командную строку в корневой папке проекта и, придерживаясь следующего формата, установите пакет в проект:

bower install <package name/url/zip/etc.> --save

Например, чтобы установить jQuery, просто введите:

bower install jquery --save

Первые три слова, видимо, понятны и так, но --save следует пояснить. Этот параметр заставляет помещать запись в файл bower.json, отмечая тот факт, что вы установили данный пакет. (Bower не создает этот файл по умолчанию; вы должны сообщить о необходимости сделать это, и вскоре я расскажу, что для этого нужно.) По умолчанию Bower-команда install создает папку bower_components в папке, где вы запустили команду install; имя папки bower_components можно изменить с помощью конфигурационного файла Bower, .bowerrc.

На рис. 2 вы заметите, что установка пакета jQuery приводит к появлению намного большего количества файлов и папок, чем вы, вероятно, ожидали. В моем проекте реально требуется только jQuery.js, но в данном случае я получаю все дерево исходного кода jQuery. Установка многих пакетов действительно дает полные деревья исходного кода, зачастую значительно больше того, что вы хотели. Это оставляет новых пользователей Bower в недоумении, какой файл следует задействовать.

Bower устанавливает все дерево исходного кода jQuery
Рис. 2. Bower устанавливает все дерево исходного кода jQuery

Некоторые проекты будут давать на выходе упакованную версию приложения за вычетом всех дополнительных файлов, которые вам не нужны. Например, Bower-пакет для AngularJS — это содержимое репозитария из корня Angular в GitHub по ссылке github.com/angular/bower-angular. При установке этого пакета (bower install angular --save) вы получаете только файлы .js и .css, на которые вы должны ссылаться на HTML-страницах.

Чтобы найти пакеты, можно перейти на bower.io, использовать Visual Studio IntelliSense (об этом позже) или выполнить поиск в репозитарии Bower-пакетов через командную строку:

bower search jquery

Кроме того, вы можете установить несколько пакетов одновременно, что удобно при установке через скрипт:

bower install jquery bootstrap-css angular
# Или устанавливаем конкретную версию jquery ui
bower install jquery-ui#1.10.4
# Устанавливаем репозитарий github как пакет
bower install https://github.com/SomeRepository/project.git

Bower создает локальный кеш устанавливаемых пакетов. В моих системах Windows 8 и Windows 10 папка кеша по умолчанию — C:\Users\<MyName>\AppData\Local\bower\cache\packages (эту папку по умолчанию можно переопределить в файле .bowerrc). Как видно на рис. 3, кешированными пакетами можно управлять командами list и clean. Заметьте, что Bower по возможности будет извлекать пакеты из локального кеша всякий раз когда вы выполняете команду bower install.

Рис. 3. Использование локального кеша Bower

# Устанавливаем пакет jquery впервые
bower install jquery

# Удаляем пакет jquery
bower uninstall jquery

# Устанавливаем из кеша (т.е. работаем
# в отсоединенном состоянии)
bower install jquery --offline

# Вывод содержимого кеша
bower cache list

# Очистка локального кеша
bower cache clean

# Команда потерпит неудачу, так как пакет
# отсутствует в локальном кеше
bower install jquery --offline

В идеале, вы захотите включать файл bower.json в свое приложение, чтобы Bower мог отслеживать зависимости и версии вашего пакета.

Для пакетов, зависимых от других пакетов, Bower пытается установить требуемые зависимости в вашу файловую систему. Bower имеет линейное дерево зависимостей, т. е. любые необходимые зависимости устанавливаются прямо под /bower_components, а не под пакетом, который требует их. Например, файл bower.json для jQuery UI перечисляет зависимость "jquery": ">=1.6". Это означает, что, если jQuery еще не установлен, новейшая версия пакета jQuery будет установлена в /bower_components — при условии, что это, как минимум, версия 1.6.

Обновление или удаление пакетов довольно прямолинейно и включает проверки версии и зависимостей:

# Обновление выполняется на основе правил версий в bower.json,
# например \"jquery\": \"~2.1.3\".
# Это указывает, что для обновления пригоден любой
# пакет версии, скажем, 2.1.4, но 2.2.0 не годится.
bower update jquery
# Команда удалит папку и ссылку в bower.json,
# но сначала запросит, есть ли у других пакетов
# зависимость от jquery
bower uninstall jquery

Файл bower.json

К этому моменту, если бы я удалил папку /bower_components, я не имел бы ни малейшего понятия, какие пакеты были установлены или нужны моему приложению. Если бы я передал исходный код (без пакетов) другому разработчику или перенес его в другую среду, такую как сервер сборки, без папки bower_components, этому разработчику и мне не повезло бы. Если вы знакомы с NuGet, то это аналогично утрате файла packages.config. В идеале, вы захотите включать файл bower.json в свое приложение, чтобы Bower мог отслеживать зависимости и версии вашего пакета, но это не обязательно.

Чтобы создать файл bower.json, выполните команду bower init в корне своего проекта и следуйте запросам, как показано на рис. 4.

Рис. 4. Создание файла bower.json

C:\Users\Adam\Documents\WebApp> bower init
? name: MyWebApp
? version: 1.0.0
? description:
? main file:
? what types of modules does this package expose?
? keywords:
? authors: Adam Tuliper <adam.tuliper@gmail.com>
? license: MIT
? homepage:
? set currently installed components as dependencies? (Y/n) Y
? add commonly ignored files to ignore list? Yes
? would you like to mark this package as private which prevents
  it from being accidentally published to the registry? (y/N)

Если вы забыли создать файл bower.json и устанавливаете набор пакетов в его отсутствие или забыли добавить эти пакеты, используя ключ --save, не волнуйтесь. Когда вы запускаете команду bower init, она запрашивает: «set currently installed components as dependencies?» (установить текущие установленные компоненты как зависимости?). Подтверждение означает, что Bower будет просматривать пакеты в /bower_components и добавлять то, что он определяет как корневые пакеты, в раздел dependencies. Однако, если jQuery UI зависит от jQuery, то jQuery не добавляется как зависимость в ваш файл этим методом, поскольку он был установлен, когда вы установили jQuery UI. Всегда желательно просматривать сгенерированное содержимое раздела dependencies, чтобы быть уверенным в правильности перечисления зависимостей.

Теперь вы можете инициализировать Bower для своего проекта в командной строке, обычно в корневой папке веб-проекта. Затем вы устанавливаете свои зависимости. Пример файла bower.json приведен на рис. 5.

Рис. 5. Пример файла bower.json

{
  "name": "MyWebApp",
  "version": "0.0.0",
  "authors": [
    "Adam Tuliper <adam.tuliper@anonymous>"
  ],
  "license": "MIT",
  "ignore": [
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ],
  "dependencies": {
    "angular": "~1.4.3",
    "bootstrap-css": "~3.3.4",
    "jquery-ui": "~1.11.4"
  },
   "devDependencies": {
    "angular-mocks": "~1.4.3"
  }
}
}

Верхний раздел содержит общую информацию о проекте и пакетах. Другие разделы, о которых стоит упомянуть, — ignore, dependencies и devDependencies. Раздел ignore исключает указанные файлы, если вы вдруг решите создать Bower-пакет из этого приложения. Поскольку я работаю с веб-приложением и не создаю собственный Bower-пакет, для меня это не имеет значения. Разделы dependencies и devDependencies содержат все пакеты, установленные мной и используемые моим приложением; подробнее о них рассказывается в следующем разделе.

Управление зависимостями

Вы видели, что в файле bower.json можно указывать два разных типа зависимостей: dependencies и devDependencies. Элементы в раздел dependencies добавляются, когда вы вызываете, например, команду bower install jquery --save. Это пакеты (вроде jQuery), которые будут выполняться в производственной среде для вашего приложения. С другой стороны, записи в разделе devDependencies — это пакеты, которые обычно не помещаются в производственную среду, такие как инфраструктуры имитации наподобие angular-mocks или компиляторы less/sass. Эти записи добавляются при использовании, скажем, команды bower install angular-mocks --save-dev. Эти два типа зависимостей отмечаются только в файле bower.json и не влияют на то, как вы используете эти файлы в файловой системе в своем приложении. Если бы вы выполняли восстановление пакетов, например, в среде, предназначенной для проверки качества, то теоретически вам не понадобилось бы устанавливать devDependencies.

Чтобы установить все зависимости только в раздел dependencies и игнорировать все, что находится в devDependencies, например при создании производственной сборки, просто введите команду bower install –production.

Если вы хотите увидеть все используемые вашим приложением зависимости, введите bower list и вы получите вывод наподобие следующего:

bower check-new     Checking for new versions
  of the project dependencies..
MyWebApp#0.0.0 C:\Users\Adam\Documents\MyWebApp
├─ angular#1.4.3 (1.4.4-build.4147+sha.bb281f8 available)
├┬ angular-mocks#1.4.3 (1.4.4-build.4147+sha.bb281f8 available)
│└─  angular#1.4.3 (latest is 1.4.4-build.4147+sha.bb281f8)
├─ bootstrap-css#3.3.4
├┬ jquery-ui#1.11.4
│└─ jquery#2.1.4 (3.0.0-alpha1+compat available)
└─ jquery1#2.1.4 (3.0.0-alpha1+compat available)

Слишком много файлов

Новички быстро осознают, что некоторые пакеты содержат много файлов, тогда как им, возможно, нужен лишь один файл. Некоторые Bower-пакеты перечисляют один или более файлов в разделе основной конфигурации, обязательные для использования пакета. Например, для jQuery в пакете содержатся более 90 файлов при установке. Но для работы с jQuery достаточно только jQuery.js, поэтому решение заключается в том, чтобы проверить main и отметить, что для jQuery здесь указан всего один файл в папке /dist:

{
  "name": "jquery",
  "version": "2.1.4",
  "main": "dist/jquery.js",
...
}

Если вы посмотрите в папку /dist для jQuery, то найдете также jQuery.min.js и соответствующий ему файл .map для отладки, хотя указывать их в элементе main нет никакого смысла, поскольку в производственной версии приложения используется либо jQuery.js, либо jQuery.min.js, но не оба файла.

Команда bower list --paths возвращает все файлы из main для каждого установленного пакета, например:

C:\Users\Adam\Documents\MyWeb> bower list --paths
  angular: 'bower_components/angular/angular.js',
  'bootstrap-css': [
    'bower_components/bootstrap-css/css/bootstrap.min.css',
    'bower_components/bootstrap-css/js/bootstrap.min.js'
  ],
  jquery: 'bower_components/jquery/dist/jquery.js'

За перечисление правильных файлов в разделе main отвечают создатели пакетов.

Поскольку по умолчанию все файлы пакета находятся в подпапке внутри /bower_components, вы можете считать, что мои HTML-файлы будут ссылаться на файлы сразу за этой папкой следующим образом:

<link rel="stylesheet" type="text/css"
  href="/bower_components/bootstrap-css/css/bootstrap.min.css">
<script src="/bower_components/bootstrap-css/
  js/bootstrap.min.js" />
<script src="/bower_components/angular/angular.js" />
<script src="/bower_components/jquery/dist/jquery.js" />

Вы можете найти много примеров в Интернете, которые делают это и ссылаются на файлы в папке /bower_components. Это не очень хорошая практика. Не советую так делать и лично я не хочу развертывать в производственной среде такую папку с потенциально сотнями или тысячами файлов, количество которых надо свести к минимуму, а потом объединить в еще меньшее число файлов. Об этих методах я расскажу в следующей статье по Grunt и Gulp, а пока рассмотрим один из нескольких доступных способов более эффективного извлечения этих основных файлов (из раздела main), используя модуль bower-installer.

Модуль bower-installer скопирует все основные файлы в указанную структуру папок. Сначала установите этот модуль глобально на своем компьютере командой npm install –g bower-installer. Затем добавьте в файл bower.json раздел, чтобы указать, куда следует копировать эти файлы:

"install": {
  "path": "lib"
},

В данном случае ваши основные файлы в итоге окажутся в подпапке внутри \lib, например lib\jquery\jQuery.js, lib\angular\angular.js.

Этот процесс можно настраивать — прочитайте документацию на пакеты по ссылке bit.ly/1gwKBmZ.

Наконец, всякий раз, когда вы хотите скопировать свои файлы в выходные папки, запускайте bower-installer. И вновь есть другие способы, в частности применение Grunt или Gulp для копирования этих файлов, используя задачу (вы даже сможете отслеживать изменения в папках), но описанный мной вариант — быстрый способ копирования в том случае, если это не требует других рабочих процессов.

Серверы сборки и контроль версий исходного кода

Как быть, если вы хотите установить нужные пакеты на сервер сборки? Это полезно в сценариях, где вам известно только то, что ваш код находится в системе контроля версий, а все другие пакеты (Bower/NuGet и т. д.) устанавливаются и/или компилируются (с помощью Sass, Less, CoffeeScript и др.) на сервере сборки. В некоторых организациях система контроля версий охватывает все, в том числе все двоичные файлы и сторонние зависимости. В других организациях полагаются на диспетчер пакетов, который восстанавливает пакеты в среде сборки. Передавая кому-либо проект Visual Studio, я, как правило, предполагаю, что пакеты будут восстановлены на его компьютере. Однако для Bower типичная рекомендация в отношении системы контроля версий заключается в том, чтобы включать в нее весь сторонний код, если вы не против потенциально больших репозитариев или если вы не хотите полагаться на диспетчер пакетов, а иначе не включайте /bower_components в эту систему.

Чтобы установить все зависимости и скопировать основные файлы, используя только файл bower.json, просто выполните следующие команды:

# Считываем файл bower.json и устанавливаем пакеты,
# на которые есть ссылки, и их зависимости
bower install

# Не обязательно. Копируем файлы main{} из каждого пакета
# по предопределенному пути
bower-installer

А почему не npm?

Некоторые разработчики просто используют npm, другие выбирают Bower, а третьи — их смесь. Вы найдете многие пакеты в репозитариях пакетов как Bower, так и npm, что в любом случае упростит ваш рабочий процесс. Диспетчер npm хорошо подходит не только для приложений Node.js, но и для управления пакетами на клиентской и серверной сторонах. Он имеет дерево вложенных зависимостей, т. е. для каждого устанавливаемого пакета все его зависимости устанавливаются в подпапку node_components этого пакета. Например, если вы используете три пакета и каждый из них использует jQuery, весь пакет jQuery устанавливается три раза. Вложенные зависимости могут создавать довольно длинную цепочку. Пользователи npm в Windows почти гарантированно в какой-то момент столкнутся с ошибкой из-за ужасающей длины пути вроде C:\Users\Adam\.......\node_modules\somePackageA\node_modules\somePackageB\node_modules\insight\node_modules\inquirer\node_modules\readline2\node_modules\strip-ansi\node_modules\ansi-rege… — слишком длинное имя каталога.

Это побочный эффект из-за структуры вложенных зависимостей в npm, хотя в бета-версии npm 3 поддерживается линейная структура. Но моя цель не состоит в том, чтобы убедить вас пользоваться чем-то одним. Я хочу, чтобы вы понимали, как успешно применять обе утилиты.

Bower, ASP.NET 5 и Visual Studio

В Visual Studio 2015 имеется встроенная поддержка Bower и npm, включая установку пакетов и IntelliSense. Получить ту же функциональность для прежних версий Visual Studio можно, скачав и установив бесплатное расширение Package IntelliSense Extension for Visual Studio. На рис. 6 показаны некоторые варианты, доступные для управления вашими пакетами в файле bower.json.

Варианты управления пакетами в Visual Studio
Рис. 6. Варианты управления пакетами в Visual Studio

Как видно на рис. 7, при вводе имени пакета IntelliSense выдает вам подходящие пакеты и версии, избавляя вас от командной строки или поиска в Интернете. После внесения изменений в файл bower.json и его сохранения пакет будет установлен локально без необходимости перехода в командную строку. С точки зрения управления файлами, здесь нет ничего специфичного для Visual Studio. Иначе говоря, bower.json по умолчанию просто работает из любого веб-проекта.

Bower IntelliSense в Visual Studio
Рис. 7. Bower IntelliSense в Visual Studio

На рис. 8 показаны зависимости из bower.json в Solution Explorer, что демонстрирует, насколько тесно Bower интегрируется с вашими веб-проектами и Visual Studio.

Зависимости Bower в Solution Explorer
Рис. 8. Зависимости Bower в Solution Explorer

В случае ASP.NET 5 структура проекта стала новой; в ней все файлы из вашей папки проекта включаются в проект по умолчанию, но лишь к элементам в папке /wwwroot веб-проекта можно адресоваться как к статическому контенту, например к HTML, CSS, изображениям и JavaScript-файлам. Зная это, вы могли бы задать конфигурацию bower.json такой, чтобы команда bower-installer копировала ваши зависимости в эту папку (хотя «прямо из коробки» шаблоны ASP.NET 5 используют Gulp для копирования заранее заданных файлов в местах их размещения, как я расскажу в следующей статье).

Конечно, в проектах ASP.NET 5 NuGet-пакеты по-прежнему превосходны, активно применяются и поддерживаются. Замечу, что параметры NuGet перенесены в раздел dependencies файла project.json, но в Visual Studio показываются в references. NuGet-пакеты изначально применяются для серверных пакетов, таких как протоколирование и поддержка MVC.

Заключение

Bower — отличный инструмент, который можно легко интегрировать в ваши рабочие процессы на клиентском стороне. Его API прост в использовании, а благодаря интегрированной поддержке в Visual Studio, он может быть задействован непосредственно в сочетании с npm и NuGet для управления пакетами как на клиентской стороне, так и на серверной. Выделите час-другой на освоение Bower; и вы будете рады, что сделали это.


Адам Тьюлипер (Adam Tuliper) — старший идеолог Microsoft, живет в солнечной Калифорнии. Занимается веб-разработкой, созданием игр, является автором на Pluralsight и интересуется разнообразной техникой. С ним можно связаться через Twitter (@AdamTuliper) или по адресу adamt@microsoft.com.

Выражаю благодарность за рецензирование статьи эксперту Microsoft Майклу Палермо (Michael Palermo).