@dannyfromtheblock

Как увеличить скорость сборки многостраничного проекта (Webpack, Sass, Pug)?

Верстка большого проекта.
Для реализации были выбраны Pug и Sass (SCSS).
35 страниц. Активно используются инклюды и миксины.
В остальном все обычно — стили, картинки (умеренное количество), скрипты.

Первоначальная сборка (запуск) в режиме development занимает ~10 минут. В режиме production — ~6 минут.

Даже если собирать 1 страницу в режиме development, то все равно занимает много времени — ~6 минут.

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


Скриншот лога плагина Speed Measure при сборке проекта со всеми страницами:
5f8709a263242944063361.png

Пробовал собирать без pug-файлов и скорость остальных лоадеров сразу увеличивалась. Pug все тормозит. Также пробовал без style-loader'а (который позволяет без перезагрузки обновлять стили), но увы.

Конфиг Webpack'а

const path = require("path");
const webpack = require("webpack");
const fs = require("fs");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const SpriteLoaderPlugin = require("svg-sprite-loader/plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const DuplicatePackageCheckerPlugin = require("duplicate-package-checker-webpack-plugin");
const TerserJSPlugin = require("terser-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");

const paths = {
  src: path.resolve(__dirname, "src"),
  build: path.resolve(__dirname, "build"),
};

const smp = new SpeedMeasurePlugin();

module.exports = (env, argv) => {
  return smp.wrap({
    entry: {
      index: "./src/index.js",
    },
    output: {
      path: paths.build,
      filename: "[name].[hash].js",
    },
    optimization: {
      minimize: argv.mode !== "development",
      minimizer: [
        new TerserJSPlugin({
          cache: true,
          parallel: true,
        }),
      ],
      splitChunks: {
        cacheGroups: {
          vendor: {
            test: /[\\/]node_modules[\\/].*\.js$/,
            filename: "vendor.[hash].js",
            chunks: "all",
          },
        },
      },
    },
    resolve: {
      alias: {
        "@": paths.src,
      },
    },
    module: {
      rules: [
        {
          test: require.resolve("jquery"),
          use: [
            {
              loader: "expose-loader",
              options: {
                exposes: ["$", "jQuery"],
              },
            },
          ],
        },
        {
          test: /\.js$/,
          exclude: /node_modules/,
          use: [
            {
              loader: "cache-loader",
            },
            {
              loader: "thread-loader",
            },
            {
              loader: "babel-loader",
              options: {
                presets: ["@babel/preset-env"],
              },
            },
          ],
        },
        {
          test: /\.pug$/,
          use: [
            {
              loader: "pug-loader",
              options: {
                pretty: argv.mode !== "development",
              },
            },
          ],
        },
        {
          test: /\.scss$/,
          exclude: /node_modules/,
          use: [
            argv.mode === "development"
              ? {
                  loader: "style-loader",
                }
              : {
                  loader: MiniCssExtractPlugin.loader,
                  options: {
                    publicPath: "../",
                  },
                },
            "css-loader",
            "postcss-loader",
            "fast-sass-loader",
          ],
        },
        {
          test: /\.svg$/,
          include: /icons/,
          use: [
            {
              loader: "svg-sprite-loader",
              options: {
                runtimeCompat: true,
              },
            },
            "svg-transform-loader",
            "svgo-loader",
          ],
        },
        {
          test: /\.(gif|png|jpe?g)$/i,
          include: /ill/,
          use: [
            {
              loader: "responsive-loader",
              options: {
                name: "[name]-[width].[ext]",
                outputPath: "images",
              },
            },
            {
              loader: "image-webpack-loader",
              options: {
                mozjpeg: {
                  progressive: true,
                  quality: 65,
                },
                optipng: {
                  enabled: false,
                },
                pngquant: {
                  quality: [0.8, 1],
                },
                gifsicle: {
                  interlaced: false,
                },
              },
            },
          ],
        },
        {
          test: /\.(svg)$/i,
          include: /ill/,
          use: [
            {
              loader: "file-loader",
              options: {
                name: "[name].[ext]",
                outputPath: "images",
              },
            },
          ],
        },
        {
          test: /\.(eot|ttf|woff|woff2)$/,
          use: [
            {
              loader: "file-loader",
              options: {
                name: "fonts/[name].[ext]",
              },
            },
          ],
        },
        {
          test: /\.(mp4)$/,
          use: [
            {
              loader: "file-loader",
              options: {
                name: "video/[name].[ext]",
              },
            },
          ],
        },
      ],
    },
    plugins: [
      new webpack.ProvidePlugin({
        $: "jquery",
        jQuery: "jquery",
        "window.jQuery": "jquery",
      }),
      new MiniCssExtractPlugin({
        filename: "./css/[name].[hash].css",
        chunkFilename: "[name].[hash].css",
      }),
      new SpriteLoaderPlugin({
        plainSprite: true,
      }),
      new CopyWebpackPlugin({
        patterns: [
          {
            from: "static",
            to: "static",
          },
        ],
      }),
      new CleanWebpackPlugin({
        dry: true,
        cleanOnceBeforeBuildPatterns: ["build/*"],
      }),
      new DuplicatePackageCheckerPlugin(),
      ...fs
        .readdirSync(path.resolve(__dirname, "src/template/pages"))
        .filter((fileName) =>
          fileName.endsWith(".pug") && env !== undefined && env.pages.length
            ? env.pages.split(",").includes(fileName)
            : true
        )
        .map(
          (page) =>
            new HtmlWebpackPlugin({
              minify: false,
              template: `${paths.src}/template/pages/${page}`,
              filename: `./${page.replace(/\.pug/, ".html")}`,
            })
        ),
    ],
    devServer: {
      watchOptions: {
        ignored: /node_modules/,
      },
      hot: true,
      port: process.env.PORT,
      overlay: {
        errors: false,
        warnings: false,
      },
    },
  });
};

  • Вопрос задан
  • 290 просмотров
Пригласить эксперта
Ответы на вопрос 1
profesor08
@profesor08
Используй create-react-app. Создай с его помощью приложение. Создай роутер. Наделай компонентов. Разрабатывай спокойно. Когда надо будет собрать, чтоб получишь html, используй react-app-rewired и настрой под него webpack.config, где будет использоваться prerender-spa-plugin
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы