Для демонстрации возможностей 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.
Проект готов и мы можем приступать к его оптимизации. Для этого необходимо выполнить несколько шагов.
- На официальном сайте скачать и установить Node. Он необходим для запуска скрипта оптимизации
- Скачать скрипт оптимизации r.js. Создадим отдельную папку optimization в корне нашего проекта и поместим этот скрипт туда.
- В папке 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)
rozboris
02.04.2015 23:31Спасибо за статью. У меня вопрос — есть веб-приложение в 15000 строк кода на JS. Есть много файлов, написанных на коленке и по-старинке, не модульно. Я уже несколько месяцев пытаюсь всё это причесать и вот что пока получилось — содержимое каждого файла обернуто в closure, из него экспортируются конкретные функции, то есть глобальный неймспейс чистый (в него кладется всего один объект с именем файла).
Это позволяет точно знать, откуда вызывается та или иная, бывшая ранее глобальной, функция. Также это мне позволило сделать всему коду JSHint и скомпилировать всё через Google Closure Compiler (simple, не advanced).
Я несколько раз пытался перевести все эти файлы в формат AMD, но сталкивался с круговыми зависимостями — каждый «модуль» использует функции из 2-6 других модулей, всего модулей около 25. Насколько я понимаю, RequireJS не умеет нормально работать с круговыми зависимостями и предлагает от них отказаться. Это здорово, но в моем случае пока невозможно.
Вы не можете посоветовать чего-нибудь в такой ситуации? У меня совсем нет опыта в RequireJS и я хотел изучить его на этом проекте, но вот, видимо, не судьба.
Заранее спасибо.Fesor
02.04.2015 23:53Вы не можете посоветовать чего-нибудь в такой ситуации?
я уже выше написал… но повторюсь…
ES6 + babel + System.jsrozboris
02.04.2015 23:55В смысле, всё переписать? Ну, это понятно. Вопрос, что сделать с этим кодом, не переписывая.
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 () { } }
RusSuckOFF
02.04.2015 23:58+3Requirejs… Используйте CommonJS модули в комплекте с browserify или webpack. У них runtime меньше, в мир npm интегрироваться проще.
Статья уже устарела на несколько лет.Fesor
03.04.2015 00:02Можно вообще без рантайма собрать при желании. Именно по этой причине я использую es6-module-transpiler — там один из форматтеров — bundler — просто оборачивает все модули в IIFE и в нужном порядке все разруливает. Собирается все на ура, даже с учетом циклических зависимостей. А оверхэд по названиям функций и переменных устраняется аглификаторами.
Jabher
03.04.2015 02:22-5Я не знаю почему, но почему-то все адепты AMD-модулей так же судорожно любят Backbone, bower и grunt. Какое-то иррациональное комбо мазохиста. Вот и тут.
enleur
03.04.2015 07:19А какая альтернатива? Я так понимаю: requirejs/amd -> browserify/commonjs, bower -> npm, grunt -> gulp, backbone -> react или angular?
Fesor
03.04.2015 08:16+1Ну в этом есть определенная логика. Если человек осознал всю прелесть модульного подхода и загрузчиков модулей (AMD, CommonJS, ES6 модули) то значит радость от использования grunt/gulp и bower он уже получил. А поскольку можно выполнить одну команду и подключить какую-нибудь библиотечку или фреймворк — можно таскать быстреньки и backbone/angular/react/ember… и не писать велосипеды.
k12th
03.04.2015 08:47+2AMD проще дружится с bower, чем с npm (потому что авторы npm ниасилили настройку «npm_modules» в конфиге).
browserify тоже не сахар — AMD хотя бы в одном месте конфигурится, и 99% проблем решается добавлением строчки в секцию shim. Еще одно мощное преимущество AMD: несклеенные файлы в dev-окружении — что критично на проекте с поддержкой старых браузеров.
Ну и потом, если человек пришел к необходимости модульной загрузки и MV* на клиенте задолго до появленияхипстеровangular и browserify — то ему ничего не оставалось, кроме как использовать Backbone и AMD, а если стэк у тебя отработан, то не всегда охота его менять.
kambur Автор
03.04.2015 23:38Вы видимо не внимательно прочитали заголовок статьи. Там не написано «RequireJS и Backbone — панацея от всего на свете». Цель, с которой была написана эта статья — это рассказать о возможности оптимизации проектов на Require, а совсем не популяризация этой библиотеки.
serf
Как-то мне кажется в наше время слишком громко называть объединение файлов оптимизацией, особенно оптимизацией кода, оптимизацией загрузки еще может быть. Вообще мне хватает browserify + gulp, всякие поделки не нужны.
Fesor
ES6 + babel + System.js
serf
ES6 я еще не исопльзую. Если начинать новые проекты ES6 + babel конечно хорошо.
k12th
Ну вообще-то загружать склеенную статику оптимальней, как ни крути.
r.js вроде бы поддерживает uglify, странно, что у автора это не упомянуто.