18 сентября 2008 в 09:29

RIA JsClasses Integrator: Оптимизируем разработку на JavaScript

RIA JavaScript Classes IntegratorЧем ближе Ваш сайт приближается к концепции Web2.0 тем больше JavaScript-кода появляется на Ваших страницах. Очевидно, что разработчику все больше времени приходится тратить на интеграцию своего серверного кода на php/java/python… с клиентской частью на JavaScript.

Большое количество кода требует какой-то систематизации, например, разнести логически связанные части кода по разным файлам, имена файлов привязать к контроллеру/событию/блоку… Наверняка у многих разработчиков есть свое решение этой проблемы.
На Хабре, например, JS-код разбит на небольшие блоки, которые запрашиваются в зависимости от страницы. Код написан красиво, откоментирован, стоят отступы. Небольшой минус такого подхода большое количество блоков (около 25) на каждой странице. В начале я использовал похожую схему, но сейчас, все же, хочу представить хабрасообществу немного другой подход.



Факторы, ускоряющие разработку


Я выделил несколько идей, которые, на мой взгляд, делают процесс разработки JS-кода более эффективным:
  1. Использование JS-фреймворка. Многие фреймворки помимо готового богатого функционала и плагинов делают код более понятным для восприятия.
    Например JS-код:
    document.getElementById(‘myElemrnt’).innerHTML=’Test’;

    на MooTools выглядит намного эстетичнее и компактнее:
    $(‘myElemrnt’).set(‘html’,’Test’);
    Фреймворк может быть любой, просто я уже привык к MooTools. У этого фреймворка есть очень мощный компонент — это классы. Конечно, речь идет не о полноценных классах (их в JavaScript нет), а о пcевдо-описании класса с помощью объекта Class. У объекта Class реализовано наследование (Extends) с доступом к переопределенным методам наследуемого класса, а также множественное наследование (Implements) методов без возможности доступа к переопределенным методам. Боле подробно о классах можно почитать в документации по mootools-классам.
  2. Нужна система в именовании классов, а также в именовании директорий, в которые эти классы помещаются. ”Правила игры” очень важны именно для коммандной разработки.Изобретать свою систему, на мой взгляд, нету смысла. Мне нравятся правила кодирования для Zend Framework. Остается только их немного адаптировать для написания JS (вместо расширения .php писать .js). Ну и, естественно, выполнять условие — один класс в одном файле. Как будет решаться проблема большого количества файлов я напишу ниже.
  3. Каждый класс должен быть документирован. Я отдаю предпочтение стандарту JsDoc. Обязательным условием является тег @requires в коментариях к классам, с помощью которого указываются зависимости между классами.
  4. Результирующий файл должен быть один. Он собирается из классов, код которых расположен по разным папкам (вне видимости веб-сервера), учитывая зависимости и порядок следования классов при сборке.
  5. Результирующий файл пересобирается и у него меняется имя, если были внесены изменения хотя бы в один из классов (или зависимых классов), затребованых «движком» вашего сайта.
  6. Результирующий файл упаковывается модулем JSMin, что увеличивает скорость отдачи и не влияет на удобство разработки.
  7. Результирующий файл «сжимается» по методу gzip, что позволяет браузерам, поддерживающим технологию упаковки gzip, еще больше увеличить скорость загрузки.


Реализация на PHP


Поскольку «голые» идеи звучат неубедительно, вышеизложенные пункты я воплотил в PHP-класс RIA JsClasses Integrator. Класс подключается к вашему движку добалением нескольких строк в php-код и одной строчки в шаблон.

В примере я написал пару базовых классов для Ajax-запросов. Для того, чтобы «пощупать» этот класс, Вам необходимо его установить у себя на компе и попробовать внести изменения в тестовый класс и увидеть, как будет вести себя движок. Данные о нахождении класса, времени модификации и зависимостях между классами кешируются в текстовый файл, но с легкостью можно переписать кеширование, например, в memcached.

Настраивается довольно просто.
В своем «движке» пишите приблизительно следующие строчки:
 require ('../libs/JSMin.php');
 require (
'../libs/JsClasses.php');
 
 
$jsClasses = new JsClasses();
 
$jsClasses->setClassesPath('../js');
 
$jsClasses->setClassesCacheFile('../cache/jsClasses.cache');
 
$jsClasses->make(array('Ria_JsonRequest_Test'));
 
