Dependency collection and analysis process
Dependency Collection and Analysis Process
One of Webpack's core functionalities is the collection and analysis of module dependencies. This process determines the correctness and optimization level of the final bundled output. Dependency collection starts from the entry file and builds a complete dependency graph through both static analysis and dynamic resolution.
Static Dependency Analysis
Static analysis primarily handles explicitly declared dependencies in the code, such as ESM's import
and CommonJS's require
. Webpack parses these statements and extracts dependency paths:
// Example: Static dependencies
import utils from './utils.js';
const lodash = require('lodash');
The analysis process:
- Recognizes
'./utils.js'
and'lodash'
as dependencies - Resolves paths to absolute paths
- Creates reference relationships between modules
Special scenario handling:
- Dynamic expressions trigger warnings
// Warning: Cannot be statically analyzed
const moduleName = 'utils';
require(`./${moduleName}`);
Dynamic Dependency Handling
For scenarios that cannot be statically analyzed, Webpack provides specific syntax support:
// Explicitly declare possible paths
require.context('./components', false, /\.vue$/);
Processing flow:
- Scans files matching the regex in the specified directory
- Generates a virtual module mapping table
- Loads dynamically at runtime based on actual needs
Dependency Graph Construction
Collected dependencies form a directed graph data structure:
// Simplified dependency graph structure
{
'entry.js': {
deps: ['a.js', 'b.js'],
exports: ['default']
},
'a.js': {
deps: ['c.js'],
exports: ['getName']
}
}
Key properties include:
dependencies
: List of direct dependenciesissuer
: Information about the referrermoduleGraph
: Complete module relationship network
Circular Dependency Handling
Webpack resolves circular references through the following mechanisms:
// a.js
import { b } from './b';
export const a = 'A';
// b.js
import { a } from './a';
export const b = 'B';
Handling strategy:
- Marks the state of loaded modules
- Returns references that are not fully initialized
- Eventually forms a closed-loop reference relationship
Dependency Optimization Phase
Optimization operations performed after analysis:
- Tree Shaking:
// Marks unused exports
import { used, unused } from './module';
console.log(used);
- Scope Hoisting:
// Merges module scopes
// Original code
import { a } from './module';
console.log(a);
// Optimized
console.log('a');
- Dependency Grouping:
// Splits based on reference frequency
optimization.splitChunks: {
chunks: 'all'
}
Custom Dependency Resolution
Modifies resolution logic through resolve
configuration:
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
},
extensions: ['.ts', '.js'],
modules: ['node_modules', 'shared']
}
Advanced resolution example:
resolve.plugins: [
new DirectoryNamedWebpackPlugin()
]
Dependency Analysis Tools
Analysis methods available during development:
- Stats Output:
webpack --profile --json > stats.json
- Visualization Tools:
new BundleAnalyzerPlugin({
analyzerMode: 'static'
})
- Custom Reports:
compilation.hooks.finishModules.tap('DependencyReport', modules => {
generateCustomReport(modules);
});
Performance Optimization Practices
Optimization examples in real projects:
// 1. Lazy loading
import('./heavyModule').then(module => {
module.run();
});
// 2. Prefetching
import(/* webpackPrefetch: true */ './modal.js');
// 3. Shared dependencies
externals: {
react: 'React'
}
Hook System for Dependency Collection
Extension points exposed by Webpack:
compiler.hooks.compilation.tap('DependencyTracking', (compilation) => {
compilation.hooks.finishModules.tapAsync('AnalyzeDeps', (modules, callback) => {
analyzeModuleGraph(modules);
callback();
});
});
Key Hook phases:
beforeResolve
afterResolve
- After
moduleGraph
updates
Dependency Handling in Module Federation
Cross-application dependency sharing solution:
// app1/webpack.config.js
new ModuleFederationPlugin({
name: 'app1',
exposes: {
'./Button': './src/Button'
}
});
// app2/webpack.config.js
new ModuleFederationPlugin({
name: 'app2',
remotes: {
app1: 'app1@http://localhost:3001/remoteEntry.js'
}
});
Runtime dependency resolution flow:
- Loads remote entry files
- Establishes version mapping tables
- Loads remote modules on demand
Caching and Incremental Analysis
Persistent cache configuration example:
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
}
}
Cache invalidation scenarios:
- File content changes
- Loader configuration modifications
- Resolution rule changes
Dependency Version Conflict Resolution
Common solutions:
- Version Negotiation:
resolve: {
alias: {
'react': path.resolve('./node_modules/react')
}
}
- Dependency Hoisting:
optimization: {
runtimeChunk: 'single'
}
- Externalization:
externals: {
'lodash': '_'
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:Chunk生成算法
下一篇:插件系统Tapable解析