Подключаем JavaScript правильно

JavaScript очень просто подключать к странице до тех пор, пока он не генерируется динамически. Как только серверная часть приложения начинает собирать для нас JS "на лету" возникают сложности.

 

 

Казалось бы, чего проще: <script src="http://my-domain.com/my-script.js type="text/javascript"> и все. Да, это работает, но только если скрипт заранее написан и лежит себе спокойно файликом в папочке. Далеко не всегда есть возможность сразу написать этот скрипт, приходится учитывать массу факторов или "на лету" формировать на сервере и передавать наборы объектов уже подключенным к странице скриптам. Что делает обычно программист в этом случае? Есть 3 способа:

  • Вставка кода непосредственно  в страницу
  • Динамическое создание JavaScript на сервере
  • Статичный скрипт и AJAX+JSON для динамического изменения пользовательских данных на клиенте

Вставка кода

Это посто <script type="text/javascript>..... Ваш замечательный код... </script> но это не наш метод. У него масса недостатков:

  • "Замусоривание" HTML-кода. 
  • Код намного тяжелее отлаживать в браузере
  • На такой код смотрят неодобрительно СЕО-специалисты
  • Код выводится при генерации страницы, а дальше все сводится к такой же унылой статике, как и <script src="/">
  • Утяжеляет страницу. Чем больше возможностей Ваше приложение предоставлет пользователю, тем больше объем таких вставок. При этом не факт, что пользователь будет использовать прямо сейчас на этой странице хотя бы половину этих возможностей. В результает, когда проект растет, приходится просто вводить новые страницы. А если нам надо осуществить связь между скриптом на одной странице и скриптом на другой? Две вкладки? Фреймы? Переделка проекта? Оно Вам надо?
  • Необходимо точно отслеживать последовательность вставки скриптов в страницу, иначе произойдет неприятность: скрипт, который включен в начале кода, начинает использовать объекты и переменные, которых просто еще нет, так как они определены в последующих частях кода. Это вызывает необходимость более тщательно простраивать алгоритмы взаимодействия блоков проекта и переходить к процедурной парадигме программирования сильную головную боль.

Поэтому прямую вставку кода рассматривать не будем. Хватит, насмотрелись.

Как же все-таки правильно?

Правильный алгоритм работы в общем случае таков:

  1. Подключаем в теге <head> необходимые библиотеки. Куда сейчас без них.
  2. Подключаем статичные скрипты.
  3. По мере необходимости динамически или загружаем с сервера и подключаем к странице полноценные скрипты или получаем с того же сервера (или другого, Виват, JSONP! ) JSON-кодированные данные и обрабатываем их при помощи уже подключенных ранее статичных скриптов.

Динамическое создание JavaScript на сервере.

М-ммм... «Программа, пишущая другие программы»... «Источник»... Определенно «Matrix has you»

Д. Котеров.

Отличный подход. Если нам не нужно писать ОЧЕНЬ высоконагруженные проекты.

Плюсы:

  • В любой момент любой нужный функционал без перезагрузки страницы
  • Простота расширения функционала. Ваш проект легко, быстро и просто масштабируется. один новый .js файл и один новый .php и не надо переписывать весь проеки целиком.
  • Скрипты подключаются к странице тогда, когда это нужно пользователю, а не в момент создания страницы на сервере. Изначально страница получается максимально легкой.
  • Просто отлаживать код. В отладчике отображается только Ваш скрипт, не нужно выискивать его среди текста, HTML и других скриптов.
  • Нет мусора в HTML
  • Гораздо легче оптимизировать страницу под поисковики при необходимости. Нет лишнего текста, снижающего вес ключевых слов в контенте. (так говорят СЕО-шники, великие и ужасные).
  • Ваш скрипт гарантированно будет загружен тогда, когда все необходимые для него переменные, функции и классы уже созданы, инициированы и готовы к работе.

Минусы:

  • Сложные скрипты тяжело писать таким способом. Приходится смешивать одновременно два языка: серверный (например, PHP) и JavaScript. Нет того, что можно было бы назвать отделением бизнес-логики от модели данных.
  • Не самый оптимальный способ с точки зрения нагрузки на канал. Она, конечно, занчительно ниже, чем если бы мы передавали всякий раз всю страницу целиком, но есть способ лучше - JSON.

Минусы довольно условные, но они  есть и я должен был о них сказать.

Как организовать такое чудо?

 

