Медленный sass-загрузчик Build Times с Webpack - PullRequest
2 голосов
/ 22 мая 2019

Резюме

Когда мы перешли на использование Webpack для обработки наших файлов SASS, мы заметили, что время сборки в некоторых случаях становилось очень медленным. После измерения производительности различных частей сборки с помощью SpeedMeasurePlugin кажется, что sass-loader является виновником ... сборка может легко занять 10 секунд (раньше было 20 секунд до мы сделали несколько исправлений), что дольше, чем хотелось бы.

Мне любопытно, есть ли у людей дополнительные стратегии оптимизации создания активов Sass, которые я не рассматривал. На данный момент я прошел через значительное количество из них (несколько раз для некоторых из них), и до сих пор не могу добиться того, чтобы время сборки было достаточно низким. С точки зрения целей, в настоящее время большая перестройка (например, внесение изменений в компонент, используемый во многих файлах), может легко занять 10-12 секунд, я надеюсь сократить его до 5 с, если это возможно.

Решения, пробованные

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

  • HardSourcePlugin - Этот работал достаточно хорошо. В зависимости от кэширования для этой сборки, она могла сократить время сборки на несколько секунд.
  • Удаление дублированного импорта (например, импорт одного и того же файла variables.sass в несколько мест) - это также сократило время сборки на несколько секунд
  • Меняя наше сочетание SASS и SCSS на просто SCSS - я не уверен, почему это помогло, но, похоже, это немного сбило наше время сборки. Может быть, из-за того, что все файлы одного и того же типа, их было проще скомпилировать? (Возможно, что-то еще происходило здесь, чтобы запутать результаты, но, похоже, оно постоянно помогало).
  • Замените sass-loader на fast-sass-loader - Многие рекомендовали это, но когда я запустил его, похоже, что время сборки вообще не изменилось. Я не уверен, почему ... возможно, была проблема с конфигурацией.
  • Использование кеш-загрузчика - Похоже, это тоже не принесло никаких улучшений.
  • Отключенные исходные карты для Sass - похоже, это имело большой эффект, сократив время сборки вдвое (с момента применения изменения)
  • Пробовал использовать includePaths для SASS, загруженной из node_modules - это было предложено для проблемы git Я обнаружил, где у sass-loader были проблемы с тем, что они называли «пользовательскими импортерами». Насколько я понимаю, используя includePaths, SASS мог полагаться на предоставленные абсолютные пути вместо использования неэффективного алгоритма для разрешения путей к таким местам, как node_modules

Судя по некоторым кратким статистическим данным, мы имеем около 16 тыс. Строк кода SASS, распределенных по 150 файлам SASS. Некоторые из них имеют достаточное количество кода, в то время как другие имеют меньше, с простым средним значением LOC для этих файлов около 107 LOC / файл.

Ниже приведена конфигурация, которая используется. Приложение является приложением Rails, и большая часть конфигурации Webpack обрабатывается через гем Webpacker.

