Pull to refresh

Организация js кода для джуниоров

JavaScript *Designing and refactoring *
Sandbox
С недавних пор я стал работать в сфере web разработки, и еще нахожусь в стадии падавана. Однако недавно я открыл для себя способ организации клиентского javascript кода, который может быть легко интегрирован в любой существующий проект и который легко освоить.

Этот подход называют «Модульный javascript», и под катом мы научимся его применять.

image

Статья названа так, потому что люди на уровне джедая уже используют более совершенные методики и думаю в комментариях поделятся ими.

Задачу я ставил себе следующую:
«Организовать весь клиентский js код удобным способом, что бы его было легко поддерживать, искать ошибки и дополнять».

Мотивацией этому стала работа с чужим сайтом, где весь js был в одном, огромном файле и попытка дополнить что-то вызывала приступ апатии.

Вся суть методики сводится к разбиению нашего приложения на модули. Я так же называю их «виджетами», потому что так проще воспринимать их суть.

Каждый виджет является обособленной сущностью. Он не знает о других виджетах и не обращается к ним на прямую. Виджет может работать только сам с собой и генерировать события, на которые могут подписываться другие виджеты.

Схематически виджет — это некая часть нашего сайта, у которой есть специфическая функциональность.

image

Наш тестовый сайт мы можем мысленно разбить на 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.js

var 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.js

var 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.

Спасибо за внимание.
Tags:
Hubs:
Total votes 72: ↑48 and ↓24 +24
Views 70K
Comments Comments 41