Пишем серверную часть:

создадим файл dynamic-js.php

<?php
       header("Content-type:application/x-javascript");
       $param1=$_GET('param1');
       $param2=$_GET('param2');
?>
alert ('Это подгруженный скрипт\nПереданы параметры\nПараметр № 1: <?php echo $param1; ?>\nПараметр № 2: <?php echo $param2; ?>');
 

 

В первой строке скрипта сразу пишем header, чтобы не забыть. Это гарантированно заставит браузер воспринимать пришедший текст как JavaScript и никак иначе.

 

Дальше вытаскиваем два параметра из массива $_GET и выводим простой  alert.

 

Теперь нам надо подключить этот скрипт к HTML-странице

 

Самый простой способ - в тег <head></head> вставить уже знакомую конструкцию <script type="javascript" src="http://my-domain.com/dynamic-js.php?param1=x&param2=y"> Можно сказать, что это промежуточный вариант между статичной и динамичной подгрузкой скриптов. Чем отличается от статичного? Тем, что генерация JavaScript может быть прозрачно вынесена в отдельный файл dynamic-js.php  и нет мусора в HTML-коде. В остальном - та же статика.

 

 Для того, чтобы сделать настоящую динамику в подключении скриптов в MooTools есть механизм Assets

Пишем клиентскую часть

Создадим файл 'static-js.js':

 
window.addEvent('onload',function(){ 
$('myA').addEvent('click',function(){//По клику на ссылке 
    var myScript = Asset.javascript( //Подключим динамический JavaScript 
        'dynamic-js.php?param1=x&param2=y', 
        { 
                id: 'myScript', 
                onLoad: function(){ 
                    console.log('Dynamic script is loaded!'); //Запишем в консоль, что событие состоялось 
                } 
        } 
    ) 
    return false;//Вернем false, чтобы исключить переход по ссылке 
}); 
}); 
 

 

Создадим файл 'myPage.html':

 <html>
          <head>
                  <title>Cтраница с динамически подгружаемым скриптом</title>
                  <script type="text/javascript" src="mootools-core.js">
                  <script type="text/javascript" src="mootools-more.js">
                  <script type="text/javascript" src="static-js.js">
          </head>
          <body>
                  <a href="nojs.html"  id="myA">кликни для подключения дополнительного скрипта</a>
          <body>
</html>

 

Вот и все. Теперь по клику на ссылке сначала загрузится динамически сгенерированный скрипт, затем в консоль выведется информационная строка, а сразу после этого отработает alert в нашем скрипте. Если на ссылку никто не кликнет, то значит, наш скрипт был в настоящий момент вообще никому на данной странице не нужен. Просто и очень гибко. Кстати, в некоторых случаях позволяет существенно снизить нагрузку на сервер и БД, как любой AJAX.

Статичный скрипт и AJAX+JSON для динамического изменения пользовательских данных на клиенте

Подобрались к самой вкусной части. В принципе, мы уже использовали этот подход в последнем примере. У нас есть и статичный скрипт, и динамический. И все работает по нужному нам событию. Что еще можно улучшить?

 

Здесь возможно несколько подходов:

 

В статичном скрипте мы прописываем не только обработчики событий, но и все элементы бизнес-логики: классы, функции и прочее, а динамический скрипт перестает отдавать нам JavaScript, вместо этого он становится полнофункциональной моделью, принимает и обрабатывает данные, работает с базой данных, а результат работы возвращает в JSON-кодированном виде. После чего на основе JSON - данных наш статичный скрипт формирует HTML-вывод. Стоп! Где-то это мы уже видели... Правильно, это классический AJAX. Так тоже можно.

 

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

 

Алгоритм:

  1. Подгружается статический скрипт с обработчиками пользовательских событий. Он небольшой по сравнению со всей бизнес-логикой, которую мы хотели бы реализовать.
  2. При возникновении события подгружается нужный нам скрипт, причем он может быть как статическим,так и динамическим, но лучше - статика, работает быстрее, да и код получается более читабельным. Избавляемся от первого недостатка способа, описанного выше.
  3. После подгрузки скрипта (событие onLoad) в дело вступает AJAX, получаем данные от нашей модели и обрабатываем свежеподгруженным скриптом. Избавляемся от второго недостатка.

Теперь, если пользователю требуется повторить операцию, нам уже не нужно второй раз генрировать весь скрипт на сервере, достаточно просто передать запросом данные модели, получить JSON и обработать его. В высоконагруженных приложениях, создающих сотни запросов к БД в секунду нам дорог каждый байт извлекаемой и передаваемой информации. При таком подходе получается очень существенная экономия и процессорного времени и нагрузки на ОЗУ и трафика.

Серверная часть:

Файл  myModel.php

<?php
// классическая модель из MVC, принимает, обрабатывает и возвращает данные. 
//Только все это в JSON</span>
     $myVar1 = $_REQUEST['myvar1']; //Пользовательская переменная 1
     $myVar2 = $_REQUEST['myvar2'] //Пользовательская переменная 2
     $incomeArray = json_decode($_REQUEST['incomeArray']); //Прием JSON-кодированного массива или объекта
     $returnedArray = array(); //Заготовка под возвращаемый массив
     .
     .
     //Наш замечательный код модели, где мы соединяемся с БД, делаем выборки, обрабатываем, записываем данные.
     //Развлекаемся, как нам угодно.
     //Главное - не забывать периодически писать в выходной массив $returnedArray,
     // то, что мы таки должны вернуть клиенту ;)
     .
     .
     echo json_encode($returnedArray); //Просто направляем на выход JSON-кодированный массив
?>
 

Клиентская часть:

Файл dynamic-include.js:

//просто функция, которая позволяет подключить файл по имени и после подключения вызывает callback - функцию
 
// Принимает путь и имя подключаемого файла,  
// имя callback - функции, ее мы выполним по окончании загрузки 
function dynInclude(filename,callback){ 
    Asset.javascript( //Подключим динамический JavaScript 
        filename, //Что грузим 
            { 
                    onLoad: function(){  
                        if(callback){ //Если передали callback, то просто вызываем эту функцию 
                            window[callback](); 
                        } 
                    } 
            } 
     ) 
} 
 

Файл business-face-001.js:

 
//Тоже просто функция, при вызове посылает AJAX - запрс к модели, получает данные, выводит сообщение в консоль 
function getMyData(){ 
    var myData = new Request.JSON({ 
         'url':'myModel.php', 
        'method':'post', 
        data:{//Те самые переменные для серверной части 
            'myvar1':'First variable', 
            'myvar2':'Second variable', 
            'incomeArray': JSON.encode([1,2,3,4,5]) 
        }, 
        onSuccsess:function(jsn,text){ 
            console.log(jsn);// Выводим в консоль полученный JSON. 
 
        } 
    }).send()//Отправляем 
} 
 

Объединяем все в myPage.html

<html>
          <head>
                  <title>Cтраница с динамически подгружаемым скриптом</title>
                  <script type="text/javascript" src="mootools-core.js">
                  <script type="text/javascript" src="mootools-more.js">
                  <script type="text/javascript" src="dynamic-include.js">
          </head>
          <body>
                  <a href="nojs.html"  id="myA" onclick="dynInclude('business-face-001.js','getMyData')">кликни для подключения дополнительного скрипта</a>
          <body>
</html>

 

По-моему, здесь все прозрачно. Естественно, событие на ссылку можно повесить любым удобным способом. 

Распределенная MVC

В самом грубом упрощении, то, что было описано в последнем примере и есть  распределенная MVC. При этом в роли контроллера выступает dynamic-include.js, в роли модели  - myModel.php а в роли представления - business-face-001.js. Более сложный, но более эффективный подход к построению распределенных приложений выглядит так:

  • контроллер на сервере, обеспечивающий централизованный доступ к моделям и представлениям , разграничивающий уровни доступа пользователей и обеспечивающий централизованную политику безопасности.
  • js-библиотека на клиенте, определяющая пространство имен и инкапсулирующая и унифицирующая подключение скриптов, работает только через контроллер.
  • модели на сервере, также подключаются только через контроллер, опять же в целях безопасности. Всегда проще и понятнее управлять единым механизмом в контроллере, чем делать свой для каждой модели. 
  • js-представления на клиенте. Подгружаются и посылают запросы к серверу при помощи методов библиотеки, получают, обрабатывают результаты запросов и работают с DOM самостоятельно.

Такой подход дает еще одно дополнительное преимущество проекту: серверная и клиентская части приложения становятся полностью независимыми. Есть возможность при необходимости, например, сменить СУБД или даже язык разработки для серверной части, и на клиентской это никак не отразится. Приложение становится супер-масштабируемым.

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

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


Защитный код
Обновить