В своем предыдущем посте я рассказал о том, как подключить RequireJS к своему проекту. Кроме того, пообещал рассказать об оптимизации. Оптимизировать проекты, построенные на RequireJS очень быстро и легко. Причём оптимизации подлежит как сам код (скрипты, библиотеки, плагины) так и файлы стилей.

Для демонстрации возможностей RequireJS по оптимизации кода я создал простой одностраничный проект. На главной странице отображается список пользователей. Кликнув по пользователю попадаем на страницу с информацией о нем. Пользователи делятся на два типа — обычные и администраторы, и их страницы отличаются как наполнением так и оформлением.

Для реализации данной задачи я, как всегда, использовал библиотеку Backbone и конечно же RequireJS. На то, что в итоге у меня получилось, вы можете посмотреть здесь.

Как это делается


С подключением RequireJS проблем возникнуть не должно. Главное правильно описать конфигурацию (файл application.js):

requirejs.config({
    baseUrl: "js/library",
    paths: {
		jquery: 'jquery.min',
		backbone: 'backbone.min',
		underscore: 'underscore.min',
		domready: 'domReady',
		appControllers: '../Controllers',
		appModels: '../Models',
		appViews: '../Views'
    },
	shim: {
        'underscore': {
            exports: '_'
        },		
		'backbone': {
            deps: ['underscore', 'jquery'],
            exports: 'Backbone'
        }
	}
});

Сразу хочу ответить на вопрос: для чего подключать Backbone в блоке shim? Ответ очень прост. Версия библиотеки Backbone и underscore, которую использую я, не поддерживает AMD. Поэтому иx обязательно необходимо добавить в блок shim. Но можно скачать версии с поддержкой AMD и спокойно использовать Backbone без дополнительных настроек RequireJS.

Проект готов и мы можем приступать к его оптимизации. Для этого необходимо выполнить несколько шагов.

  1. На официальном сайте скачать и установить Node. Он необходим для запуска скрипта оптимизации
  2. Скачать скрипт оптимизации r.js. Создадим отдельную папку optimization в корне нашего проекта и поместим этот скрипт туда.
  3. В папке optimization создаём файлы с настройками оптимизации для скриптов (файл build_scripts.js) и стилей (build_css.js). Настроек достаточно много. Полный их список находится здесь. Эти настройки можно задавать и как параметры командной строки непосредственно при запуске скрипта оптимизации. Но я решил сохранить их в файлах. Во-первых, это удобнее при редактировании. Во-вторых, параметры командной строки имеют ограничения в синтаксисе. А именно — точка интерпретируется как разделитель для свойств объектов. Поэтому запись вида

    paths.core/jquery.tabs=empty: 
    

    будет соответствовать конфигурации

    paths: {
        'core/jquery': {
            tabs: 'empty:'
        }
    }
    

    а не

    paths: {
        'core/jquery.tabs': 'empty:'
        }
    }
    


Все готово. Теперь просто запускаем скрипт оптимизации. Заходим в консоль и выполняем команду:

node r.js -o build_scripts.js

Получили оптимизированный файл скриптов application-build.js.

Теперь давайте оптимизируем наши файлы стилей. Здесь не все так просто как со скриптами. В проекте используются четыре файла стилей:

  • application.css
  • adminpage.css
  • userpage.css
  • bootstrap.min.css

Для того, чтобы объединить их в один, сделаем так: импортируем в файл application.css другие три файла:

@import url("bootstrap.min.css");
@import url("userpage.css");
@import url("adminpage.css");

Выполним оптимизацию:

node r.js -o build_css.js

Получили файл application-built.css. А теперь проверим результат подключив наши оптимизированные файлы в проект.

<link href="css/application-built.css" rel="stylesheet" media="screen">

<script type="text/javascript" src="application-built.js"></script>

Как видим все работает. Кстати, вот такая строка в файле с конфигурацией

include: ["requireLib"],

позволяет нам использовать оптимизированный файл скриптов отдельно, без RequireJS. Иначе оптимизированный скрипт подключался бы так:

<script data-main="js/application-built" src="js/library/require.js"></script>

Для чего это нужно


Оптимизация помогает ускорить загрузку скриптов, что в свою очередь улучшает скорость работы сайта в целом. Но как узнать, действительно ли проекту нужна оптимизация? Возможно все и так работает достаточно быстро, и улучшить ничего нельзя? Ответ на этот вопрос вам даст YSlow.

