Налаштування бандлера Webpack для веб-проекту

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Налаштування бандлера Webpack для веб-проекту
Середня
від 1 робочого дня до 3 робочих днів
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Налаштування бандлера Webpack для веб-проекту

Webpack — найбільш гнучкий і широко підтримуваний бандлер для фронтенду. Підтримує будь-який стек, будь-які трансформації, Module Federation, складні стратегії split-chunks. Вимагає конфігурації, але поведінка передбачувана і добре документована.

Використовується там, де потрібні можливості, недоступні у Vite: Module Federation, складний code splitting, нестандартні лоадери, підтримка IE11 (якщо все ще актуально), інтеграція з існуючими webpack-плагінами.

Що входить до роботи

Налаштування webpack.config.ts для dev та prod, Babel/SWC, TypeScript, CSS/PostCSS, активи, code splitting, tree shaking, аналіз bundle, dev server з HMR.

Встановлення

npm install -D webpack webpack-cli webpack-dev-server
npm install -D html-webpack-plugin mini-css-extract-plugin css-minimizer-webpack-plugin
npm install -D terser-webpack-plugin compression-webpack-plugin
npm install -D babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript
# або SWC замість Babel (швидше)
npm install -D swc-loader @swc/core @swc/helpers

webpack.config.ts — базова структура

import path from 'path'
import webpack from 'webpack'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'
import TerserPlugin from 'terser-webpack-plugin'
import CompressionPlugin from 'compression-webpack-plugin'

const isDev = process.env.NODE_ENV !== 'production'
const root = path.resolve(__dirname)

const config: webpack.Configuration = {
  mode: isDev ? 'development' : 'production',

  entry: {
    main: './src/index.tsx',
  },

  output: {
    path: path.resolve(root, 'dist'),
    filename: isDev ? '[name].js' : '[name].[contenthash:8].js',
    chunkFilename: isDev ? '[name].chunk.js' : '[name].[contenthash:8].chunk.js',
    assetModuleFilename: 'assets/[hash][ext][query]',
    publicPath: '/',
    clean: true,
  },

  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx'],
    alias: {
      '@': path.resolve(root, 'src'),
      '@components': path.resolve(root, 'src/components'),
      '@hooks': path.resolve(root, 'src/hooks'),
      '@utils': path.resolve(root, 'src/utils'),
    },
  },

  module: {
    rules: [
      // TypeScript + React через SWC
      {
        test: /\.(ts|tsx|js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'swc-loader',
          options: {
            jsc: {
              parser: { syntax: 'typescript', tsx: true },
              transform: {
                react: {
                  runtime: 'automatic',
                  development: isDev,
                  refresh: isDev,
                },
              },
              target: 'es2020',
            },
          },
        },
      },
      // CSS + Modules + PostCSS
      {
        test: /\.css$/,
        use: [
          isDev ? 'style-loader' : MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              modules: {
                auto: /\.module\.css$/,
                localIdentName: isDev ? '[local]--[hash:base64:5]' : '[hash:base64:8]',
              },
              importLoaders: 1,
            },
          },
          'postcss-loader',
        ],
      },
      // Статичні активи
      {
        test: /\.(png|jpg|webp|gif|svg)$/,
        type: 'asset',
        parser: {
          dataUrlCondition: { maxSize: 4 * 1024 }, // < 4kb → inline
        },
      },
      {
        test: /\.(woff2?|ttf|eot)$/,
        type: 'asset/resource',
      },
    ],
  },

  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html',
      favicon: './public/favicon.ico',
      minify: !isDev,
    }),
    !isDev && new MiniCssExtractPlugin({
      filename: 'css/[name].[contenthash:8].css',
      chunkFilename: 'css/[name].[contenthash:8].chunk.css',
    }),
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
      'process.env.API_URL': JSON.stringify(process.env.API_URL),
    }),
    !isDev && new CompressionPlugin({
      algorithm: 'brotliCompress',
      test: /\.(js|css|html|svg)$/,
      threshold: 10240,
    }),
  ].filter(Boolean),

  optimization: {
    minimize: !isDev,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: { drop_console: true },
          format: { comments: false },
        },
        extractComments: false,
      }),
      new CssMinimizerPlugin(),
    ],
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/](react|react-dom|react-router-dom)[\\/]/,
          name: 'vendor-react',
          chunks: 'all',
          priority: 20,
        },
        commons: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendor-commons',
          chunks: 'all',
          priority: 10,
          minChunks: 2,
        },
      },
    },
    runtimeChunk: 'single',
    moduleIds: isDev ? 'named' : 'deterministic',
    chunkIds: isDev ? 'named' : 'deterministic',
  },

  devServer: {
    port: 3000,
    hot: true,
    historyApiFallback: true,
    compress: true,
    proxy: [
      {
        context: ['/api'],
        target: 'http://localhost:8000',
        changeOrigin: true,
      },
    ],
    client: {
      overlay: { errors: true, warnings: false },
    },
  },

  devtool: isDev ? 'eval-cheap-module-source-map' : 'source-map',

  performance: {
    hints: isDev ? false : 'warning',
    maxAssetSize: 250_000,
    maxEntrypointSize: 500_000,
  },
}

