SWC 打包功能 #

打包基础 #

什么是打包? #

打包是将多个模块和依赖合并成一个或多个文件的过程:

text
┌─────────────────────────────────────────────────────────────┐
│                     打包过程                                 │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  src/                                                        │
│  ├── index.js                                               │
│  ├── utils.js                                               │
│  └── components/                                            │
│      ├── Button.js                                          │
│      └── Input.js                                           │
│                                                              │
│              │                                               │
│              ▼                                               │
│                                                              │
│  dist/                                                       │
│  └── bundle.js                                              │
│                                                              │
└─────────────────────────────────────────────────────────────┘

spack 简介 #

spack 是 SWC 内置的打包器,提供:

  • 极快的打包速度
  • Tree-shaking 支持
  • 代码分割
  • 多入口支持

安装 #

bash
npm install --save-dev @swc/cli @swc/core @swc/spack

基本配置 #

创建配置文件 #

javascript
// spack.config.js
module.exports = {
  entry: {
    main: './src/index.js'
  },
  output: {
    path: './dist',
    name: 'bundle.js'
  }
};

运行打包 #

bash
npx spack

配置详解 #

完整配置结构 #

javascript
module.exports = {
  mode: 'production',
  entry: {
    main: './src/index.js'
  },
  output: {
    path: './dist',
    name: '[name].js',
    format: 'esm'
  },
  module: {},
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx', '.json']
  },
  options: {
    jsc: {
      parser: {
        syntax: 'ecmascript',
        jsx: true
      },
      target: 'es2015'
    }
  }
};

entry 配置 #

单入口 #

javascript
module.exports = {
  entry: {
    main: './src/index.js'
  }
};

多入口 #

javascript
module.exports = {
  entry: {
    main: './src/index.js',
    admin: './src/admin.js',
    login: './src/login.js'
  }
};

数组入口 #

javascript
module.exports = {
  entry: {
    main: ['./src/polyfills.js', './src/index.js']
  }
};

output 配置 #

基本输出 #

javascript
module.exports = {
  output: {
    path: './dist',
    name: 'bundle.js'
  }
};

使用占位符 #

javascript
module.exports = {
  entry: {
    main: './src/index.js',
    admin: './src/admin.js'
  },
  output: {
    path: './dist',
    name: '[name].[hash].js'
  }
};

输出格式 #

javascript
module.exports = {
  output: {
    path: './dist',
    name: 'bundle.js',
    format: 'esm'  // 'esm' | 'cjs' | 'iife'
  }
};

resolve 配置 #

javascript
module.exports = {
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
    alias: {
      '@': './src',
      '@utils': './src/utils',
      '@components': './src/components'
    }
  }
};

module 配置 #

javascript
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.(png|jpg|gif)$/,
        use: ['file-loader']
      }
    ]
  }
};

代码分割 #

动态导入 #

javascript
// src/index.js
async function loadModule() {
  const { heavyFunction } = await import('./heavy.js');
  heavyFunction();
}

loadModule();
javascript
// spack.config.js
module.exports = {
  entry: {
    main: './src/index.js'
  },
  output: {
    path: './dist',
    name: '[name].js'
  }
};

打包结果:

text
dist/
├── main.js
└── heavy.[hash].js

手动分割 #

javascript
module.exports = {
  entry: {
    main: './src/index.js',
    vendor: ['lodash', 'axios']
  },
  output: {
    path: './dist',
    name: '[name].js'
  }
};

SplitChunks 配置 #

javascript
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 30000,
      minChunks: 1,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true
        }
      }
    }
  }
};

Tree-shaking #

启用 Tree-shaking #

javascript
// spack.config.js
module.exports = {
  mode: 'production',
  options: {
    jsc: {
      target: 'es2015'
    }
  }
};

Tree-shaking 示例 #

javascript
// utils.js
export function used() {
  return 'used';
}

export function unused() {
  return 'unused';
}

// index.js
import { used } from './utils';
console.log(used());

打包后只包含 used 函数。

副作用标记 #

json
// package.json
{
  "sideEffects": false
}

或指定有副作用的文件:

json
{
  "sideEffects": ["*.css", "*.scss"]
}

外部依赖 #

排除依赖 #

javascript
module.exports = {
  external: ['lodash', 'axios', 'react', 'react-dom']
};

函数形式 #

javascript
module.exports = {
  external: (id) => {
    return id.includes('node_modules');
  }
};

全局变量映射 #

javascript
module.exports = {
  external: ['lodash', 'jquery'],
  output: {
    globals: {
      lodash: '_',
      jquery: '$'
    }
  }
};

Source Map #

启用 Source Map #

javascript
module.exports = {
  options: {
    sourceMaps: true
  }
};

内联 Source Map #

javascript
module.exports = {
  options: {
    sourceMaps: 'inline'
  }
};

开发模式 #

开发配置 #

javascript
// spack.config.js
const isDev = process.env.NODE_ENV !== 'production';

