阿里云主机折上折
  • 微信号
Current Site:Index > The principle of Dependency Pre-Bundling

The principle of Dependency Pre-Bundling

Author:Chuan Chen 阅读数:6680人阅读 分类: 构建工具

Principle of Dependency Pre-Bundling

Vite.js leverages native browser ES module support in development mode, serving source files on demand. However, bare module imports (e.g., import vue from 'vue') can lead to a waterfall of browser requests. Dependency pre-bundling solves this by converting CommonJS/UMD dependencies into ESM format and merging them into a single file.

Pre-Bundling Trigger Mechanism

Pre-bundling is automatically triggered in the following scenarios:

  1. During the initial startup of the development server
  2. When dependencies in node_modules change
  3. When the node_modules/.vite cache directory is manually deleted
// vite.config.js
export default {
  optimizeDeps: {
    // Dependencies forced to pre-bundle
    include: ['lodash-es'],
    // Dependencies excluded from pre-bundling
    exclude: ['moment'],
    // Disable pre-bundling
    disabled: false
  }
}

Core Pre-Bundling Process

  1. Dependency Scanning: Identifies all bare module imports through static analysis

    • Uses esbuild's scanner to quickly parse import statements
    • Recursively analyzes all dependencies of entry files
  2. Dependency Bundling:

    • Bundles each dependency and its deep dependencies into a single ESM file
    • Handles special format conversions (CommonJS → ESM)
    • Applies minification optimizations
  3. Cache Handling:

    • Generates a content hash as a cache identifier
    • Stores results in the node_modules/.vite directory
    • Reuses existing cache on subsequent startups

Technical Implementation Details

Dependency Graph Construction Algorithm

interface DepInfo {
  id: string
  imports: Set<string>
  // Other metadata...
}

function scanImports(filePath: string): DepInfo {
  // Leverages esbuild's parsing capabilities
  const result = esbuild.buildSync({
    entryPoints: [filePath],
    bundle: true,
    write: false,
    metafile: true
  })
  
  return {
    id: filePath,
    imports: extractImports(result.metafile)
  }
}

Module Format Conversion

Example of converting a CommonJS module:

// Before (CommonJS)
const _ = require('lodash')
module.exports = _.cloneDeep

// After (ESM)
import * as _ from 'lodash'
export default _.cloneDeep

Cache Invalidation Strategy

Vite uses multi-layer cache validation:

  1. Hash of the dependencies field in package.json
  2. Content hash of lock files (package-lock.json/yarn.lock)
  3. Hash of the optimizeDeps configuration in the config file

Performance Optimization Techniques

  1. Parallel Processing: Uses worker threads to process multiple dependencies in parallel
  2. Incremental Building: Only rebuilds changed dependencies
  3. Smart Caching: Persistent caching based on file content hashes
// Example of multi-threaded processing
const worker = new Worker('./pre-bundle-worker.js')
worker.postMessage({
  dep: 'vue',
  version: '3.2.0'
})

Handling Special Scenarios

Circular Dependency Resolution

// a.js
import { b } from './b'
export const a = b + 1

// b.js
import { a } from './a'
export const b = a || 0

// After pre-bundling:
const __a = { a: undefined }
const __b = { b: undefined }
__a.a = __b.b + 1
__b.b = __a.a || 0
export { __a as a, __b as b }

Dynamic Import Handling

// Original code
const module = await import('lodash-es')

// After pre-bundling
const module = await import('/node_modules/.vite/lodash-es.js')

Custom Pre-Bundling Configuration

Advanced configuration example:

// vite.config.js
export default {
  optimizeDeps: {
    // Custom entry files
    entries: [
      'src/main.js',
      '!src/excluded.js'
    ],
    // Custom esbuild plugins
    esbuildOptions: {
      plugins: [myEsbuildPlugin()]
    },
    // Force rebuild
    force: process.env.FORCE_REBUILD === 'true'
  }
}

Differences from Regular Bundling

  1. Development-Only: Used exclusively for development server acceleration
  2. Preserves Module Boundaries: Unlike production bundling, it doesn't fully flatten modules
  3. Omits Full Optimizations: Skips production optimizations like code splitting and Tree-shaking

Debugging the Pre-Bundling Process

Debugging information can be output via environment variables:

DEBUG=vite:deps vite

Example output:

vite:deps Pre-bundling dependencies: vue, lodash-es +12ms
vite:deps Cache hit: react, react-dom +4ms
vite:deps Rebuilding dependency: vue-router (version changed) +8ms

Analysis of Pre-Bundled Output

Example structure of generated cache files:

node_modules/.vite/
  ├── _metadata.json
  ├── vue.js
  ├── lodash-es.js
  └── dep-hash.json

The _metadata.json contains:

{
  "hash": "a1b2c3d",
  "browserHash": "e4f5g6h",
  "optimized": {
    "vue": {
      "file": "/path/to/vue.js",
      "src": "/path/to/node_modules/vue/dist/vue.runtime.esm-bundler.js",
      "needsInterop": false
    }
  }
}

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.