Всем привет!

Начну с небольшого рассказа.

В своем новом проекте я решил использовать Vue.js. Мне понадобился рендеринг на стороне сервера (SSR), модули CSS, разделение кода и другие интересные вещи. Конечно, для быстрой разработки мне потребовалась горячая замена модулей (HMR).

Я не хотел использовать высокоуровневые фреймворки, такие как Nuxt.js, потому что возможность настройки очень важна, когда проект будет огромным. Но никакие высокоуровневые фреймворки не позволяют этого (у меня аналогичный опыт работы с Next.js для React).

Основная проблема локальной разработки с использованием рендеринга на стороне сервера и горячей перезагрузки заключается в том, что вам недостаточно запустить только webpack-dev-server. Вы должны смотреть и перекомпилировать исходные коды, которые выполняет Node.js, иначе код сервера не будет обновлен, но будет обновлен код клиента.

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

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

  • VueJS
  • ССР
  • Vuex
  • CSS-модули
  • Разделение кода
  • ESLint, красивее

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

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

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

Обратите внимание, что Vue.js имеет очень глубокую документацию по настройке SSR. Вы тоже должны это проверить.

Сторона сервера

Итак, для сервера Node.js мы будем использовать Express, и нам понадобится vue-server-renderer. Этот пакет позволяет нам отображать код в html-строку в соответствии с пакетом сервера, html-шаблоном и клиентским манифестом, который содержит имена и пути к ресурсам.

Файл результата server.js будет:

Как видите, у нас есть 2 файла: vue-ssr-server-bundle.json и vue-ssr-client-manifest.json. Они генерируются во время сборки; первый файл содержит код, который будет выполняться на сервере, второй содержит имена и пути к ресурсам.

Кроме того, у параметров есть свойство inject: false. Это означает, что мы будем вводить ресурсы вручную, потому что нам нужен более точный контроль.

Шаблон выглядит так:

  • meta.inject (). title.text () и meta.inject (). meta.text () необходимы для создания заголовка и мета-описания. У него есть пакет vue-meta, об этом я расскажу вам позже.
  • renderResourceHints () - отображает ссылки rel = ”preload / prefetch” на ресурсы из клиентского манифеста.
  • renderStyles () - отображает ссылки на стили из клиентского манифеста.
  • renderState () - отображает состояние в окне переменных по умолчанию .__ INITIAL_STATE__
  • renderScripts () - отображает скрипты для нашего приложения.

Комментарий ‹! - vue-ssr-outlet - ›будет заменен на html-код приложения. Требуется комментарий.

Точкой входа в наше приложение Vue является файл entry-server.js.

Сторона клиента

Точка входа клиента - файл entry-client.js.

Модуль app.js создает экземпляр Vue, который используется на клиенте и сервере.

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

App.vue - корневой компонент, содержащий тег ‹router-view› ‹/router-view›, который будет внедрять компоненты в соответствии с маршрутом.

Маршрутизатор выглядит так:

С Vue.use мы соединяем два плагина Router и VueMeta.

В маршрутах мы указываем компоненты как

() => import('./client/components/About.vue')

Это необходимо для разделения кода.

Что касается управления состоянием (с Vuex), в настройках нет ничего особенного. Во-первых, я разделил хранилище на модули и использую константы с именами, чтобы упростить навигацию по коду.

Теперь рассмотрим нюансы компонентов Vue.

Свойство metaInfo отвечает за мета-рендеринг с использованием пакета vue-meta. Мы можем указать множество свойств (подробнее).

metaInfo: {
  title: 'Main page',
}

Компоненты имеют метод, который выполняется только на стороне сервера.

serverPrefetch() {
  console.log('Run only on server');
}

Также я хотел использовать модули CSS. Мне нравится идея, что вам не нужно заботиться об именах классов, чтобы избежать пересечения между компонентами. Если вы используете модули CSS, результирующий класс будет выглядеть как ‹имя класса› _ ‹hash›.

Для этого вы должны указать в компоненте style module.

<style module>
.item {
  padding: 3px 0;
}

.controls {
  margin-top: 12px;
}
</style>

А в шаблоне следует указать атрибут : class.

<div :class="$style.item"></div>

Также в конфиге webpack необходимо указать, что мы будем использовать модули.

Пучок

Рассмотрим конфиги webpack.

У нас есть базовый конфиг, который расширяется конфигами серверов и клиентов.

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

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

Но я столкнулся с проблемой, документ появился на стороне сервера и, конечно же, у меня возникла ошибка. Это была проблема мини-css-extract-plugin и была решена разделением конфигурации сборки CSS для клиента и сервера.

VueSSRServerPlugin создает файл vue-ssr-server-bundle.json, который содержит код выполнения сервера.

Теперь рассмотрим конфигурацию клиента.

Для локальной разработки мы указываем publicPath, связанный с webpack-dev-server, и генерируем имя файла без хеша. Также для devServer мы указываем свойство writeToDisk: true.

Требуется объяснение.

По умолчанию webpack-dev-server обслуживает ресурсы из памяти, а не записывает их на диск. В этом случае возникает проблема: манифест клиента (vue-ssr-client-manifest.json), который находится на диске, будет содержать устаревшие ресурсы, потому что он не будет обновлен. Чтобы этого избежать, dev-сервер должен записывать изменения на диск, тогда манифест клиента будет обновлен и будут использоваться фактические ресурсы.

На самом деле, я бы хотел от этого избавиться. Одно из решений - в режиме разработки мы можем включить манифест из url-адреса dev-сервера вместо папки / dist. Но это асинхронная операция. Буду рад получить чистое решение проблемы.

Для перезагрузки серверного процесса мы используем Nodemon, который следит за двумя файлами: dist / vue-ssr-server-bundle.json и app / server.js и перезагружается. app, то эти файлы были изменены.

Чтобы иметь возможность перезапускать приложение для изменений server.js, мы не указываем этот файл в качестве точки входа в nodemon. Вместо этого мы создали файл nodemon.js, который включает server.js. Теперь точкой входа является файл nodemon.js.

В рабочем режиме точкой входа является app / server.js.

Выводы

Итак, у нас есть репо с настройками и несколькими задачами.

Для местного развития:

yarn run dev

На стороне клиента: он запускает webpack-dev-server, который следит за изменениями компонентов Vue и js-кода и генерирует клиентский манифест с путями ресурсов к dev-серверу, сохраняет его на диск и обновляет код, стили в браузер в реальном времени.

На стороне сервера: он запускает webpack в качестве наблюдателя, создает пакет сервера (vue-ssr-server-bundle.json) и перезапускает приложение, после чего оно изменяется.

В этом случае код будет последовательно обновляться на клиенте и сервере автоматически.

Обратите внимание: если вы запустите его в первый раз, вы получите сообщение об ошибке: этот комплект сервера не найден.

Это нормально. Просто запустите эту задачу еще раз.

Для производственной сборки:

yarn run build

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

На стороне сервера: он создает пакет сервера.

Также я создал задачу yarn run start-node, которая запускает server.js, но это только для примера. В производственном приложении вы должны использовать диспетчер процессов, например PM2.

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

Полезные ссылки