$classesResultJsFileName $jsClasses->getJsFileName();
В заголовке шаблона страницы, который формирует html-код размещаем ссылку на созданный нашим модулем упакованный JavaScript:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" «http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd»>
<html xmlns=«http://www.w3.org/1999/xhtml»>
<head>
        <meta http-equiv=«Content-Type» content=«text/html; charset=utf-8» />
        <title>Simple JsClasses Integration on PHP Site Engine</title>
        <script type=«text/javascript» src=«js/mootools-1.2-core-nc.js»></script>
        <script type=«text/javascript» src="<? echo $classesResultJsFileName ?>"></script>
</head>

Для отдачи браузеру запакованных файлов в директории c автособраными JS-файлами (по умочанию js/cache/) создаем .htaccess следующего содержания:
<FilesMatch "\.js.gz$">
ForceType text/javascript
Header set Content-Encoding: gzip
</FilesMatch>
<FilesMatch "\.js$">
RewriteEngine On
RewriteCond %{HTTP_USER_AGENT} !".*Safari.*"
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{REQUEST_FILENAME}.gz -f
RewriteRule (.*)\.js$ $1\.js.gz [L]
ForceType text/javascript
</FilesMatch>


Ccылки по теме

Олег Черний @apelsyn
карма
178,1
рейтинг 0,0
Разработчик
Похожие публикации
Самое читаемое Разработка

