Definer.js — простая модульная система

от автора

Пока JavaScript не обзавёлся настоящими модулями мы продолжаем импровизировать.
Так появилась на свет ещё одна реализация модулей — definer.

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

Для хорошего понимания идеи, под катом примеры от простого к сложному.

Возьмём абстрактную страницу товаров интернет-магазина, index.html:

<html> <head>     <meta charset="utf-8"/>     <title>Каталог интернет-магазина</title>     <script src="jquery.js"></script>     <script src="https://rawgithub.com/tenorok/definer/master/definer.js"></script>     <script src="modules/cart.js"></script>     <script src="modules/list.js"></script> </head> <body>     <ul class="list">         <li class="item">             <span class="name">Компьютер</span>, <span class="price">250</span>         </li>         <li class="item">             <span class="name">Телевизор</span>, <span class="price">100</span>         </li>         <li class="item">             <span class="name">Холодильник</span>, <span class="price">300</span>         </li>     </ul> </body> </html> 

Будем использовать jQuery для работы с DOM и definer для модулей.

Модуль Cart, реализующий корзину интернет-магазина с возможностью добавить товар и получить суммарную стоимость добавленных товаров:

definer('Cart', function() {      function Cart() {         this.list = [];     }      Cart.prototype = {         add: function(target) {             var item = $(target);             this.list.push({                 name: item.find('.name').text(),                 price: +item.find('.price').text()             });         },         sum: function() {             return this.list.reduce(function(sum, item) {                 return sum + item.price;             }, 0);         }     };      return Cart;  }); 

Модуль list, зависящий от Cart и реализующий взаимодействие посетителя с каталогом:

definer('list', function(Cart) {      var iCart = new Cart();      $(function() {         $('.item').on('click', function(e) {             iCart.add(e.currentTarget);             console.log(iCart.sum());         });     });  }); 

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

В данном примере, у нас осталась глобальная переменная, которую тоже можно вынести в модуль, это jQuery.

Подключим файл modules/clean.js перед существующими модулями:

definer.clean('$'); 

Теперь переменной $ нет в глобальном контексте:

console.log($); // undefined 

Чтобы продолжать использовать jQuery в модулях, добавим зависимость:

definer('Cart', function($) { ... }); definer('list', function($, Cart) { ... }); 

Сборка

Теперь всё готово и можно собрать исходники в единый файл.

Устанавливаем сборщик модулей:

npm install definer 

Собираем все модули из директории modules в файл index.js:

./node_modules/.bin/definer -d modules/ index.js 

Теперь в index.html достаточно подключить только jQuery и собранный файл:

<script src="jquery.js"></script> <script src="index.js"></script> 

Сборка с помощью grunt-definer

Для удобства разработки есть grunt-плагин. Можно установить мониторинг на изменение файлов модулей и автоматически запускать сборку.

Устанавливаем всё, необходимое для гранта:

npm install grunt grunt-cli grunt-contrib-watch grunt-definer 

Добавим в корень проекта файл Gruntfile.js:

module.exports = function(grunt) {      grunt.initConfig({         watch: {             scripts: {                 files: ['modules/*.js'],                 tasks: ['definer:all']             },         },         definer: {             all: {                 target: 'index.js',                 directory: 'modules/'             }         }     });      grunt.loadNpmTasks('grunt-contrib-watch');     grunt.loadNpmTasks('grunt-definer');  }; 

И запустим мониторинг:

./node_modules/.bin/grunt watch 

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

Сборка сторонних файлов

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

Для этого достаточно добавить опцию clean в grunt-цель:

definer: {     all: {         target: 'index.js',         directory: 'modules/',         clean: {             $: 'jquery.js'         }     } } 

Теперь в index.html достаточно подключить только один собранный файл:

<script src="index.js"></script> 

JSDoc

Возможно формирование JSDoc, содержащего информацию о собранном файле.

Для этого добавим опцию jsdoc в grunt-цель:

jsdoc: {     "file": "Добавление товаров в корзину интернет-магазина",     "copyright": "2014 Artem Kurbatov, tenorok.ru",     "license": "MIT license",     "version": "package.json",     "date": true } 

Возможно указание относительного пути до JSON-файла, из которого сборщик получит значение одноимённого поля.

Положим в корень проекта файл package.json:

{     "version": "0.1.0" } 

Перед модулями в собранном файле появится такой JSDoc:

/*!  * @file Добавление товаров в корзину интернет-магазина  * @copyright 2014 Artem Kurbatov, tenorok.ru  * @license MIT license  * @version 0.1.0  * @date 17 February 2014  */ 

Итого

Definer помогает:

  • разбить всё приложение на модули по зависимостям
  • избавиться от глобальных переменных
  • собирать все скрипты в один файл с помощью grunt-плагина

Документацию по definer и grunt-definer можно найти на гитхабе.

ссылка на оригинал статьи http://habrahabr.ru/post/212817/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *