Активные веб-элементы - Советы по оптимизации структуры и увеличению производительности

Вспомните, как много времени вы раньше тратили на оптимизацию активных элементов вашего проекта (изображения, CSS и т.д.)? Хорошо, что сегодня пользователи имеют в своем распоряжении гораздо более быстрый интернет, и теперь, кажется, мы, наконец, можем использовать большие изображения или большие флэш-файлы.

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

Изображения

Обеспечить нужный размер

Часто мы используем те же изображения для различных разделов нашего сайта. Например, в интернет-магазине все товары имеют общий снимок. Давайте предположим, что у нас есть три страницы, на которых выводится это изображение: одна страница – это общий перечень товаров, в который входит данный продукт, вторая – собственно его детальное описание, и третья – вывод изображения товара в оригинальном размере.

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

Где на самом деле хватило бы и картинки 200 на 200 пикселей. Если исходный файл имеет размер порядка 1 Мб, а у нас списке существует десять позиций, то только при загрузке изображений пользователю придется выкачать 10 Мб.

Это не очень хорошо. Если бы вы попытались создать различные изображения для различных страниц вашего сайта, это сэкономило бы пользователю много KBs. Также правильно было бы обращать внимание на разрешение экрана устройства пользователя.

Например, если кто-то заходит на сайт с мобильного устройства, ему нет никакой нужды загружать огромное изображение в шапке сайта, которое вы обычно используете. С помощью Медиа-запросов CSS можно отправлять пользователю на загрузку изображение меньших размеров:

@media only screen
and (min-device-width : 320px) 
and (max-device-width : 480px) {
    .header {
        background-image: url(../images/background_400x200.jpg);
    }
}

Сжатие

Загрузки изображения с применением настроек нужного размера не всегда достаточно. Файлы некоторых форматов могут быть значительно ужаты, при этом без потери качества. Есть много программ, которые могут вам в этом помочь. Например, Photoshop поддерживает отличную функцию «Сохранить для Веб и прочих устройств»:

Сжатие

В данной панели есть множество опций, но одна из самых важных – «Качество». Если задать ее значение в районе 80%, то можно значительно уменьшить размер загружаемого файла.

Конечно, можно использовать для сжатия файлов код, но я лично предпочитаю Photoshop. Его я использую всегда, когда есть такая возможность. Ниже приводится простой пример кода PHP:

function compressImage($source, $destination, $quality) {
    $info = getimagesize($source);
    switch($info['mime']) {
        case "image/jpeg":
            $image = imagecreatefromjpeg($source);
            imagejpeg($image, $destination, $quality);
        break;
        case "image/gif":
            $image = imagecreatefromgif($source);
            imagegif($image, $destination, $quality);
        break;
        case "image/png":
            $image = imagecreatefrompng($source);
            imagepng($image, $destination, $quality);
        break;
    }
}
compressImage('source.png', 'destination.png', 85);

Спрайты

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

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

Например, Twitter Bootstrap при загрузке главной страницы использует спрайты для своих внутренних иконок:

Спрайты

Тогда через CSS вы можете прописать приблизительно вот такой код, чтобы вывести ту часть спрайта, что вам нужна:

.icon-edit {
    background-image: url("../img/glyphicons-halflings-white.png");
    background-position: -96px -72px;
}

Кэширование

Механизм кэширования браузера тоже станет вам большим подспорьем. Да, иногда в процессе работы это может привести к некоторым очень смешным ситуациям, но это действительно помогает увеличить эффективность сайта. Каждый браузер кэширует контент, например, изображения, JavaScript или CSS.

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

$expire = 60 * 60 * 24 * 1;// seconds, minutes, hours, days
header('Cache-Control: maxage='.$expire);
header('Expires: '.gmdate('D, d M Y H:i:s', time() + $expire).' GMT');
header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');

Предварительная выборка

HTML5 развивается каждый день. В нем есть одна полезная особенность, которая называется предварительная выборка. С ее помощью браузеру сообщается, что в ближайшем будущем вам будут нужны некоторые ресурсы, и он должен загрузить информацию с них сейчас – на будущее. Например:

<link rel="prefetch" href="/images/background.jpg">

Схемы URI- данных / Встроенные картинки

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

И, конечно, нужно было встроить в код несколько изображений. Схемы URI- данных помогли мне решить эту проблему. Идея состояла в том, чтобы конвертировать изображения в кодировке Base64 и поместить его в атрибут SRC тега IMG .

Например:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot">

С помощью этого метода ваше изображение на самом деле будет частью HTML-кода, и вся страница будет загружаться за один HTTP-запрос. Конечно, если у вас большое изображение, строка кода будет очень длинной. Вот пример простого скрипта PHP , который преобразует изображения в строку кода base64:

$picture = fread($fp,filesize($file));
fclose($fp);
// base64 переводит данные в двоичный код, а затем разбивает его на части
// в соответствии правилами семантики RFC 2045
$base64 = base64_encode($picture);
$tag = '<img src="data:image/jpg;base64,'.$base64.'" alt="" />';
$css = 'url(data:image/jpg;base64,'.str_replace("n", "", $base64).'); ';

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

CSS

Мне нравится думать, что описание CSS-стилей похоже на составление кода. Вы описываете стили для различных блоков вашего сайта и взаимосвязей между ними. Поэтому я думаю, что управление CSS является действительно важной вещью.

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

Все мы знаем, что использование оператора @import не очень хорошая идея. Потому что каждый новый оператор @import означает новый запрос к серверу. И если у вас, например, есть 20 различных файлов CSS, то это значит, что браузер будет выполнять 20 HTTP-запросов.

И браузер не сможет загрузить/вывести страницу, прежде чем не будут загружены все стили. Если некоторые из ваших файлов CSS удалены или имеют большой размер, то прежде чем вы увидите страницу на экране может пройти большое время.

Используйте CSS-препроцессоры

С помощью CSS-препроцессоров можно решить все описанные выше проблемы. Вы можете все также разделять ваши стили по разным файлам, но в конечном итоге препроцессор компилирует их в один CSS-файл. Кроме того, CSS-препроцессоры предоставляют еще много действительно интересных функций: переменные, вложенные блоки, Mixins и наследования.

Код по-прежнему представляется в формате CSS, но теперь он уже хорошо отформатирован и структурирован. Есть несколько распространенных препроцессоров, которые вы можете попробовать применить на своем сайте: Sass, LESS и Stylus. Вот простой пример кода CSS-препроцессора LESS:

.position(@top: 0, @left: 0) {
    position: absolute;
    top: @top;
    left: @left;
    text-align: left;
    font-size: 24px;
}
.header {
    .position(20px, 30px);
    .tips {
        .position(10px, -20px);
    }
    .logo {
        .position(10px, 20px);  
    }
}

В конечном итоге он приводит к следующей группировке стилей:

.header {
    position: absolute;
    top: 20px;
    left: 30px;
    text-align: left;
    font-size: 24px;
}
.header .tips {
    position: absolute;
    top: 10px;
    left: -20px;
    text-align: left;
    font-size: 24px;
}
.header .logo {
    position: absolute;
    top: 10px;
    left: 20px;
    text-align: left;
    font-size: 24px;
}

Или, например, если у вас есть стиль кнопки, и вы хотите вывести такую же кнопку, только с другим цветом текста. Тогда вы можете использовать следующий код:

.button {
    border: solid 1px #000;
    padding: 10px;
    background: #9f0;
    color: #0029FF;
}
.active-button {
    .button();
    color: #FFF;
}

Эффективный CSS

Очень часто разработчики не задумываются об эффективности CSS. Эффективность CSS определяет скорость вывода страницы, и, если ваши стили неэффективны, запрос будет обрабатываться браузером очень медленно. Интересно, что браузеры разбирают селекторы CSS справа налево. Это означает, что следующий код:

body ul li a {
    color: #F000;
    text-decoration: none;
}

неэффективен в принципе. Движок загрузит все теги <a> и должен будет оценивать каждый из родительских элементов, чтобы, в конце концов, собрать все необходимые стили. Вы также должны знать, что с точки зрения эффективности виды селекторов ранжируются в следующем порядке: ID, класс, тэг и универсальные.

Это означает, что элемент с ID SET будет показываться быстрее, чем просто элемент с селектором тегов. Конечно, нет смысла добавлять идентификаторы ко всем элементам в дереве DOM, но вы должны обязательно проинспектировать свой код и улучшить его там, где это возможно. Например, если у вас есть запись вроде этой:

ul #navigation li {
    background: #ff0232;
}

Вы должны удалить части ul, потому, что у вас на странице есть только один элемент #navigation. Или в следующем селекторе:

body .content p {
    font-size: 20px;
}

понятно, что элемент .content является дочерним для тега body. На самом деле все элементы являются для него дочерними.

Вот две полезных ссылки по этой теме: developers.google.com и css-tricks.com

Размер файла

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

Объединяйте похожие стили:

.header {
    font-size: 24px;
}
.content {
    font-size: 24px;
}

можно преобразовать в:

.header, .content {
    font-size: 24px;
}

Используйте сокращения. Вместо:

.header {
    background-color: #999999;
    background-image: url(../images/header.jpg);
    background-position: top right;
}

Можно написать следующий код:

.header {
    background: #999 url(../images/header.jpg) top right;
}

Минимизируйте ваш CSS-код. Вы можете сделать это с помощью инструментов, которые предназначены для удаления лишних пробелов и строк. Например, CSSOptimiser или Minifycss.

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

Размещайте ссылки на ваши файлы CSS между тэгами <head>.

Будет полезно включить ссылки на ваши CSS-файлы между тэгами <head>. В этом случае браузер будет загружать их первыми.

JavaScript

Уменьшение количества HTTP-запросов

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

Минимизировать код

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

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

Попробуйте CommonJS, AMD, RequireJS

Изначально в JavaScript не предусмотрены механизмы для управления модулями. Поэтому они были разработаны отдельно, чтобы решить данную проблему. Они предусмотрены приложения, которые вы можете использовать, чтобы задавать и использовать модули.

Вот пример одного из них, взятый с http://requirejs.org/:

<!DOCTYPE html>
<html>
    <head>
        <title>My Sample Project</title>
        <!-- data-main attribute tells require.js to load
             scripts/main.js after require.js loads. -->
        <script data-main="scripts/main" src="scripts/require.js"></script>
    </head>
    <body>
        <h1>My Sample Project</h1>
    </body>
</html>

Внутри скрипта main.js вы можете прописать функцию require() для загрузки любого другого сценария, который вам нужен:

require(["helper/util"], function(util) {
    //This function is called when scripts/helper/util.js is loaded.
    //If util.js calls define(), then this function is not fired until
    //util's dependencies have loaded, and the util argument will hold
    //the module value for "helper/util".
});

Использование раздела имен

Если мы говорим об упорядочении кода, то в этой теме никак нельзя обойти стороной разделы имен. В оригинальном JavaScript данная функция отсутствует, но вы можете достичь того же результата, добавив небольшой код. Например, если вы хотите создать свой собственный фреймворк MVC, можно задать следующие классы:

var model = function() { ... };
var view = function() { ... };
var controller = function() { ... };

Если оставить приведенный выше код, как есть, во-первых он может быть скопирован кем-то, а, во-вторых, существует вероятность, что во время использования будут часто возникать конфликты с другими библиотеками вашего проекта. Однако сгруппировав его, как самостоятельный объект (раздел имен), можно обеспечить его защиту:

ar MyAwesomeFramework = {
    model: function() { ... },
    view: function() { ... },
    controller: function() { ... }

Используйте шаблоны дизайна

Вам нет никакой необходимости заново изобретать колесо. JavaScript на сегодняшний день достиг широкого распространения, и дня него разработано большое количество готовых решений. Шаблоны дизайна представляют собой многоразовые решения для общих проблем программирования.

Использование некоторых из них поможет вам создать функциональные и надежные приложения. Если я попытаюсь хотя бы бегло охватить их все, то мне придется написать целую книгу. Однако вот примеры некоторых из них:

Шаблон конструктора

Данный шаблон можно использовать для создания объекта определенного типа. Например:

var Class = function(param1, param2) {
    this.var1 = param1;
    this.var2 = param2;
}
Class.prototype = {
    method:function() {
        alert(this.var1 + "/" + this.var2);
    }
};

Или вот:

function Class(param1, param2) {
    this.var1 = param1;
    this.var2 = param2;
    this.method = function() {
        alert(param1 + "/" + param2);
    };
};
 
var instance = new Class("value1", "value2");

Шаблон модуля

Шаблон модуля дает нам возможность создавать частные и публичные методы. Например, в приведенном ниже коде, переменная _index и метод privateMethod являются частными. А increment и getIndex – публичные:

var Module = (function() {
    var _index = 0;
    var privateMethod = function() {
        return _index * 10;
    }
    return {
        increment: function() {
            _index += 1;
        },
        getIndex: function() {
            return _index;
        }
    };      
})();

Шаблон наблюдения

Везде, где у вас есть подписки или нужна модерация событий, у вас, вероятно, наблюдается следующая картина. Есть люди, которые отслеживают изменения, связанные с определенным объектом.

Как только происходит какое-то действие, объект уведомляет об этом наблюдателей. В приведенном ниже примере показано, как мы можем добавить наблюдателя к объекту пользователей:

var Users = {
    list: [],
    listeners: {},
    add: function(name) {
        this.list.push({name: name});
        this.dispatch("user-added");
    },
    on: function(eventName, listener) {
        if(!this.listeners[eventName]) this.listeners[eventName] = [];
        this.listeners[eventName].push(listener);
    },
    dispatch: function(eventName) {
        if(this.listeners[eventName]) {
            for(var i=0; i&lt;this.listeners[eventName].length; i++) {
                this.listeners[eventName][i](this);
            }
        }
    },
    numOfAddedUsers: function() {
        return this.list.length;
    }
}
 
Users.on("user-added", function() {
    alert(Users.numOfAddedUsers());
});
 
Users.add("Krasimir");
Users.add("Tsonev");

Шаблон цепочки функций

Данный шаблон является хорошим способом организовать публичный интерфейс модуля. Это экономит время и улучшает читабельность:

var User = {
    profile: {},
    name: function(value) {
        this.profile.name = value;
        return this;
    },
    job: function(value) {
        this.profile.job = value;
        return this;
    },
    getProfile: function() {
        return this.profile;
    }
};
 
var profile = User.name("Krasimir Tsonev").job("web developer").getProfile();
console.log(profile);

Я настоятельно рекомендую вам ознакомиться с этой книгой Адди Османи. Это один из лучших источников, которые можно найти о шаблонах проектирования JavaScript.

Assets-Pack

Теперь, когда мы приближаемся к концу данной статьи, я хочу поделиться с вами некоторыми своими соображениями по поводу управления кодами CSS и JavaScript на сервере. Это распространенный метод для обеспечения объединения, минимизации и компиляции логики приложений.

Часто какие-то его элементы запускаются во время кэширования, но все операции происходят непосредственно во время выполнения крипта. Таким образом, проверяется логика кода, с помощью которого осуществляется обработка запроса JS или CSS файлов. Во время этого процесса и осуществляется компиляция, минимизация и упорядочение элементов структуры вашего сайта.

В моих последних проектах я использовал инструмент, который называется assets-pack. Это действительно полезный набор функций, и ниже я подробно объясню, как он функционирует. Но еще более интересно, то, как я его применяю. Данная библиотека предназначена для использования только в процессе разработки.

Она не предназначена для внедрения в исходный код сайта и не предусматривает разворачивания на сервере. Она используется только тогда, когда вы работаете над вашими активными элементами (CSS, JS). Она отслеживает изменения, вносимые в определенные папки и компилирует/запаковывает код в отдельный файл. При таком подходе вам больше не нужно самим заботиться о минимизации или компиляции.

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

Ниже приводится описание установки и использования assets-pack.

Установка

Данный инструмент представляет собой модуль NodeJS, поэтому прежде, чем переходить к его установке, у вас уже должен быть установлен Node. В противном случае просто перейдите по адресу nodejs.org/download, где можно скачать и установить на вашу операционную систему данный пакет. После чего:

npm install -g assetspack

Использование

Модуль работает в конфигурации JSON. Если вы запускаете его из командной строки, то должны заранее позаботиться о том, чтобы все установки были прописаны в файле JSON. Набираем в режиме эмуляции командной строки следующие команды:

Создаем файл assets.json и запускаем в том же каталоге на исполнение следующую команду:

assetspack --config [path to json file]

Код:

var AssetsPack = require("assetspack");
var config = [
    {
        type: "css",
        watch: ["css/src"],
        output: "tests/packed/styles.css",
        minify: true,
        exclude: ["custom.css"]
    }
];
var pack = new AssetsPack(config, function() {
    console.log("AssetsPack is watching");
});
pack.onPack(function() {
    console.log("AssetsPack did the job"); 
});

Настройки:

Настройки должны задаваться в действующем файле / объекте JSON. Который представляет собой просто массив объектов:

[
    (asset object),
    (asset object),
    (asset object),
    ...
]

Объект элемента

Базовая структура объекта элемента выглядит следующим образом:

{
    type: (file type /string, это может быть css, js или, к примеру, less),
    watch: (директория или директории просмотра/string or array of strings/),
    pack: (директория или директории кода на выходе/string or array of strings/. ),
    output: (путь к полученному файлу/string/),
    minify: /boolean/,
    exclude: (массив имен файлов)
}

Задавать параметры опции pack не обязательно. По умолчанию инструмент настроен так, чтобы минимизировать общий размер. Вот несколько примеров настроек:

Оптимизация CSS:

{
    type: "css",
    watch: ["tests/data/css", "tests/data/css2"],
    pack: ["tests/data/css", "tests/data/css2"],
    output: "tests/packed/styles.css",
    minify: true,
    exclude: ["header.css"]

Оптимизация JavaScript:

{
    type: "js",
    watch: "tests/data/js",
    pack: ["tests/data/js"],
    output: "tests/packed/scripts.js",
    minify: true,
    exclude: ["A.js"]
}

Оптимизация .less файлов

Оптимизация .less файлов производится немного по-другому. Здесь уже обязательно задавать параметры опции pack, в качестве которых, как правило, выступают точки входа. Вы должны будете импортировать все существующие .less файлы. Исключенные опции не будут доступны для обработки.

{
    type: "less",
    watch: ["tests/data/less"],
    pack: "tests/data/less/index.less",
    output: "tests/packed/styles-less.css",
    minify: true
}

Если у вас возникнут какие-то проблемы, пожалуйста, проверьте файл tests/packing-less.spec.js через специальный сервис на GitHub.

Оптимизация файлов других форматов

assets-pack может обрабатывать файлы любого формата. Например, мы можем объединить несколько шаблонов HTML в один файл. Для этого нужно задать настройки приблизительно таким образом:

{
    type: "html",
    watch: ["tests/data/tpl"],
    output: "tests/packed/template.html",
    exclude: ["admin.html"]
}

Однако вы должны учитывать, что в этом случае минимизация кода выполняться не будет.

Заключение

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

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

Перевод статьи «Web Assets – Tips for Better Organization and Performance» был подготовлен дружной командой проекта Сайтостроение от А до Я.