Оптимизация одностраничных приложений: 3 основных подхода

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

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

Один из основных недостатков СПА - низкая скорость загрузки. Один пакетный файл со всем приложением требует времени для первой загрузки (особенно для больших приложений), и, поскольку никто не любит ждать, вы можете потерять клиентов из-за этого. Но с Webpack это больше не проблема. Webpack можно настроить довольно гибко, что позволяет в большинстве случаев избавиться от проблемы медленной загрузки. В этом руководстве мы будем использовать Webpack 3.11.0.

Прочтите наш Фотоблог и узнайте, как мы оптимизировали производительность сайта и увеличили скорость загрузки страницы.

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

1. Используйте CommonsChunkPlugins.

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

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

Представим, что у нас есть следующий проект:

Здесь есть index.js, profile.js, и user.js.. Допустим, index.js и profile.js требуется user.js.

Мы хотим, чтобы index.js и profile.js работали отдельно на разных страницах. Обычно мы пишем что-то вроде этого:

const webpack = require('webpack');

module.exports = {

    context: __dirname + '/scripts',

    entry: {
        index: './index',
        profile: './profile'
    },

    output: {
        path: __dirname + '/app/public',
        filename: '[name].js'
    }

};

Это создаст следующую структуру в общей папке:

Если user.js требуется как в index.js, так и в profile.js, при такой конфигурации Webpack будет вставлять user.js в оба файла, а код в конечном приложении будет дублироваться, что плохо. Мы хотим выделить часто используемые части в отдельный файл, чтобы браузер мог загружать и кэшировать файл.

Давайте оптимизируем это с помощью CommonsChunkPlugins. Наша конфигурация Webpack для этой цели будет:

const webpack = require('webpack');

module.exports = {

    context: __dirname + '/app/scripts',

    entry: {
        index: './index.js',
        profile: './profile.js'
    },

    output: {
        path: __dirname + '/app/public',
        filename: '[name].js'
    },

    plugins: [
        new webpack.optimize.CommonsChunkPlugin({
            name: 'common'
        })
    ]

};

Посмотрим, что изменилось:

Теперь вы видите, что появился файл common.js. Этот файл содержит все общие require () для всех наших скриптов. Кроме того, размер файлов был значительно уменьшен.

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

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

Https://webpack.js.org/plugins/commons-chunk-plugin/

2. Динамический require ()

Может быть плохой идеей загружать все ваши функции, независимо от того, будут ли они использоваться. Мы предпочитаем загружать вещи только по запросу. Когда ваше приложение становится очень большим, вам обязательно стоит подумать о разделении скриптов и загрузке частей по запросу. До автоматизации в JavaScript требовалось разработать собственные методы для динамической вставки новых файлов с тегом <script> в документ. К счастью, теперь Webpack может служить этой цели, и это происходит автоматически. Но помните, что обычный require () не позволяет загружать файлы на основе правил. Webpack просто добавит его в пакет или удалит все.

Итак, если вы напишете что-то вроде:

const location = window.location.pathname;

switch(location) {

    case 'user':
        const user = require('./user');
        user();
        break;

    case 'profile':
        const profile = require('./profile');
        profile();
        break;

    default: alert('No match'); break;

}

Вы можете ожидать, что файлы будут загружаться динамически по request (), но этого не произойдет. Вместо этого будет создан один файл со всеми необходимыми скриптами независимо от правил, поскольку Webpack не будет анализировать переключатель и сами правила.

К счастью, для этой цели Webpack предоставляет исходное требование с функцией sure (), которая имеет следующий синтаксис:

const location = window.location.pathname;

switch(location) {

    case 'user':
        require.ensure(['./user'], function(require) {
            const user = require('./user');
            user();
        });
        break;

    case 'profile':
        require.ensure(['./profile'], function(require) {
            const profile = require('./profile');
            profile();
        });
        break;

    default: alert('No match'); break;

}

После сборки получаем:

Webpack создает файлы фрагментов 0.js и 1.js. Main.js не будет содержать этих файлов, и они будут загружены только по запросу - в нашем случае, если имя файла совпадает с URL-адресом. Поскольку они находятся на сервере, не забудьте указать publicpath для выходного параметра в config.

Мы также опишем ситуацию для разработчиков React, которые используют библиотеку Router для управления рендерингом. Было бы здорово загрузить файл с компонентами, если они соответствуют какой-то части URL. Проблема в том, что нет удобного способа использовать require.ensure () в <Route> компоненте. Для этого мы используем react-loadable (https://github.com/jamiebuilds/react-loadable). С этой библиотекой ваши маршруты будут выглядеть так:

<Router>
    <Route path="/profile" render={() => {
        const Profile = Loadable({
            loader: () => import('./profile'),
            loading: Loading
    });
        return <Profile/>;
    }}/>
</Router>

Кроме того, иногда вы можете написать что-то вроде этого:

const location = window.location.pathname;

require.ensure(['/somepath/' + location], function(require) {
    const script = require('/somepath/' + location);
    script();
});

Этот случай называется динамическим требованием. Поскольку имя файла хранится в переменной, Webpack слишком сложно обнаружить этот файл и создать чанки. Вам нужно будет создать промежуточный файл с помощью static require.ensure () или использовать bundle-loader. Мы предпочитаем второй вариант: https://github.com/webpack-contrib/bundle-loader

3. Проанализируйте свою сборку.

В этом разделе мы покажем вам (или напомним), как использовать инструменты Webpack для анализа.

Сначала воспользуйтесь ключом - display-modules. Это отобразит информацию о том, как ваша сборка хранит модули. Если мы используем его в нашем предыдущем примере с CommonsChunkPlugins, он покажет следующее:

Ты это видишь:

profile.js перешел к блоку {0}, который является записью profile.js.

index.js перешел в блок {1}, который представляет собой index.js (также отдельная запись).

user.js перешел к блоку {2}, который является common.js, поскольку этот скрипт используется обоими index.js и profile.js

Но вы можете пойти еще глубже с помощью -display-modules - verbose. Эта команда также покажет вам, почему сборка была собрана именно так.

Вот подробная информация о том, почему мы получили такой common.js и что он содержит.

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

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

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

Этот файл report.json содержит всю необходимую информацию о вашей сборке.

Затем перейдите на http://webpack.github.io/analyse и загрузите report.json.

В итоге вы получите вот что:

Этот инструмент покажет вам информацию о модулях, чанках, активах, предупреждениях и ошибках.

Анализ поможет вам избежать дублирования и сохранить все, как ожидалось.

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