YSlow — это расширение для браузера Mozilla Firefox, разработанное компанией Yahoo. Оно умеет измерять скорость загрузки страниц, а также проводит достаточно подробный анализ их отдельных компонент. Одним из пунктов при анализе как раз и есть необходимость оптимизации кода. Работает YSlow не самостоятельно, а как часть расширения Firebug.

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


  1. serf
    02.04.2015 23:14
    +5

    Как-то мне кажется в наше время слишком громко называть объединение файлов оптимизацией, особенно оптимизацией кода, оптимизацией загрузки еще может быть. Вообще мне хватает browserify + gulp, всякие поделки не нужны.


    1. Fesor
      02.04.2015 23:53
      +3

      ES6 + babel + System.js


      1. serf
        02.04.2015 23:55

        ES6 я еще не исопльзую. Если начинать новые проекты ES6 + babel конечно хорошо.


    1. k12th
      03.04.2015 08:36

      Ну вообще-то загружать склеенную статику оптимальней, как ни крути.
      r.js вроде бы поддерживает uglify, странно, что у автора это не упомянуто.


  1. rozboris
    02.04.2015 23:31

    Спасибо за статью. У меня вопрос — есть веб-приложение в 15000 строк кода на JS. Есть много файлов, написанных на коленке и по-старинке, не модульно. Я уже несколько месяцев пытаюсь всё это причесать и вот что пока получилось — содержимое каждого файла обернуто в closure, из него экспортируются конкретные функции, то есть глобальный неймспейс чистый (в него кладется всего один объект с именем файла).
    Это позволяет точно знать, откуда вызывается та или иная, бывшая ранее глобальной, функция. Также это мне позволило сделать всему коду JSHint и скомпилировать всё через Google Closure Compiler (simple, не advanced).
    Я несколько раз пытался перевести все эти файлы в формат AMD, но сталкивался с круговыми зависимостями — каждый «модуль» использует функции из 2-6 других модулей, всего модулей около 25. Насколько я понимаю, RequireJS не умеет нормально работать с круговыми зависимостями и предлагает от них отказаться. Это здорово, но в моем случае пока невозможно.
    Вы не можете посоветовать чего-нибудь в такой ситуации? У меня совсем нет опыта в RequireJS и я хотел изучить его на этом проекте, но вот, видимо, не судьба.

    Заранее спасибо.


    1. Fesor
      02.04.2015 23:53

      Вы не можете посоветовать чего-нибудь в такой ситуации?

      я уже выше написал… но повторюсь…
      ES6 + babel + System.js


      1. rozboris
        02.04.2015 23:55

        В смысле, всё переписать? Ну, это понятно. Вопрос, что сделать с этим кодом, не переписывая.


        1. Fesor
          03.04.2015 00:01
          +1

          Почему же? Оставьте как есть, просто переведите все на ES6 (или правильнее ES2015). Код для этого менять не нужно. Зато можно заюзать модули и постепенно причесывать проект. Собирать в прод рекомендую при помощи es6-module-transpiler и потом уже прогонять все через babel (и то если у вас кроме модулей еще чего юзается, например классы или стрелочные функции). Грубо говоря:

          // было
          
          window.someModule = (function () {
              var somePrivateVar;
          
              return {
                  someMethod: someMethod
              }
          
              function someMethod() {}
          }());
          
          // с приходом ES2015
          // собственно не особо чего поменялось
          // просто можно смело убрать IIFE 
          var somePrivateVar;
          
          export default {
              someMethod: someMethod
          }
          
          function someMethod() {}
          
          // много позже может стать:
          
          export default class SomeModule {
              constructor() {
              }
          
              someMethod () {
              }
          }
          


  1. RusSuckOFF
    02.04.2015 23:58
    +3

    Requirejs… Используйте CommonJS модули в комплекте с browserify или webpack. У них runtime меньше, в мир npm интегрироваться проще.
    Статья уже устарела на несколько лет.


    1. Fesor
      03.04.2015 00:02

      Можно вообще без рантайма собрать при желании. Именно по этой причине я использую es6-module-transpiler — там один из форматтеров — bundler — просто оборачивает все модули в IIFE и в нужном порядке все разруливает. Собирается все на ура, даже с учетом циклических зависимостей. А оверхэд по названиям функций и переменных устраняется аглификаторами.


      1. spmbt
        03.04.2015 01:34

        ES6 + babel + System.js?


        1. Fesor
          03.04.2015 01:35

          Это для разработки, модули пока не поддерживаются ни в одном браузере нативно… только через полифил. Для продакшена я все же собирать это должен как-то. HTTP2 пока не так уж и популярен.


  1. Jabher
    03.04.2015 02:22
    -5

    Я не знаю почему, но почему-то все адепты AMD-модулей так же судорожно любят Backbone, bower и grunt. Какое-то иррациональное комбо мазохиста. Вот и тут.


    1. enleur
      03.04.2015 07:19

      А какая альтернатива? Я так понимаю: requirejs/amd -> browserify/commonjs, bower -> npm, grunt -> gulp, backbone -> react или angular?


    1. Fesor
      03.04.2015 08:16
      +1

      Ну в этом есть определенная логика. Если человек осознал всю прелесть модульного подхода и загрузчиков модулей (AMD, CommonJS, ES6 модули) то значит радость от использования grunt/gulp и bower он уже получил. А поскольку можно выполнить одну команду и подключить какую-нибудь библиотечку или фреймворк — можно таскать быстреньки и backbone/angular/react/ember… и не писать велосипеды.


    1. k12th
      03.04.2015 08:47
      +2

      AMD проще дружится с bower, чем с npm (потому что авторы npm ниасилили настройку «npm_modules» в конфиге).
      browserify тоже не сахар — AMD хотя бы в одном месте конфигурится, и 99% проблем решается добавлением строчки в секцию shim. Еще одно мощное преимущество AMD: несклеенные файлы в dev-окружении — что критично на проекте с поддержкой старых браузеров.

      Ну и потом, если человек пришел к необходимости модульной загрузки и MV* на клиенте задолго до появления хипстеров angular и browserify — то ему ничего не оставалось, кроме как использовать Backbone и AMD, а если стэк у тебя отработан, то не всегда охота его менять.


    1. kambur Автор
      03.04.2015 23:38

      Вы видимо не внимательно прочитали заголовок статьи. Там не написано «RequireJS и Backbone — панацея от всего на свете». Цель, с которой была написана эта статья — это рассказать о возможности оптимизации проектов на Require, а совсем не популяризация этой библиотеки.