Комментарии (27)

  • –1
    К сожалению пока не встречал вреймворков которые в полной мере удовлетворили потребности!
  • –1
    Использование фреймворков для JsClasses не обязательно, можно использовать родную конструкцию:
    var myClass = new function() {

    }

    Но на мой взгляд это все же снижает скорость разработки.
  • 0
    хехе, если в яваскрипте нету классов, то в пхп нету объектов :-P
    класс, в динамических языках — это объект, выполняющий функции фабрики для создания других объектов.
    в яваскрипте эта функция возложена на функции. так что в нём есть классы и они самые, что ни на есть, настояшие. просто на них не висит таблички «это класс» — вот все и бегут «реализовывать классы в яваскрипте» =)
    впрочем, в мутулз классы какие-то совсем закастылезованные, хотя, конечно есть и более замороченные реализации…

    jsmin вместе с gzip применять смысла не имеет. ибо от минимизации тут выигрыша никакого, а исходник таки поганится.

    • 0
      Читай внимательно dklab.ru/chicken/nablas/39.html. И только после этого делай громкие заявления. ;)
      • +1
        1) Fatal: /chicken/nablas/39.html.: could not find handlers chain for extension «»
        2) Fatal: /chicken/nablas/39.html.: could not find handlers chain for extension «»
        BEGIN failed--compilation aborted at (eval 6) line 2.
        4) Fatal: /chicken/nablas/39.html.: could not find handlers chain for extension «»
        BEGIN failed--compilation aborted at (eval 6) line 2. at /home/dklab/domains/www/_Kernel/Scriptor.pl line 44.

        спасибо, подрочил =)
        • –2
          а, там точку в конце надо было убрать… ну и как можно серьёзно относиться к человеку, который вываливать фатальные ошибки в браузер? ;-)

          впрочем, я очередной раз прочитал эту наблу… и что теперь?
        • +1
          Можете почитать из кеша google:
    • 0
      По поводу обязательности использования фреймворков я уже отвечал.

      А JsClasses создает два файла js/cache/<tmp_имя>.js и js/cache/<tmp_имя>.js.gz.

      Второй файл отдается внутренним редиректом если в header-е http-запроса есть строка «Accept-Encoding: gzip, deflate». Т.е. запрашивается всегда первый файл а отдаваться он может в «сжатом» виде, в случае, если браузер такой вариант отдачи понимает.
  • 0
    а какие браузеры не поддерживают гзипование?
    • 0
      У Safari были проблемы. У меня сейчас версия 3.1.2, там уже все пофиксили.
      • 0
        были проблемы у сафари под винду, который только релизнулся и которым никто не пользуется? это не слишком актуально.
  • 0
    Спасибо за статью, давно была идея что-то делать с десятками подгружаемых js-файлами модулей, а тут и готовое решение =)
    А есть какие-нибудь цифры тестов скорости загрузки с использованием RIA JsClasses и без?
    • 0
      Перед созданием модуля я изучал код Smarty. Так вот, по сравнению со Smarty класс просто «пушинка» :).
      Пока что, к сожалению, я не нашел времени на написание тестов.
  • +1
    Еще рекомендую добавить в .htaccess проверку не только Accept-Encoding но и TE для клиентов которые используют Оперу и Аутпост. Я про это немного писал здесь
    • +1
      Я внес Вашу статью в «Ccылки по теме».
  • 0
    <script type=«text/javascript» src=«js/mootools-1.2-core-nc.js»></script>
    <script type=«text/javascript» src=«<? echo $classesResultJsFileName ?>»></script>

    А почему первый скрипт не попал в $classesResultJsFileName?

    ЗЫЖ «изминения» режет глаз. исправьте пока никто не увидел =)
    • 0
      Так и задумано. Пересборка такого большого файла заняла бы лишние ресурсы. У меня, например, этот файл грузится с одного url для всех проектов. Некоторые его грузят через google.load;

      Цена за такую неоптимальность не очень большая, зато убирается много проблем.

      Кроме того, я специально оставил MooTools незакомпресированным. Чтоб можно было смотреть в код фреймворка в процессе разработки, когда возникают ошибки. На продакшин сервере я использую mootools-1.2-core-yc.js

      Ошибку поравил, спасибо.
    • –11
      это заказная статья. человек в статье про свой продукт (по иллюстрации страница с кнопкой «download») первым пунктом расписывает mooTools, а потом еще дополнительно выделяет его в исходнике.
      • +1
        ага, и заказал её Valerio Proietti :-)
    • 0
      С задачей кэширования скриптов прекрасно справляются браузеры — а склеивание скриптов в 1ом файле может этому сильно помешать, потому как при открытии новой страницы браузеру ещё надо вытягивать её «индивидуальный» js-файл.

      Имхо, решение хабра более правильное: для каждой страницы — свое подмножество скриптов. После загрузки нескольких таких страниц в кэш браузера переедет всё множество скриптов сайта — и ничего тормозить не будет.

      Цена за такую неоптимальность не очень большая, зато убирается много проблем.

      • +1
        Большое количество файлов не единственный минус такого подхода, есть еще проблемы с обновленной версией блока. На хабре они решаються вот так xmlhandler.js? revision=12. Некоторые браузеры (Opera, Internet Explorer 6+, Safari) НЕ кешируют документы, если в адресе есть вопросительный знак, т.к считают их динамическими.

        Видимо мое упущение в том что я не расписал «в деталях» принцип работы моего скрипта.

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

        Если не учитывать swfupload*, то все js-файлы Хабра можно было спаковать (я думаю) в 50-60 кб один архив и ставить его на каждую станичку без '?' вместо 200-250 как сейчас.

        Но я не говорю что у Хабра сделано плохо. У любого метода есть свои плюсы и свои минусы.

        Я ориентироваля на решение проблемы оптимизация или ускорение процесса коммандной разработки.

        • 0
          От вопросительного знака можно избавиться, добавив номер версии в имя скрипта. Например, xmlhandler_v12.js.

          Кстати, как Ваш упаковщик скриптов «разруливает» сборку различных ревизий классов?
          • 0
            По дате изменения класса. По каждому классу эта информация храниться в кеше.

            На production-сервере можно проверки отрубить
            $jsClasses->setCheckClassModifyTime(false)

            и после каждого апдейта запускать модуль отдельно в режиме «чистки» кеша, с помощью «спецключа» к запуску сайта:

            if ($_GET['mySuperCacheRebuildKey']) { $jsClasses->cleanCacheDir(); }

            Тогда в коде ничего менять не прийдется. И нагдузка будет еще меньше.

            Сам класс хорошо документирован, вы можете его можете наследовать в своем классе и доработать до того вида который нужен Вам.
        • 0
          НЕ кэшируют если НЕ указаны заголовки регламентирующие кэширование.
  • 0
    в JsDoc ужасное комментирование.

    попробуйте закомментировать /* функцию */

    я вобще давно уже приучаю своих подопечных делать комментарии для функций исключительно в стиле

    // ===========
    // * бла бла бла
    // ===========
  • 0
    А как насчет ленивой (on-demand) загрузки нужных скриптов/стилей? Такая используется в Dojo, еще есть Chiron (modules.js), и еще много где. :)

    Где-то смотрел видео, где комрад рассказывал как раз об этом: делайте все лениво. Производите вычисления только тогда, когда это нужно.
  • 0
    тоже интересная статья в «тему»

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