# Конфигурация сборки
Конфигурация webpack для проекта с SSR аналогична конфигурации проекта только для клиентской стороны. Если ещё не знакомы с настройкой webpack, более подробную информацию можно найти в документации Vue CLI (opens new window) или настроить Vue Loader вручную (opens new window).
# Ключевые отличия от сборки только для клиента
Необходимо создать манифест webpack (opens new window) для серверной части кода. Это JSON-файл, который webpack держит для отслеживания того, как все исходные модули сопоставляются с получившимися файлами сборки.
Требуется экстернализация зависимостей приложения (opens new window). Это делает сборку сервера намного быстрее и создаёт меньший по размеру файл. При этом необходимо исключить зависимости, которые должны обрабатываться webpack (например,
.css
или.vue
).Нужно переключить target (opens new window) в webpack на Node.js. Это позволит webpack обрабатывать динамические импорты в соответствии с подходом в Node, а также укажет
vue-loader
выдавать серверно-ориентированный код при компиляции компонентов Vue.При создании серверной точки входа, необходимо определить переменную окружения, указывающую что работаем с SSR. Удобно добавить несколько записей в
scripts
вpackage.json
проекта для этого:
"scripts": {
"build:client": "vue-cli-service build --dest dist/client",
"build:server": "SSR=1 vue-cli-service build --dest dist/server",
"build": "npm run build:client && npm run build:server",
}
2
3
4
5
# Пример конфигурации
Ниже приведён пример vue.config.js
, который добавляет SSR в проект Vue CLI, но его можно адаптировать для любой сборки webpack.
const { WebpackManifestPlugin } = require('webpack-manifest-plugin')
const nodeExternals = require('webpack-node-externals')
const webpack = require('webpack')
module.exports = {
chainWebpack: webpackConfig => {
// Необходимо отключать cache-loader, иначе в сборке для клиента
// будут использоваться кэшированные компоненты из сборки для сервера
webpackConfig.module.rule('vue').uses.delete('cache-loader')
webpackConfig.module.rule('js').uses.delete('cache-loader')
webpackConfig.module.rule('ts').uses.delete('cache-loader')
webpackConfig.module.rule('tsx').uses.delete('cache-loader')
if (!process.env.SSR) {
// Определяем точку входа клиентской части приложения
webpackConfig
.entry('app')
.clear()
.add('./src/entry-client.js')
return
}
// Определяем точку входа серверной части приложения
webpackConfig
.entry('app')
.clear()
.add('./src/entry-server.js')
// Это позволяет webpack обрабатывать динамические импорты в соответствии
// с подходом в Node, а также указывает `vue-loader` выдавать
// серверно-ориентированный код при компиляции компонентов Vue.
webpackConfig.target('node')
// Это указывает сборке для сервера использовать экспорты в стиле Node
webpackConfig.output.libraryTarget('commonjs2')
webpackConfig
.plugin('manifest')
.use(new WebpackManifestPlugin({ fileName: 'ssr-manifest.json' }))
// https://webpack.js.org/configuration/externals/#function
// https://github.com/liady/webpack-node-externals
// Экстернализация зависимостей приложения. Это сделает сборку для сервера
// гораздо быстрее и создаст более лёгкий файл итоговой сборки.
// Не нужно экстернализировать зависимости, которые должны обрабатываться webpack.
// Также следует внести в белый список зависимости, которые изменяют `global` (например, полифилы)
webpackConfig.externals(nodeExternals({ allowlist: /\.(css|vue)$/ }))
webpackConfig.optimization.splitChunks(false).minimize(false)
webpackConfig.plugins.delete('preload')
webpackConfig.plugins.delete('prefetch')
webpackConfig.plugins.delete('progress')
webpackConfig.plugins.delete('friendly-errors')
webpackConfig.plugin('limit').use(
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1
})
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# Предостережения для externals
Обратите внимание, что в опции externals
в белый список добавляются CSS-файлы. Это связано с тем, что CSS импортируемый из зависимостей, всё равно должен обрабатываться webpack. Если импортируете файлы других типов, которые также полагаются на webpack (например, *.vue
, *.sass
), их тоже следует добавить в белый список.
При использовании runInNewContext: 'once'
или runInNewContext: true
необходимо добавить в белый список полифилы, изменяющие global
(например, babel-polyfill
). Это требуется для того, что при использовании режима нового контекста код внутри сборки для сервера будет иметь свой собственный объект global
. Поскольку на сервере он не нужен, проще просто импортировать его в файл клиентской точки входа.
# Генерация clientManifest
В дополнение к серверной сборке также можно сгенерировать манифест клиентской сборки. Благодаря клиентскому манифесту и серверной сборке, рендерер будет иметь информацию как о серверной так и о клиентской сборках. Благодаря этому он сможет автоматически определять и внедрять директивы для preload / prefetch (opens new window), теги <link>
и <script>
в создаваемый HTML.
Выгода от этого двойная:
Он заменяет
html-webpack-plugin
для внедрения корректных URL ресурсов, когда в именах генерируемых файлов присутствуют хэши.При генерации сборки, которая использует возможности webpack по разделению кода, можно обеспечить preload / prefetch необходимых фрагментов, а также интеллектуально внедрять теги
<script>
для требуемых асинхронных фрагментов, чтобы избежать водопада запросов на клиенте, тем самым улучшая TTI (time-to-interactive).