{
  "mode": "production",
  "output": {
    "filename": "js/[name].js",
    "chunkFilename": "js/[name].js",
    "hotUpdateChunkFilename": "js/[id]-[hash].hot-update.js",
    "path": "myApp/public/packs",
    "publicPath": "/packs/"
  },
  "resolve": {
    "extensions": [".mjs", ".js", ".sass", ".scss", ".css", ".module.sass", ".module.scss", ".module.css", ".png", ".svg", ".gif", ".jpeg", ".jpg"],
    "plugins": [{
      "topLevelLoader": {}
    }],
    "modules": ["myApp/app/assets/javascript", "myApp/app/assets/css", "node_modules"]
  },
  "resolveLoader": {
    "modules": ["node_modules"],
    "plugins": [{}]
  },
  "node": {
    "dgram": "empty",
    "fs": "empty",
    "net": "empty",
    "tls": "empty",
    "child_process": "empty"
  },
  "devtool": "source-map",
  "stats": "normal",
  "bail": true,
  "optimization": {
    "minimizer": [{
      "options": {
        "test": {},
        "extractComments": false,
        "sourceMap": true,
        "cache": true,
        "parallel": true,
        "terserOptions": {
          "output": {
            "ecma": 5,
            "comments": false,
            "ascii_only": true
          },
          "parse": {
            "ecma": 8
          },
          "compress": {
            "ecma": 5,
            "warnings": false,
            "comparisons": false
          },
          "mangle": {
            "safari10": true
          }
        }
      }
    }],
    "splitChunks": {
      "chunks": "all",
      "name": false
    },
    "runtimeChunk": true
  },
  "externals": {
    "moment": "moment"
  },
  "entry": {
    "entry1": "myApp/app/assets/javascript/packs/entry1.js",
    "entry2": "myApp/app/assets/javascript/packs/entry2.js",
    "entry3": "myApp/app/assets/javascript/packs/entry3.js",
    "entry4": "myApp/app/assets/javascript/packs/entry4.js",
    "entry5": "myApp/app/assets/javascript/packs/entry5.js",
    "entry6": "myApp/app/assets/javascript/packs/entry6.js",
    "entry7": "myApp/app/assets/javascript/packs/entry7.js",
    "entry8": "myApp/app/assets/javascript/packs/entry8.js",
    "landing": "myApp/app/assets/javascript/packs/landing.js",
    "entry9": "myApp/app/assets/javascript/packs/entry9.js",
    "entry10": "myApp/app/assets/javascript/packs/entry10.js",
    "entry11": "myApp/app/assets/javascript/packs/entry11.js",
    "entry12": "myApp/app/assets/javascript/packs/entry12.js",
    "entry13": "myApp/app/assets/javascript/packs/entry13.js",
    "entry14": "myApp/app/assets/javascript/packs/entry14.js",
    "entry15": "myApp/app/assets/javascript/packs/entry15.js"
  },
  "module": {
    "strictExportPresence": true,
    "rules": [{
      "parser": {
        "requireEnsure": false
      }
    }, {
      "test": {},
      "use": [{
        "loader": "file-loader",
        "options": {
          "context": "app/assets/javascript"
        }
      }]
    }, {
      "test": {},
      "use": ["myApp/node_modules/mini-css-extract-plugin/dist/loader.js", {
        "loader": "css-loader",
        "options": {
          "sourceMap": true,
          "importLoaders": 2,
          "localIdentName": "[name]__[local]___[hash:base64:5]",
          "modules": false
        }
      }, {
        "loader": "postcss-loader",
        "options": {
          "config": {
            "path": "myApp"
          },
          "sourceMap": true
        }
      }],
      "sideEffects": true,
      "exclude": {}
    }, {
      "test": {},
      "use": ["myApp/node_modules/mini-css-extract-plugin/dist/loader.js", {
        "loader": "css-loader",
        "options": {
          "sourceMap": true,
          "importLoaders": 2,
          "localIdentName": "[name]__[local]___[hash:base64:5]",
          "modules": false
        }
      }, {
        "loader": "postcss-loader",
        "options": {
          "config": {
            "path": "myApp"
          },
          "sourceMap": false,
          "plugins": [null, null]
        }
      }, {
        "loader": "sass-loader",
        "options": {
          "sourceMap": false,
          "sourceComments": true
        }
      }],
      "sideEffects": true,
      "exclude": {}
    }, {
      "test": {},
      "use": ["myApp/node_modules/mini-css-extract-plugin/dist/loader.js", {
        "loader": "css-loader",
        "options": {
          "sourceMap": true,
          "importLoaders": 2,
          "localIdentName": "[name]__[local]___[hash:base64:5]",
          "modules": true
        }
      }, {
        "loader": "postcss-loader",
        "options": {
          "config": {
            "path": "myApp"
          },
          "sourceMap": true
        }
      }],
      "sideEffects": false,
      "include": {}
    }, {
      "test": {},
      "use": ["myApp/node_modules/mini-css-extract-plugin/dist/loader.js", {
        "loader": "css-loader",
        "options": {
          "sourceMap": true,
          "importLoaders": 2,
          "localIdentName": "[name]__[local]___[hash:base64:5]",
          "modules": true
        }
      }, {
        "loader": "postcss-loader",
        "options": {
          "config": {
            "path": "myApp"
          },
          "sourceMap": true
        }
      }, {
        "loader": "sass-loader",
        "options": {
          "sourceMap": true
        }
      }],
      "sideEffects": false,
      "include": {}
    }, {
      "test": {},
      "include": {},
      "exclude": {},
      "use": [{
        "loader": "babel-loader",
        "options": {
          "babelrc": false,
          "presets": [
            ["@babel/preset-env", {
              "modules": false
            }]
          ],
          "cacheDirectory": "tmp/cache/webpacker/babel-loader-node-modules",
          "cacheCompression": true,
          "compact": false,
          "sourceMaps": false
        }
      }]
    }, {
      "test": {},
      "include": ["myApp/app/assets/javascript", "myApp/app/assets/css"],
      "exclude": {},
      "use": [{
        "loader": "babel-loader",
        "options": {
          "cacheDirectory": "tmp/cache/webpacker/babel-loader-node-modules",
          "cacheCompression": true,
          "compact": true
        }
      }]
    }, {
      "test": "myApp/node_modules/jquery/dist/jquery.js",
      "use": [{
        "loader": "expose-loader",
        "options": "jQuery"
      }, {
        "loader": "expose-loader",
        "options": "$"
      }]
    }, {
      "test": "myApp/node_modules/popper.js/dist/umd/popper.js",
      "use": [{
        "loader": "expose-loader",
        "options": "Popper"
      }]
    }, {
      "test": "myApp/node_modules/scroll-depth/jquery.scrolldepth.js",
      "use": [{
        "loader": "expose-loader",
        "options": "scrollDepth"
      }]
    }]
  },
  "plugins": [{
    "environment_variables_plugin": "values don't really matter in this case I think"
  }, {
    "options": {},
    "pathCache": {},
    "fsOperations": 0,
    "primed": false
  }, {
    "options": {
      "filename": "css/[name]-[contenthash:8].css",
      "chunkFilename": "css/[name]-[contenthash:8].chunk.css"
    }
  }, {}, {
    "options": {
      "test": {},
      "cache": true,
      "compressionOptions": {
        "level": 9
      },
      "filename": "[path].gz[query]",
      "threshold": 0,
      "minRatio": 0.8,
      "deleteOriginalAssets": false
    }
  }, {
    "pluginDescriptor": {
      "name": "OptimizeCssAssetsWebpackPlugin"
    },
    "options": {
      "assetProcessors": [{
        "phase": "compilation.optimize-chunk-assets",
        "regExp": {}
      }],
      "assetNameRegExp": {},
      "cssProcessorOptions": {},
      "cssProcessorPluginOptions": {}
    },
    "phaseAssetProcessors": {
      "compilation.optimize-chunk-assets": [{
        "phase": "compilation.optimize-chunk-assets",
        "regExp": {}
      }],
      "compilation.optimize-assets": [],
      "emit": []
    },
    "deleteAssetsMap": {}
  }, {
    "definitions": {
      "$": "jquery",
      "jQuery": "jquery",
      "jquery": "jquery",
      "window.$": "jquery",
      "window.jQuery": "jquery",
      "window.jquery": "jquery",
      "Popper": ["popper.js", "default"]
    }
  }, {
    "definitions": {
      "process.env": {
        "MY_DEFINED_ENV_VARS": "my defined env var values"
      }
    }
  }, {
    "options": {}
  }]
}
...