target: node и его отличия от web

Параметр target определяет, для какой среды выполнения Webpack должен собирать итоговый бандл. От выбранного значения зависит:

  • способ генерации модулей;
  • формат загрузки чанков;
  • работа с встроенными Node.js API;
  • поддержка import();
  • обработка __dirname, global, process;
  • наличие браузерных или серверных оптимизаций;
  • поведение runtime-кода Webpack.

Наиболее распространённые значения:

module.exports = {
  target: 'web'
};
module.exports = {
  target: 'node'
};

web используется для браузеров, node — для серверной среды Node.js.


Поведение target: web

target: 'web' считается значением по умолчанию.

Webpack предполагает, что код будет выполняться:

  • в браузере;
  • внутри DOM-среды;
  • с доступом к window;
  • без встроенных Node.js модулей;
  • с загрузкой чанков через <script>.

Пример:

module.exports = {
  target: 'web',
  entry: './src/index.js'
};

В этом режиме Webpack:

  • генерирует браузерный runtime;
  • использует JSONP-механизм загрузки чанков;
  • ориентируется на ES-модули браузеров;
  • исключает серверные оптимизации;
  • может автоматически внедрять некоторые полифилы (в старых версиях).

Поведение target: node

При использовании target: 'node' Webpack начинает генерировать код для выполнения внутри Node.js.

Пример:

module.exports = {
  target: 'node',
  entry: './src/server.js'
};

В этом режиме:

  • отключается браузерная логика;
  • чанки загружаются через require;
  • сохраняется работа __dirname;
  • поддерживаются встроенные модули Node.js;
  • runtime адаптируется под CommonJS;
  • отсутствует зависимость от DOM.

Главные отличия node от web

1. Работа с встроенными Node.js модулями

target: web

Браузер не поддерживает:

  • fs
  • path
  • os
  • net
  • tls
  • child_process

Поэтому код:

const fs = require('fs');

в браузерной сборке вызывает проблемы.

В Webpack 4 некоторые модули автоматически полифилились. В Webpack 5 это поведение удалено.

Ошибка:

Module not found: Error: Can't resolve 'fs'

target: node

Node.js уже содержит эти модули.

Webpack не пытается их заменять:

const fs = require('fs');

const text = fs.readFileSync('./file.txt', 'utf8');

Такой код корректно работает в серверной сборке.


2. Поведение __dirname и __filename

В браузере

В web Webpack эмулирует эти переменные.

Поведение отличается от настоящего Node.js.

Пример:

console.log(__dirname);

Может выводить:

/

или путь виртуальной сборки.


В Node.js

При target: 'node' сохраняется настоящее поведение:

console.log(__dirname);

Результат:

/home/project/dist

Это особенно важно:

  • для SSR;
  • файловых операций;
  • шаблонизаторов;
  • загрузки конфигураций;
  • работы с логами.

3. Загрузка чанков

web

В браузере чанки подгружаются динамически через <script>.

Webpack генерирует JSONP runtime.

Пример:

import('./module.js');

Превращается в механизм:

<script src="chunk.js"></script>

node

В Node.js динамические чанки подключаются через require.

Webpack создаёт серверный loader:

require("./chunk.js");

Это значительно быстрее и не требует сетевых запросов.


4. Глобальные объекты

В браузере

Доступны:

window
document
navigator
location

Но отсутствует:

global

В Node.js

Доступен:

global
process
Buffer

Но отсутствует DOM.

Код:

document.querySelector('.app');

в Node.js завершится ошибкой:

ReferenceError: document is not defined

5. Формат библиотек

web

Обычно используются:

  • UMD
  • var
  • module

Пример:

output: {
  libraryTarget: 'umd'
}

node

Чаще применяется:

output: {
  libraryTarget: 'commonjs2'
}

или:

output: {
  module: false
}

Node.js ориентирован на:

  • CommonJS;
  • ESM;
  • server-side runtime.

6. Оптимизация размера

web

Браузерные сборки должны быть минимальными.

Webpack активно:

  • минифицирует код;
  • удаляет dead code;
  • делит код на чанки;
  • оптимизирует lazy loading.

node

Для сервера размер менее критичен.

Главные цели:

  • скорость запуска;
  • удобство деплоя;
  • совместимость с Node.js;
  • отсутствие браузерных зависимостей.

Иногда серверные сборки вообще не минифицируются:

optimization: {
  minimize: false
}

7. Обработка динамического импорта

Browser runtime

В браузере:

const module = await import('./math.js');

Webpack создаёт асинхронную загрузку файла.


Node runtime

В Node.js используется локальная файловая система.

Webpack генерирует загрузку через:

require()

или внутренний runtime-обработчик.


8. External-зависимости

Для Node.js часто используется externals.

Пример:

module.exports = {
  target: 'node',
  
  externals: {
    express: 'commonjs express'
  }
};

Webpack не включает express в бандл.

В runtime Node.js сам загрузит пакет:

require('express');

Это уменьшает размер серверной сборки.


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

