当我们切换到使用Webpack来处理SASS文件时,我们注意到在某些情况下,构建时间变得非常缓慢。在使用SpeedMeasurePlugin测量了构建的不同部分的性能之后,似乎sass-loader是罪魁祸首……它很容易花费10s来构建(过去我们花了20s进行了一些修复),比我们想要的更长。
我很好奇,如果人们还有其他策略可以优化我没有介绍的Sass资产建设。此时,我已经经历了相当多的过程(其中一些过程已经经历了多次),但似乎仍无法将构建时间降低得足够低。就目标而言,当前的大规模重建(例如更改用于许多文件的组件)可能很容易花费10-12秒,我希望将其降低到5s。
我们尝试了许多不同的解决方案,其中一些可行,而其他解决方案却无济于事。
includePaths
进行从node_modules加载的SASS-我发现的git issue上建议这样做,因为sass-loader遇到了称为“自定义进口商”的问题。我的理解是,通过使用includePaths,SASS能够依靠那些提供的绝对路径,而不是使用效率低下的算法来解析到诸如node_modules之类的地方的路径从一些简要的统计数据来看,我们似乎在150个SASS文件中分布了约1.6万行SASS代码。有些文件的代码量很大,而其他代码的代码量却很少,这些文件的LOC平均值约为107 LOC /文件。
下面是正在使用的配置。该应用程序是Rails应用程序,因此很多Webpack配置都是通过Webpacker gem处理的。
{
"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": {}
}]
}