[Из песочницы] Организация js кода для джуниоров
С недавних пор я стал работать в сфере web разработки, и еще нахожусь в стадии падавана. Однако недавно я открыл для себя способ организации клиентского javascript кода, который может быть легко интегрирован в любой существующий проект и который легко освоить. Этот подход называют «Модульный javascript», и под катом мы научимся его применять. Статья названа так, потому что люди на уровне джедая уже используют более совершенные методики и думаю в комментариях поделятся ими. Задачу я ставил себе следующую: «Организовать весь клиентский js код удобным способом, что бы его было легко поддерживать, искать ошибки и дополнять». Мотивацией этому стала работа с чужим сайтом, где весь js был в одном, огромном файле и попытка дополнить что-то вызывала приступ апатии. Вся суть методики сводится к разбиению нашего приложения на модули. Я так же называю их «виджетами», потому что так проще воспринимать их суть. Каждый виджет является обособленной сущностью. Он не знает о других виджетах и не обращается к ним на прямую. Виджет может работать только сам с собой и генерировать события, на которые могут подписываться другие виджеты. Схематически виджет — это некая часть нашего сайта, у которой есть специфическая функциональность. Наш тестовый сайт мы можем мысленно разбить на 3 виджета. 1. Глобальный модуль — будет отвечать за инициализацию других модулей. 2. Профиль — отобразит аватарку пользователя (привет Ричард :)), и меню с направлениями деятельности. 3. Портфолио — отобразит примеры работ по выбранному направлению у этого юзера А теперь создадим наши модули. Каждый модуль будет находится в отдельном js файле. Html разметку и CSS стили мы рассматривать не будем. Отмечу лишь, что для отображения я обычно использую шаблонизатор входящий в состав underscore.js. А стили, в основном, используются из основного css файла. Глобаный модуль App.js// Модуль представляет из себя переменную, которой присвоено значение самовызывающейся анонимной функции // Функция возвращает объект, предоставляющий публичный API для работы с модулем
var App = (function (){ //Тут можно определить приватные переменные и методы //Например var someArray = []; //Не будет доступен по ссылке App.someArray, не как либо еще вне объекта
//Объект, содержащий публичное API return { init: function (){ // Инициализация модуля. В ней мы инициализируем все остальные модули на странице Profile.init (); Portfolio.init (); } } })();
//И инициализируем наш глобальный модуль App.init (); Модуль профиля Profile.jsvar Profile = (function (){ //Приватная переменная хранящая путь до сервера, предоставляющего информацию для модуля var url = 'http://someweb.com'; //Приватная переменная хранящая корневой html элемент, в котором отрисовывается модуль var el = '.div-profile';
return { //Инициализация модуля init: function (){ // Получим список пунктов меню и аватарку с сервера var profileData = this.getData (url); }, getData: function (url){ /* * Тут будет код ajax запроса на сервер, который в случае успеха сохранит результат в переменную res */
//Отрисуем наши данные this.render (res); }, render: function (){ /* * Тут будет код создания html разметки, с использованием вашего любимого шаблонизатора. * Допустим результирующая строка будет сохранена в переменную html */
//Добавим полученную разметку в корневой элемент модуля. //Для простоты представим что на проекте используется jQuery $(el).html (html);
//И привяжем DOM события к нужным элементам модуля this.event (); }, event: function (){ //Пусть пункты меню имеют класс .menu-item //И содержат атрибут data-list-id $('.menu-item').click (function (){ var id = $(this).data ('list-id');
//Теперь самое важное. Генерируем событие, что пользователь кликнул пункт. //На это событие и будут подписываться другие модули //В триггере передадим id выбранного пункта $(window).trigger ('clickItem', {id: id}); }); } } })(); Модуль портфолио Portfolio.jsvar Portfolio = (function (){
//Ссылка на текущий объект $this = this; var el = '.portfolio'
return { init: function (){
//Повесим слушатель нашего кастомного события. В функцию обработчик передадим пришедшие данные $(window).on ('clickItem', function (e, data){ $this.getData (data.id) }); }, getData: function (id){ /* * Тут сделаем запрос на сервер и получим наши работы в портфолио. Пусть они так же сохраняются в res */ this.render (res); }, render: function (data){ /* * И снова отрисовываем данные удобным вам способом */ }, event: function (){ /* * Навесим нужные события */ } } }); Что это нам даетКод разделен по файлам. Легко найти нужное место для изменения Модули общаются по средствам событий. Можно удалять или заменять модули другими, не трогая код остальных частей приложения Процесс внесения новых фич стал более удобным Например мы захотим добавить новый модуль, который что то делает после того, как пользователь выбрал пункт в профиле. Нам достаточно подписать этот модуль на событие 'clickItem' и выполнить нужные действия. Мы хотим добавить всплывающее окно, появляющееся при клике на работе в портфолио? Не вопрос. В методе event модуля Portfolio добавим нечто вроде//'.portfolio-item' — класс-обертка, для каждой работы $('.portfolio-item').click (function (){ $(window).trigger ('showModal'); }); Теперь нам нужно подписать модуль, генерирующий всплывающие окна, по всему нашему приложению — на событие 'showModal' и все. Надеюсь этот материал будет вам полезен. По теме так же советую почитать largescalejs.ru. Для загрузки файлов с модулями я использую yepnope.js. Спасибо за внимание.