target: node активно применяется в:

  • Next.js;
  • Nuxt SSR;
  • Express SSR;
  • NestJS;
  • серверном рендеринге React/Vue.

Пример серверной точки входа:

import express from 'express';
import render from './render';

const app = express();

app.get('*', (req, res) => {
  res.send(render());
});

app.listen(3000);

Такой код предназначен только для Node.js.


Runtime Webpack для web и node

Runtime для браузера

Webpack генерирует:

  • JSONP loader;
  • script injection;
  • browser chunk map;
  • publicPath runtime.

Фрагмент runtime:

var script = document.createElement('script');

Runtime для Node.js

Для node runtime проще:

require("./chunk.js");

Отсутствуют:

  • DOM API;
  • <script>;
  • browser loaders.

Параметр externalsPresets

Webpack 5 добавил специальные пресеты.

Пример:

module.exports = {
  target: 'node',
  
  externalsPresets: {
    node: true
  }
};

Webpack автоматически учитывает:

  • встроенные Node.js модули;
  • CommonJS runtime;
  • server environment.

Различия в resolve.mainFields

В браузере

Приоритет:

resolve: {
  mainFields: ['browser', 'module', 'main']
}

Webpack пытается взять браузерную версию пакета.


В Node.js

Приоритет меняется:

resolve: {
  mainFields: ['module', 'main']
}

Используется серверная версия библиотеки.


Работа поля browser в package.json

Некоторые библиотеки содержат:

{
  "browser": {
    "./server.js": "./browser.js"
  }
}

При target: web

Webpack заменяет серверный файл браузерным.


При target: node

Замена не выполняется.

Используется настоящий серверный модуль.


Использование target: node для CLI-приложений

Webpack может собирать:

  • CLI;
  • daemon-процессы;
  • cron-задачи;
  • backend API.

Пример:

#!/usr/bin/env node

console.log('CLI app');

Конфигурация:

module.exports = {
  target: 'node'
};

Различия в HMR

Browser HMR

Для браузера Hot Module Replacement работает через:

  • WebSocket;
  • обновление <script>;
  • browser runtime.

Node HMR

В Node.js HMR сложнее.

Используются:

  • webpack/hot/poll;
  • server reload;
  • nodemon;
  • webpack-node-externals.

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

Electron содержит две среды:

  • renderer;
  • main process.

Renderer

Используется:

target: 'web'

или:

target: 'electron-renderer'

Main process

Используется:

target: 'node'

или:

target: 'electron-main'

Поддерживаемые значения target

Webpack поддерживает множество платформ.

Примеры:

target: 'web'
target: 'node'
target: 'webworker'
target: 'electron-main'
target: 'electron-renderer'
target: 'async-node'

async-node

Специальный вариант для асинхронной загрузки чанков в Node.js.

Пример:

target: 'async-node'

Чанки загружаются асинхронно через filesystem API.


Комбинирование target с Babel

Пример серверной сборки:

module.exports = {
  target: 'node',

  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'babel-loader'
      }
    ]
  }
};

Babel может транспилировать код под конкретную версию Node.js:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "node": "18"
        }
      }
    ]
  ]
}

Типичная серверная конфигурация

const path = require('path');

module.exports = {
  mode: 'production',

  target: 'node',

  entry: './src/server.js',

  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'server.js',
    libraryTarget: 'commonjs2'
  },

  externalsPresets: {
    node: true
  },

  optimization: {
    minimize: false
  }
};

Типичная браузерная конфигурация

const path = require('path');

module.exports = {
  mode: 'production',

  target: 'web',

  entry: './src/index.js',

  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  }
};

Когда использовать target: web

Подходит для:

  • SPA;
  • frontend;
  • React;
  • Vue;
  • Angular;
  • браузерных библиотек;
  • клиентских виджетов.

Когда использовать target: node

Подходит для:

  • backend;
  • SSR;
  • CLI;
  • микросервисов;
  • API;
  • серверных приложений;
  • Electron main process;
  • cron-задач;
  • утилит автоматизации.

Основные проблемы при неправильном target

Использование web вместо node

Ошибки:

Can't resolve 'fs'
document is not defined
process is not defined

Использование node вместо web

Ошибки:

window is not defined
navigator is not defined

или невозможность загрузки браузерных чанков.


Влияние target на tree shaking

web обычно агрессивнее оптимизирует:

  • ESM;
  • sideEffects;
  • browser bundles.

В node Webpack чаще сохраняет CommonJS-поведение для совместимости.


Влияние target на output environment

Webpack адаптирует runtime под платформу.

Например:

output: {
  environment: {
    arrowFunction: false
  }
}

Для Node.js и браузеров требования могут различаться.


Сравнение web и node

Возможность web node
DOM Есть Нет
fs/path Нет Есть
window Есть Нет
process Ограничено Есть
Загрузка чанков <script> require()
Runtime Browser CommonJS
SSR Нет Да
JSONP Да Нет
CLI Нет Да
Browser field Используется Игнорируется
Размер критичен Да Реже
Built-in Node modules Нет Есть