export default config

postcss.config.js

module.exports = {
  plugins: [
    require('postcss-import'),
    require('tailwindcss'),
    require('autoprefixer'),
    !process.env.DEV && require('cssnano')({ preset: 'default' }),
  ].filter(Boolean),
}

.swcrc (альтернатива — окремий файл конфігу SWC)

{
  "$schema": "https://json.schemastore.org/swcrc",
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "tsx": true,
      "decorators": true
    },
    "transform": {
      "react": {
        "runtime": "automatic"
      },
      "legacyDecorator": true
    },
    "target": "es2020",
    "loose": false,
    "externalHelpers": true
  },
  "module": {
    "type": "es6"
  }
}

Аналіз bundle

npm install -D webpack-bundle-analyzer
// додати до plugins при ANALYZE=true
process.env.ANALYZE && new BundleAnalyzerPlugin({
  analyzerMode: 'static',
  reportFilename: 'bundle-report.html',
  openAnalyzer: false,
  generateStatsFile: true,
})
ANALYZE=true npm run build
# відкриваємо dist/bundle-report.html

Lazy loading маршрутів

// src/App.tsx
const ProductsPage = lazy(() =>
  import(/* webpackChunkName: "products" */ './pages/ProductsPage')
)
const CheckoutPage = lazy(() =>
  import(/* webpackChunkName: "checkout" */ './pages/CheckoutPage')
)

// prefetch при idle
const AdminPage = lazy(() =>
  import(/* webpackChunkName: "admin", webpackPrefetch: true */ './pages/AdminPage')
)

TypeScript-шляхи в tsconfig

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@components/*": ["src/components/*"],
      "@hooks/*": ["src/hooks/*"]
    }
  }
}

Шляхи в tsconfig потрібно дублювати в resolve.alias — webpack не читає tsconfig автоматично.

Змінні окружння через .env

npm install -D dotenv-webpack
import Dotenv from 'dotenv-webpack'

plugins: [
  new Dotenv({
    path: `.env.${process.env.NODE_ENV}`,
    safe: true, // перевіряє .env.example
    systemvars: true, // env vars з окружння мають пріоритет
  }),
]

Скрипти в package.json

{
  "scripts": {
    "dev": "webpack serve --mode development",
    "build": "webpack --mode production",
    "build:analyze": "ANALYZE=true webpack --mode production",
    "typecheck": "tsc --noEmit"
  }
}

Що ми робимо

Налаштовуємо webpack із SWC (або Babel) для TypeScript + React, CSS Modules, PostCSS, оптимізуємо code splitting під конкретний проект, налаштовуємо dev server з proxy на backend, додаємо bundle analyzer, налаштовуємо стиснення brotli для production.

Терміни: 1–3 дні залежно від складності стека та кількості нестандартних вимог.