module.exports = {
  mode: isDev ? 'development' : 'production',
  entry: {
    main: './src/index.js'
  },
  output: {
    path: './dist',
    name: '[name].js'
  },
  options: {
    jsc: {
      parser: {
        syntax: 'ecmascript',
        jsx: true
      },
      target: 'es2015'
    },
    sourceMaps: isDev ? 'inline' : true
  }
};

监听模式 #

bash
npx spack watch

TypeScript 支持 #

TypeScript 配置 #

javascript
module.exports = {
  entry: {
    main: './src/index.ts'
  },
  output: {
    path: './dist',
    name: '[name].js'
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx']
  },
  options: {
    jsc: {
      parser: {
        syntax: 'typescript',
        tsx: true
      },
      target: 'es2015'
    }
  }
};

React 项目 #

React 配置 #

javascript
module.exports = {
  entry: {
    main: './src/index.jsx'
  },
  output: {
    path: './dist',
    name: '[name].js',
    format: 'iife'
  },
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx']
  },
  options: {
    jsc: {
      parser: {
        syntax: 'ecmascript',
        jsx: true
      },
      transform: {
        react: {
          runtime: 'automatic'
        }
      },
      target: 'es2015'
    }
  }
};

库打包 #

库配置 #

javascript
module.exports = {
  entry: {
    main: './src/index.ts'
  },
  output: [
    {
      path: './dist',
      name: 'my-lib.cjs.js',
      format: 'cjs'
    },
    {
      path: './dist',
      name: 'my-lib.esm.js',
      format: 'esm'
    }
  ],
  external: ['lodash'],
  options: {
    jsc: {
      parser: {
        syntax: 'typescript'
      },
      target: 'es2015',
      externalHelpers: true,
      keepClassNames: true
    }
  }
};

JavaScript API #

编程式使用 #

javascript
const { bundle } = require('@swc/spack');

async function build() {
  const result = await bundle({
    entry: {
      main: './src/index.js'
    },
    output: {
      path: './dist',
      name: 'bundle.js'
    }
  });
  
  console.log(result);
}

build();

自定义构建 #

javascript
const { bundle } = require('@swc/spack');
const fs = require('fs');

async function build() {
  const result = await bundle({
    entry: {
      main: './src/index.js'
    },
    output: {
      path: './dist',
      name: 'bundle.js'
    },
    options: {
      jsc: {
        parser: {
          syntax: 'ecmascript'
        },
        target: 'es2015'
      },
      minify: true,
      sourceMaps: true
    }
  });
  
  for (const [filename, content] of Object.entries(result.output)) {
    fs.writeFileSync(`./dist/${filename}`, content.code);
    if (content.map) {
      fs.writeFileSync(`./dist/${filename}.map`, JSON.stringify(content.map));
    }
  }
}

build();

与其他工具对比 #

spack vs Webpack #

特性 spack Webpack
速度 极快 较慢
配置 简单 复杂
插件生态 🔄 发展中 ✅ 成熟
代码分割 ✅ 支持 ✅ 强大
HMR ⚠️ 有限 ✅ 完善

spack vs Rollup #

特性 spack Rollup
速度 极快
Tree-shaking ✅ 支持 ✅ 优秀
输出格式 多种 多种
插件 SWC 插件 JS 插件
适用场景 应用/库 库开发

实战示例 #

完整项目配置 #

javascript
// spack.config.js
const isDev = process.env.NODE_ENV !== 'production';
const isProd = !isDev;

module.exports = {
  mode: isProd ? 'production' : 'development',
  
  entry: {
    main: './src/index.tsx'
  },
  
  output: {
    path: './dist',
    name: isProd ? '[name].[contenthash].js' : '[name].js',
    format: 'iife'
  },
  
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
    alias: {
      '@': './src',
      '@components': './src/components',
      '@utils': './src/utils'
    }
  },
  
  external: {
    react: 'React',
    'react-dom': 'ReactDOM'
  },
  
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  },
  
  options: {
    jsc: {
      parser: {
        syntax: 'typescript',
        tsx: true,
        decorators: true
      },
      transform: {
        react: {
          runtime: 'automatic'
        }
      },
      target: 'es2015',
      minify: isProd ? {
        compress: {
          defaults: true,
          drop_console: isProd,
          drop_debugger: isProd
        },
        mangle: isProd
      } : false
    },
    sourceMaps: true,
    minify: isProd
  }
};

npm scripts #

json
{
  "scripts": {
    "build": "spack",
    "build:prod": "NODE_ENV=production spack",
    "build:watch": "spack watch",
    "dev": "spack watch"
  }
}

常见问题 #

问题一:模块解析失败 #

解决方案

javascript
module.exports = {
  resolve: {
    extensions: ['.js', '.jsx', '.ts', '.tsx', '.json']
  }
};

问题二:CSS 文件不处理 #

解决方案

javascript
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
};

问题三:外部依赖未排除 #

解决方案

javascript
module.exports = {
  external: ['react', 'react-dom', 'lodash']
};

下一步 #

现在你已经掌握了 SWC 的打包功能,接下来学习 插件开发 了解如何扩展 SWC!

最后更新:2026-03-28