Наш вариант подхода к написанию JS приложений. Загрузка скриптов и проксирование

в 18:03, , рубрики: javascript, web-разработка, Веб-разработка, ооп, ооп js, Проектирование и рефакторинг, метки: , , , ,

Приветствую Всех!
В прошлый раз, то ли по неопытности, то ли ещё по какой причине — выложив статью я не смог отстоять своё мнение по поводу «необходимости» написания своего «велосипеда» ( как некоторые это назвали ) для масштабируемых JS приложений. На это положило отпечаток то что проект находится в разработке, и некоторые скрипты выложить я ну никак не мог.
Но сегодня я всё-же хочу постараться переубедить всех противников такого «велосипедостроения» в том что это действительно было необходимо, и покажу конкретные примеры, и с конкретными же исходниками.

Наш вариант подхода к написанию JS приложений. Загрузка скриптов и проксирование

Вступление

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

Проект является достаточно большим, и конкретно до меня писался 3-4 программистами, и у каждого из них было своё видение по структуре и общим правилам кода.
И именно кем-то из них была придумана такая структура какая она есть, но как мне кажется разделение проекта на составные части ( директории и области видимости ) является хорошей, и даже полезной практикой:

  • разделение директорий и файлов позволяет лучше вникать в ООП и разделять логику и интерфейс.
  • разделение областей видимости для каждого из компонентов — даёт возможность избежать пересечения интересов.

Исходя из этого, и того что проект достаточно специфичен у нас имеется 2 ключевых скрипта которые представляют из себя «ядро» сайта:

  • boot.js — который занимается загрузкой скриптов ( эдакий аналог RequireJS ) и прочими функциями при загрузке ( инициализация, создание классов, наследование и т.д. )
  • processor.js — который занимается проксированием всех функций в наших областях видимости.

boot.js

Основная задача данного скрипта — это (под)загрузка модулей из 3х системных директорий:

  • libraries
  • modules
  • addons

Загрузка происходит путём обращения к файлу *.boot.js в одноимённой директории, а вызывается вот так:

/* Библиотеки */
boot.libraries( [ 'area', 'window' ], function( ) 
{
	/* Модули */
	boot.modules( [ 'account', 'settings' ], function( ) 
	{ 
		/* Аддоны */
		boot.addons( [ 'debugUtils', 'vFile', 'vAudio', 'vVideo' ], function( )
		{			
			// Проксируем области видимости core и interface
			processor.proxy( core, 'core' );
			processor.proxy( interface, 'interface' );
		} );
	} );		
} );

Исходя из этого загрузка будет выглядеть так ( пример на библиотеке «window» ):

  • скрипт загружает файл по адресу /libraries/window/window.boot.js
  • в нём в свою очередь прописаны другие скрипты которые находятся в этой-же папке:
    // Загружаем файл window.style.css
    boot.css( { file: 'window.style' } );
    // Загружаем файл windows.core.js и вызываем функцию core.windows.initialize
    boot.js( { file: 'windows.core', initialize: [ 'core.windows' ] } );
    // Загружаем файл Window.interface.js, но перед этим производим загрузку библиотеки area если она ещё не загружена
    boot.js( { file: 'Window.interface', require: [ 'area' ] } );
    

  • загружаются файлы из *.boot.js исходя из прописанных параметров.

boot.js и «классы»

Кроме загрузки модулей, boot.js берёт на себя так же функции по созданию правильных ( по нашему мнению ) классов и их наследовании.

var Area =
boot.createClass( interface, 'Area', function( )
{
	/** Создаём класс Area в области видимости interface **/
} );

Area.prototype.showOrHide = function( state )
{
	 /** Какой-то код **/

	return this;
};

var Window =
boot.createClass( interface, 'Window', function( )
{
	/** Создаём класс Window в области видимости interface и наследуемся от interface.Area **/
},
interface.Area );

Window.prototype.showOrHide = function( state )
{
	var newState = !state;
	
	this.parentFunction( 'showOrHide' )( newState ); // Вызываем функцию родителя

	return this;
};

var Area = new interface.Area( );
var Window = new interface.Window( );

processor.js

Данный скрипт позволяет нам использовать событийную модель, и вмешиваться в работу другого модуля — так что-бы он о нас не знал.
Данный функционал очень полезен, так как даёт возможность писать модули и аддоны которые в случае своего отключения — ничего не ломают.

У процессора есть 5 функций которые очень схожи с аналогичными в JQuery:

  • proxy — проксирование заданного объекта ( посмотреть можно в примере работы boot.js )
  • signal — произвести сигнал на который можно прикрепить обработчик ( некий аналог $.trigger( ) )
  • bind — прикрепить обработчик на вызов функции или сигнал
  • unbind — открепить обработчик
  • one — прикрепить обработчик который сработает только один раз

Вот небольшой пример использование таких вот «обработчиков».
Допустим мы вызываем функцию interface.Window.showOrHide( true );, из другого модуля мы можем перехватить её вот так:

// Цепляемся на начало выполнения функции 'interface.Window.showOrHide' - из аддона.
processor.bind( 'interface.Window.showOrHide', function( sender, params ) 
{ 
	console.log( 'Значение в начале выполнения функции: ' + params[0] );
	params[0] = false; // Меняем входящее значение на необходимое нам
} );

// Цепляемся на конец выполнения функции 'interface.Window.showOrHide' - из аддона.
processor.bind( 'post-interface.Window.showOrHide', function( sender, params ) 
{ 
	console.log( 'Значение в конце выполнения функции: ' + params[0] );
} );

И таким образом непосредственно в interface.Window.showOrHide теперь «прилетит» false а не true как изначально планировалось.

Или вот так можно совсем прервать выполнение функции:

processor.bind( 'interface.Window.showOrHide', function( sender, params ) 
{ 
	return false;
} );

Итог

Надеюсь в этот раз у меня лучше вышло показать плюсы вот такого «велосипеда».
Ну а посмотреть и скачать исходники ( с небольшими примерами ) вышеуказанных библиотек можно здесь:

Конечно скрипты далеко не идеальны, и есть идеи по улучшению и оптимизациям ( в частности по работе с классами) но это немного позже.

Автор: ange007

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js