Эта статья была рецензирована Адрианом Санду , Вилданом Софтиком и Дэном Принсом . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!
Библиотеки: мы используем их постоянно. Библиотека представляет собой упакованный код, который разработчики могут использовать в своих проектах, что неизменно экономит работу и не позволяет изобретать велосипед. Наличие пакетов многократного использования, как с открытым, так и с закрытым исходным кодом, лучше, чем пересоздание той же функции или ручное копирование и вставка из прошлых проектов.
Но кроме упакованного кода, что такое библиотека? За некоторыми исключениями, библиотека всегда должна быть одним файлом или несколькими в одной папке. Его код должен храниться отдельно и должен оставаться как есть при реализации его в вашем проекте. Библиотека должна позволять вам устанавливать конфигурацию и / или поведение для конкретного проекта. Думайте об этом как о USB-устройстве, которое позволяет осуществлять связь только через USB-порт. Некоторые устройства, такие как мыши и клавиатуры, допускают настройку через интерфейс, предоставляемый устройством или с помощью устройства.
В этой статье я объясню, как создаются библиотеки. Хотя большинство рассматриваемых тем будет применяться к другим языкам, эта статья в основном посвящена созданию библиотеки JavaScript.
Зачем создавать свою собственную библиотеку Javascript?
Прежде всего, библиотеки делают повторное использование существующего кода очень удобным. Вам не нужно копать старый проект и копировать некоторые файлы, вы просто извлекаете библиотеку. Это также фрагментирует ваше приложение, уменьшая кодовую базу приложения и облегчая поддержку.
Любой код, который облегчает достижение определенной цели и который может быть использован повторно, например, как абстракция, является кандидатом для включения в библиотеку. Интересным примером является jQuery. Хотя API jQuery значительно больше, чем упрощенный DOM API, он много значил несколько лет назад, когда кросс-браузерная манипуляция с DOM была довольно сложной.
Если проект с открытым исходным кодом становится популярным и его используют все больше разработчиков, вполне вероятно, что люди присоединятся к нему и будут помогать с этим проектом, сообщая о проблемах или внося свой вклад в базу кода. В любом случае это пойдет на пользу библиотеке и всем проектам в зависимости от нее.
Популярный проект с открытым исходным кодом также может привести к большим возможностям. Компания может быть впечатлена качеством вашей работы и предложит вам работу. Возможно, компания попросит вас помочь интегрировать ваш проект в их приложение. В конце концов, никто не знает вашу библиотеку лучше, чем вы.
Для многих это просто хобби — наслаждаться написанием кода, помогать другим, учиться и расти в процессе. Вы можете расширить свои возможности и попробовать новые вещи.
Сфера и цели
Перед написанием первой строки кода должно быть ясно, какова цель вашей библиотеки — вы должны установить цели. С их помощью вы можете сосредоточиться на том, какую проблему вы хотите решить с помощью своей библиотеки. Имейте в виду, что ваша библиотека должна быть проще в использовании и запоминании, чем проблема в ее сыром виде. Чем проще API, тем легче пользователям научиться пользоваться вашей библиотекой. Процитирую философию Unix:
Делай одно и делай хорошо
Задайте себе вопрос: какую проблему решает ваша библиотека? Как вы собираетесь решить это? Ты сам все напишешь или можешь использовать чужую библиотеку?
Независимо от размера библиотеки, попробуйте составить план. Перечислите все функции, которые вам нужны, затем отбросьте столько, сколько сможете, пока у вас не появится маленькая, но функциональная библиотека, похожая на минимально жизнеспособный продукт . Это будет ваш первый релиз. Оттуда вы можете создавать вехи для каждой новой функции. По сути, вы разбиваете свой проект на куски размером с кусочек, делая каждую функцию более совершенной и более приятной. Поверьте мне, это будет держать вас в здравом уме.
API дизайн
Лично мне действительно нравится подходить к моей библиотеке с точки зрения конечного пользователя. Вы могли бы назвать это ориентированным на пользователя дизайном. По сути, вы создаете схему своей библиотеки, надеясь уделить ей больше внимания и сделать ее более удобной для любого, кто захочет ее использовать. В то же время вы можете подумать о том, какие аспекты следует настраивать, что будет обсуждаться далее в этой статье.
Окончательный тест качества API состоит в том, чтобы есть свою собачью еду, использовать свою библиотеку в своих собственных проектах. Попробуйте заменить код приложения своей библиотекой и посмотреть, охватывает ли он все функции, которые вы желаете. Постарайтесь сохранить библиотеку настолько простой, насколько это возможно, при этом сохраняя ее достаточно гибкой, чтобы она работала и для их пограничных случаев, путем настройки (как описано далее в этой статье).
Вот пример того, как может выглядеть реализация или схема строковой библиотеки User-Agent:
// Start with empty UserAgent string var userAgent = new UserAgent; // Create and add first product: EvilCorpBrowser/1.2 (X11; Linux; en-us) var application = new UserAgent.Product('EvilCorpBrowser', '1.2'); application.setComment('X11', 'Linux', 'en-us'); userAgent.addProduct(application); // Create and add second product: Blink/20420101 var engine = new UserAgent.Product('Blink', '20420101'); userAgent.addProduct(engine); // EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101 userAgent.toString(); // Make some more changes to engine product engine.setComment('Hello World'); // EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101 (Hello World) userAgent.toString();
В зависимости от сложности вашей библиотеки, вы также можете подумать о структурировании. Использование шаблонов проектирования — отличный способ структурировать вашу библиотеку или даже преодолеть некоторые технические проблемы. Это также снижает риск рефакторинга крупных деталей при добавлении новых функций.
Гибкость и настройка
То, что делает библиотеки великолепными, — это гибкость, но также трудно провести грань между тем, что вы можете, и тем, что вы не можете настроить. Прекрасным примером этого является chart.js против D3.js. Оба являются отличными библиотеками для визуализации данных. Chart.js позволяет легко создавать и стилизовать различные типы встроенных диаграмм. Но если вам нужен больший контроль над графикой, D3.js — это то, что вам нужно.
Существуют различные способы управления пользователем: настройка, предоставление открытых методов, а также обратные вызовы и события.
Настройка библиотеки часто выполняется во время инициализации, но некоторые библиотеки позволяют изменять параметры во время выполнения. Параметры часто ограничены крошечными частями, и их изменение не должно делать ничего, кроме обновления этих значений для последующего использования.
// Configure at initialization var userAgent = new UserAgent({ commentSeparator: ';' }); // Run-time configuration using a public method userAgent.setOption('commentSeparator', '-'); // Run-time configuration using a public property userAgent.commentSeparator = '-';
Методы могут быть доступны для взаимодействия с экземпляром, например, для извлечения данных из экземпляра (получателей), для помещения данных в экземпляр (установщиков) и для выполнения действий.
var userAgent = new UserAgent; // A getter to retrieve comments from all products userAgent.getComments(); // An action to shuffle the order of all products userAgent.shuffleProducts();
Обратные вызовы иногда передаются с помощью открытых методов, часто для запуска пользовательского кода после асинхронной задачи.
var userAgent = new UserAgent; userAgent.doAsyncThing(function asyncThingDone() { // Run code after async thing is done });
События имеют большой потенциал. Они похожи на обратные вызовы, за исключением того, что добавление обработчиков событий не должно вызывать действия. События часто используются для обозначения, как вы уже догадались, событий! Подобно обратному вызову, вы можете предоставить дополнительную информацию и вернуть значение для библиотеки для работы.
var userAgent = new UserAgent; // Validate a product on addition userAgent.on('product.add', function onProductAdd(e, product) { var shouldAddProduct = product.toString().length < 5; // Tell the library to add the product or not return shouldAddProduct; });
В некоторых случаях вы можете разрешить пользователям расширять вашу библиотеку. Для этого вы можете предоставить открытый метод или свойство, которое могут заполнять пользователи, во многом как Angular модули ( angular.module('myModule')
) и jQuery’s fn
( jQuery.fn.myPlugin
), или ничего не делать и просто позволить пользователям получать доступ к вашей библиотеке. Пространство имен:
// AngryUserAgent module // Has access to UserAgent namespace (function AngryUserAgent(UserAgent) { // Create new method .toAngryString() UserAgent.prototype.toAngryString = function() { return this.toString().toUpperCase(); }; })(UserAgent); // Application code var userAgent = new UserAgent; // ... // EVILCORPBROWSER/1.2 (X11; LINUX; EN-US) BLINK/20420101 userAgent.toAngryString();
Точно так же это позволяет вам перезаписывать методы.
// AngryUserAgent module (function AngryUserAgent(UserAgent) { // Store old .toString() method for later use var _toString = UserAgent.prototype.toString; // Overwrite .toString() UserAgent.prototype.toString = function() { return _toString.call(this).toUpperCase(); }; })(UserAgent); var userAgent = new UserAgent; // ... // EVILCORPBROWSER/1.2 (X11; LINUX; EN-US) BLINK/20420101 userAgent.toString();
В последнем случае, предоставляя пользователям доступ к пространству имен вашей библиотеки, вы получаете меньше контроля над тем, как определяются расширения / плагины. Чтобы убедиться, что расширения следуют некоторому соглашению, вы можете (и должны) написать документацию.
тестирование
Написание набросков — отличное начало для разработки на основе тестов . Короче говоря, это когда вы записываете критерии в форме тестов, прежде чем писать реальную библиотеку. Если эти тесты проверяют, ведет себя ли функция так, как она должна, и вы пишете ее перед написанием своей библиотеки, стратегия называется поведенческой разработкой . В любом случае, если ваши тесты охватывают все функции вашей библиотеки и ваш код проходит все тесты, вы можете смело предполагать, что ваша библиотека работает.
Яни Хартикайнен объясняет, как вы можете написать модульные тесты с Mocha в модульном тестировании вашего JavaScript, используя Mocha и Chai . В разделе «Тестирование JavaScript с помощью Jasmine, Travis и Karma» Тим Эвко показывает, как настроить удобный конвейер тестирования с другой средой под названием Jasmine. Эти две среды тестирования очень популярны, но их гораздо больше во многих вариантах.
В моем наброске, созданном ранее в этой статье, уже были комментарии относительно ожидаемого результата. Здесь все тесты начинаются: с ожиданием. Тест Жасмин для моей библиотеки будет выглядеть так:
describe('Basic usage', function () { it('should generate a single product', function () { // Create a single product var product = new UserAgent.Product('EvilCorpBrowser', '1.2'); product.setComment('X11', 'Linux', 'en-us'); expect(product.toString()) .toBe('EvilCorpBrowser/1.2 (X11; Linux; en-us)'); }); it('should combine several products', function () { var userAgent = new UserAgent; // Create and add first product var application = new UserAgent.Product('EvilCorpBrowser', '1.2'); application.setComment('X11', 'Linux', 'en-us'); userAgent.addProduct(application); // Create and add second product var engine = new UserAgent.Product('Blink', '20420101'); userAgent.addProduct(engine); expect(userAgent.toString()) .toBe('EvilCorpBrowser/1.2 (X11; Linux; en-us) Blink/20420101'); }); it('should update products correctly', function () { var userAgent = new UserAgent; // Create and add first product var application = new UserAgent.Product('EvilCorpBrowser', '1.2'); application.setComment('X11', 'Linux', 'en-us'); userAgent.addProduct(application); // Update first product application.setComment('X11', 'Linux', 'nl-nl'); expect(userAgent.toString()) .toBe('EvilCorpBrowser/1.2 (X11; Linux; nl-nl)'); }); });
Как только вы полностью удовлетворитесь дизайном API для своей первой версии, пришло время задуматься об архитектуре и о том, как будет использоваться ваша библиотека.
Совместимость загрузчика модулей
Вы можете использовать или не использовать загрузчик модулей. Однако разработчик, который решит реализовать вашу библиотеку, может, поэтому вы захотите сделать вашу библиотеку совместимой с загрузчиками модулей. Но какой? Как вы можете выбирать между CommonJS, RequireJS, AMD и другими?
На самом деле, вам не нужно! Universal Module Definition (UMD) — это еще одна стратегия, направленная на поддержку нескольких загрузчиков модулей. Вы можете найти различные варианты фрагментов в Интернете, но вы также можете найти варианты в репозитории UMD GitHub, чтобы сделать вашу библиотеку UMD-совместимой. Запустите вашу библиотеку, используя один из шаблонов, или добавьте UMD с вашим любимым инструментом сборки , и вам не придется беспокоиться о загрузчиках модулей.
Если вы хотите использовать синтаксис import
/ export
ES2015, я настоятельно рекомендую использовать Babel для компиляции в ES5 в сочетании с плагином UMD Babel . Таким образом, вы можете использовать ES2015 в своем проекте, при этом создавая библиотеку, подходящую для всех.
Документация
Я все за тщательную документацию для всех проектов, но это часто считается большой работой, отложенной и в конечном итоге забытой.
Основная информация
Документация всегда должна начинаться с базовой информации, такой как название проекта и описание. Это поможет другим понять, что делает ваша библиотека и является ли это хорошим выбором для них.
Вы можете предоставить дополнительную информацию, такую как сфера охвата и цели, чтобы лучше информировать пользователей, и дорожную карту, чтобы они знали, чего ожидать в будущем или знали, как они могут внести свой вклад.
API, учебные пособия и примеры
Конечно, вам нужно, чтобы пользователи знали, как использовать вашу библиотеку. Это начинается с документации API. Учебные пособия и примеры делают отличные дополнения, но написание их может быть большой работой. Встроенной документации , однако, нет. Это комментарии, которые можно проанализировать и преобразовать в страницы документации с помощью JSDoc .
Мета-задачи
Некоторые пользователи могут захотеть внести изменения в вашу библиотеку. В большинстве случаев это будет полезно, но некоторые могут захотеть создать собственную сборку для частного использования. Для этих пользователей полезно включать документацию для мета-задач, например, список команд для создания библиотеки, запуска тестов, генерации, преобразования или загрузки данных и т. Д.
Вклад
Когда вы открываете исходный код своей библиотеки, вклады велики. Чтобы направлять участников, вы можете добавить документацию, в которой вы объясните шаги для внесения вклада и критерии, которым он должен соответствовать. Вам будет легче просматривать и принимать материалы, а также делать это правильно.
Лицензия
И последнее, но не менее важное: включите лицензию. Технически, если вы решите не включать его, он все равно будет защищен авторским правом, но не все это знают.
Я считаю ChooseALicense.com отличным ресурсом для выбора лицензии без необходимости быть юристом. После выбора лицензии просто сохраните текст в файле LICENSE.txt
в корне вашего проекта.
Заверните это и добавьте лук
Создание версий важно для хорошей библиотеки. Если вы когда-нибудь решите внести критические изменения, пользователь, вероятно, захочет продолжать использовать версию, которая ему подходит.
Текущим стандартом де-факто для именования версий является Semantic Versioning или SemVer. Версии SemVer состоят из трех чисел, каждое из которых указывает на разные изменения: мажор, минор и патч.
Добавление версий / выпусков в ваш репозиторий Git
Если у вас есть git-репозиторий, вы можете добавить номера версий в ваш репозиторий. Вы могли бы рассмотреть их снимки вашего хранилища. Теги , мы их называем. Чтобы создать тег, откройте терминал и введите:
# git tag -a [version] -m [version message] git tag -a v1.2.0 -m "Awesome Library v1.2.0"
Многие сервисы, такие как GitHub, предоставят обзор всех ваших версий и ссылки для скачивания для каждой из них.
Публикация в общих репозиториях
НПМ
Многие языки программирования поставляются с менеджером пакетов или имеют сторонний менеджер пакетов. Это позволяет нам использовать библиотеки специально для этих языков. Примерами являются PHP Composer и RubyGems для Ruby.
Node.js, своего рода автономный движок JavaScript, поставляется с npm . Если вы не знакомы с npm, у нас есть отличное руководство для начинающих .
По умолчанию ваш пакет npm будет опубликован публично. Не бойся! Вы также можете публиковать частные пакеты , настраивать частный реестр или вообще избегать публикации .
Чтобы опубликовать ваш пакет, вашему проекту понадобится файл package.json
. Вы можете сделать это вручную или использовать интерактивный мастер. Чтобы запустить мастер, введите:
npm init
Свойство version
должно соответствовать вашему тегу git. Также убедитесь, что у вас есть файл README.md
. Как и GitHub, npm использует это для страницы, представляющей ваш пакет.
После этого вы можете опубликовать свой пакет, набрав:
npm publish
Это оно! Вы опубликовали свой пакет npm.
Беседка
Несколько лет назад появился еще один менеджер пакетов по имени Bower. Этот менеджер пакетов, однако, предназначен не для конкретного языка, а для конкретной платформы — Интернета. Вы можете найти все основные входные активы прямо здесь. Публикация вашего пакета на Bower интересна, только если ваша библиотека совместима с браузером.
Если вы не знакомы с Бауэром, у нас также есть руководство для начинающих .
Как и в случае с npm, вы также можете создать частный репозиторий . Вы также можете запретить его полную публикацию в мастере.
Интересно, что в течение прошедшего года или двух многие люди, кажется, переходят на npm для внешних активов. Хотя пакеты npm — это в основном JavaScript, многие внешние пакеты также публикуются на npm. В любом случае, Bower по-прежнему популярен, поэтому я определенно рекомендую опубликовать ваш пакет на Bower.
Я упоминал, что Bower на самом деле является модулем npm, и изначально был вдохновлен им? Команды действительно похожи. Чтобы создать файл bower.json
, введите:
bower init
Как и в npm init
, инструкции не требуют пояснений. Наконец, чтобы опубликовать ваш пакет:
bower register awesomelib https://github.com/you/awesomelib
Точно так же вы поместили свою библиотеку в дикую природу, чтобы каждый мог использовать ее в своих проектах Node и / или в Интернете!
Вывод
Основным продуктом является библиотека. Убедитесь, что она решает проблему, проста в использовании и стабильна, и вы порадуете свою команду или многих разработчиков.
Многие из упомянутых мной задач легко автоматизируются, например: запуск тестов, создание тега, обновление вашей версии в package.json
и package.json
публикация вашего пакета в npm и bower. Здесь вы вступаете в сферу непрерывной интеграции и используете такие инструменты, как Travis CI или Jenkins. Об этом упоминается в статье Тима Эвко, о которой я упоминал ранее.
Вы построили и опубликовали библиотеку? Пожалуйста, поделитесь в разделе комментариев ниже!