@Dale-Lin
2022-08-09T13:17:54.000000Z
字数 9393
阅读 667
webpack5
webpack 在大型项目中的性能表现较差,一方面是因为 JavaScript 语言的单线程架构决定了 webpack 的运行效率不会太高;另一方面是因为在大型项目中,webpack 通常需要借助许多 plugins、loader 来完成大量文件的读写、编译操作。
行之有效的性能优化方法包括:缓存、并发、优化文件处理步骤等,但优化前必须理解 webpack 打包的核心流程,借助性能分析工具找到耗时瓶颈。
webpack 打包的核心功能有两个:
为了实现上述功能,webpack 底层的工作流程可以总结成几个阶段:
compilation.addEntry 方法,将一个个文件加入到编译队列中;这之中可能造成性能问题的地方:
构建阶段:
生成阶段:
splitChunks 配置、entry 配置,动态模块引入等语句,确定模块和 chunk 之间的映射关系,其中 splitChunks 相关的分包算法非常复杂,涉及大量 CPU 运算;optimizatiokn 配置对产物进行优化,特别是 收集 webpack 打包过程的性能数据——webpack内置了 stats 接口,在编译过程中可以生成一个包含模块分析数据的 JSON 文件,这些数据可以用来分析应用的依赖图谱,以及优化编译速度,使用以下命令生成:
npx webpack --profile --json=compilation-stats.json
参数说明:
--json=compilation-stats.json:告知 webpack 把依赖图谱和其他构建信息输出到 compilation-stats.json 中;--profile:每个 module-objects 都会增加一个 profile 部分,包含这个模块的编译数据。顶层结构:
{'version': '5.9.0', // Version of webpack used for the compilation'hash': '11593e3b3ac85436984a', // Compilation specific hash'time': 2469, // Compilation time in milliseconds'publicPath': 'auto','outputPath': '/', // path to webpack output directory'assetsByChunkName': {// Chunk name to emitted asset(s) mapping'main': ['web.js?h=11593e3b3ac85436984a'],'named-chunk': ['named-chunk.web.js'],'other-chunk': ['other-chunk.js','other-chunk.css']},'assets': [// A list of asset objects],'chunks': [// A list of chunk objects],'modules': [// A list of module objects],'entryPoints': {// A list of entry objects},'errors': [// A list of error objects],'errorsCount': 0, // number of errors'warnings': [// A list of warning objects],'warningsCount': 0, // nummber of warnings}
每个 assets object 表示一个输出的 output 文件,结构如下:
{"chunkNames": [], // The chunks this asset contains"chunks": [10, 6], // The chunk IDs this asset contains"comparedForEmit": false, // Indicates whether or not the asset was compared with the same file on the output file system"emitted": true, // Indicates whether or not the asset made it to the `output` directory"name": "10.web.js", // The `output` filename"size": 1058, // The size of the file in bytes"info": {"immutable": true, // A flag telling whether the asset can be long term cached (contains a hash)"size": 1058, // The size in bytes, only becomes available after asset has been emitted"development": true, // A flag telling whether the asset is only used for development and doesn't count towards user-facing assets"hotModuleReplacement": true, // A flag telling whether the asset ships data for updating an existing application (HMR)"sourceFilename": "originalfile.js", // sourceFilename when asset was created from a source file (potentially transformed)"javascriptModule": true // true, when asset is javascript and an ESM}}
每个 chunk object 表示一组模块组成的 chunk(没有代码分离的情况下,一个 entry 构成的模块一般对应一个 chunk),结构如下:
{"entry": true, // Indicates whether or not the chunk contains the webpack runtime"files": [// An array of filename strings that contain this chunk],"filteredModules": 0, // See the description in the top-level structure above"id": 0, // The ID of this chunk"initial": true, // Indicates whether this chunk is loaded on initial page load or on demand(/guides/lazy-loading)"modules": [// A list of [module objects](#module-objects)"web.js?h=11593e3b3ac85436984a"],"names": [// An list of chunk names contained within this chunk],"origins": [// ...],"parents": [], // Parent chunk IDs"rendered": true, // Indicates whether or not the chunk went through Code Generation"size": 188057 // Chunk size in bytes}
每个 origins 描述当前 chunk 是怎么生成的:
{"loc": "", // Lines of code that generated this chunk"module": "(webpack)\\test\\browsertest\\lib\\index.web.js", // Path to the module"moduleId": 0, // The ID of the module"moduleIdentifier": "(webpack)\\test\\browsertest\\lib\\index.web.js", // Path to the module"moduleName": "./lib/index.web.js", // Relative path to the module"name": "main", // The name of the chunk"reasons": [// A list of the same `reasons` found in module objects]}
编译应用的每个实际 module(存在于依赖图谱中)数据;结构如下:
"assets": [// A list of [asset objects](#asset-objects)],"built": true, // Indicates that the module went through [Loaders](/concepts/loaders), Parsing, and Code Generation"cacheable": true, // Whether or not this module is cacheable"chunks": [// IDs of chunks that contain this module],"errors": 0, // Number of errors when resolving or processing the module"failed": false, // Whether or not compilation failed on this module"id": 0, // The ID of the module (analogous to [`module.id`](/api/module-variables/#moduleid-commonjs))"identifier": "(webpack)\\test\\browsertest\\lib\\index.web.js", // A unique ID used internally"name": "./lib/index.web.js", // Path to the actual file"optional": false, // All requests to this module are with `try... catch` blocks (irrelevant with ESM)"prefetched": false, // Indicates whether or not the module was [prefetched](/plugins/prefetch-plugin)"profile": {// Module specific compilation stats corresponding to the [`--profile` flag](/api/cli/#profiling) (in milliseconds)"building": 73, // Loading and parsing"dependencies": 242, // Building dependencies"factory": 11 // Resolving dependencies},"reasons": [// ...],"size": 3593, // Estimated size of the module in bytes"source": "// Should not break it...\r\nif(typeof...", // The stringified raw source"warnings": 0 // Number of warnings when resolving or processing the module
reasons 数组描述为什么该模块存在于依赖图谱中:
{"loc": "33:24-93", // Lines of code that caused the module to be included"module": "./lib/index.web.js", // Relative path to the module based on [context](/configuration/entry-context/#context)"moduleId": 0, // The ID of the module"moduleIdentifier": "(webpack)\\test\\browsertest\\lib\\index.web.js", // Path to the module"moduleName": "./lib/index.web.js", // A more readable name for the module (used for "pretty-printing")"type": "require.context", // The [type of request](/api/module-methods) used"userRequest": "../../cases" // Raw string used for the `import` or `require` request}
"main": {"name": "main","chunks": [179],"assets": [{"name": "main.js","size": 22}],"filteredAssets": 0,"assetsSize": 22,"auxiliaryAssets": [],"filteredAuxiliaryAssets": 0,"auxiliaryAssetsSize": 0,"children": {},"childAssets": {},"isOverSizeLimit": false}
errors and warnings 属性包含一组对象,每个对象包含一个栈追踪和其他属性:
{"moduleIdentifier": "C:\\Repos\\webpack\\test\\cases\\context\\issue-5750\\index.js","moduleName": "(webpack)/test/cases/context/issue-5750/index.js","loc": "3:8-47","message": "Critical dependency: Contexts can't use RegExps with the 'g' or 'y' flags.","moduleId": 29595,"moduleTrace": [{"originIdentifier": "C:\\Repos\\webpack\\test\\cases|sync|/^\\.\\/[^/]+\\/[^/]+\\/index\\.js$/","originName": "(webpack)/test/cases sync ^\\.\\/[^/]+\\/[^/]+\\/index\\.js$","moduleIdentifier": "C:\\Repos\\webpack\\test\\cases\\context\\issue-5750\\index.js","moduleName": "(webpack)/test/cases/context/issue-5750/index.js","dependencies": [{"loc": "./context/issue-5750/index.js"}],"originId": 32582,"moduleId": 29595},{"originIdentifier": "C:\\Repos\\webpack\\testCases.js","originName": "(webpack)/testCases.js","moduleIdentifier": "C:\\Repos\\webpack\\test\\cases|sync|/^\\.\\/[^/]+\\/[^/]+\\/index\\.js$/","moduleName": "(webpack)/test/cases sync ^\\.\\/[^/]+\\/[^/]+\\/index\\.js$","dependencies": [{"loc": "1:0-70"}],"originId": 8198,"moduleId": 32582}],"details": "at RequireContextDependency.getWarnings (C:\\Repos\\webpack\\lib\\dependencies\\ContextDependency.js:79:5)\n at Compilation.reportDependencyErrorsAndWarnings (C:\\Repos\\webpack\\lib\\Compilation.js:1727:24)\n at C:\\Repos\\webpack\\lib\\Compilation.js:1467:10\n at _next2 (<anonymous>:16:1)\n at eval (<anonymous>:42:1)\n at C:\\Repos\\webpack\\node_modules\\neo-async\\async.js:2830:7\n at Object.each (C:\\Repos\\webpack\\node_modules\\neo-async\\async.js:2850:39)\n at C:\\Repos\\webpack\\lib\\FlagDependencyExportsPlugin.js:219:18\n at C:\\Repos\\webpack\\node_modules\\neo-async\\async.js:2830:7\n at Object.each (C:\\Repos\\webpack\\node_modules\\neo-async\\async.js:2850:39)\n at C:\\Repos\\webpack\\lib\\FlagDependencyExportsPlugin.js:40:16\n at Hook.eval [as callAsync] (<anonymous>:38:1)\n at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (C:\\Repos\\tapable\\lib\\Hook.js:18:14)\n at Compilation.finish (C:\\Repos\\webpack\\lib\\Compilation.js:1462:28)\n at C:\\Repos\\webpack\\lib\\Compiler.js:909:18\n at processTicksAndRejections (internal/process/task_queues.js:75:11)\n","stack": "ModuleDependencyWarning: Critical dependency: Contexts can't use RegExps with the 'g' or 'y' flags.\n at Compilation.reportDependencyErrorsAndWarnings (C:\\Repos\\webpack\\lib\\Compilation.js:1732:23)\n at C:\\Repos\\webpack\\lib\\Compilation.js:1467:10\n at _next2 (<anonymous>:16:1)\n at eval (<anonymous>:42:1)\n at C:\\Repos\\webpack\\node_modules\\neo-async\\async.js:2830:7\n at Object.each (C:\\Repos\\webpack\\node_modules\\neo-async\\async.js:2850:39)\n at C:\\Repos\\webpack\\lib\\FlagDependencyExportsPlugin.js:219:18\n at C:\\Repos\\webpack\\node_modules\\neo-async\\async.js:2830:7\n at Object.each (C:\\Repos\\webpack\\node_modules\\neo-async\\async.js:2850:39)\n at C:\\Repos\\webpack\\lib\\FlagDependencyExportsPlugin.js:40:16\n at Hook.eval [as callAsync] (<anonymous>:38:1)\n at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (C:\\Repos\\tapable\\lib\\Hook.js:18:14)\n at Compilation.finish (C:\\Repos\\webpack\\lib\\Compilation.js:1462:28)\n at C:\\Repos\\webpack\\lib\\Compiler.js:909:18\n at processTicksAndRejections (internal/process/task_queues.js:75:11)\n"}