打包工具:webpack + vite

一、打包工具基础概念

1. 什么是打包工具?

打包工具(Bundler)是将多个模块(JS、CSS、图片等)组合成一个或多个文件的工具,主要用于:

  • 模块化处理:解决浏览器不支持原生模块的问题
  • 资源优化:压缩代码、处理依赖关系、分割文件
  • 兼容性转换:编译TypeScript、SCSS等非原生格式
  • 开发体验增强:提供热更新、代码分割等功能
2. 为什么需要打包工具?
  • 模块管理:处理复杂的依赖关系
  • 性能优化:减少HTTP请求、代码分割、懒加载
  • 兼容性:转换现代语法以支持旧浏览器
  • 资源处理:统一处理图片、字体、CSS等资源
3. Webpack 与 Vite 对比

链接: vite中文网
链接: Webpack中文网

特性WebpackVite
构建原理基于打包(Bundle)基于ES模块(ESM)和按需编译
冷启动速度慢(需要分析整个依赖树)极快(利用浏览器原生ES模块支持)
热更新(HMR)中等速度(需要重新打包受影响模块)即时更新(无需重新打包)
生态系统成熟,插件丰富新兴,生态快速发展
适用场景大型复杂项目,需要全面优化中小型项目,追求极致开发体验

二、Webpack 基础配置

1. 安装依赖
npm install webpack webpack-cli --save-dev
npm install babel-loader @babel/core @babel/preset-env --save-dev
2. 基本配置文件 webpack.config.js
const path = require('path');

module.exports = {
  mode: 'production',           // 模式:development/production
  entry: './src/index.js',      // 入口文件     	
 
  output: {
    path: path.resolve(__dirname, 'dist'), // 输出目录
    filename: 'bundle.[contenthash].js',   // 输出文件名(带哈希)
  },
  module: {
    rules: [
      {
        test: /\.js$/,          // 匹配JS文件
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader', // 使用Babel转换
          options: {
            presets: ['@babel/preset-env'] // 预设环境
          }
        }
      }
    ]
  },
  plugins: [
    // 更多插件配置...
  ]
};
1.入口
// 单入口
entry: './src/index.js'

// 多入口(生成多个 bundle)
entry: {
  main: './src/index.js',
  vendor: './src/vendor.js'
}
2.输出
output: {
  path: path.resolve(__dirname, 'dist'),
  filename: '[name].[contenthash].js', // 基于内容生成哈希
  publicPath: '/assets/', // 资源加载路径
  clean: true // 构建前清理dist目录
}
3.loader
module: {
  rules: [
    // CSS 处理
    {
      test: /\.css$/,
      use: [
        'style-loader', // 将 CSS 注入 DOM
        {
          loader: 'css-loader',
          options: {
            modules: true // 启用 CSS 模块
          }
        },
        'postcss-loader' // 自动添加浏览器前缀
      ]
    },
    
    // 图片处理
    {
      test: /\.(png|jpg|gif)$/i,
      type: 'asset/resource', // 替代 file-loader
      generator: {
        filename: 'images/[hash][ext][query]'
      }
    },
    
    // TypeScript 处理
    {
      test: /\.tsx?$/,
      use: 'ts-loader',
      exclude: /node_modules/
    }
  ]
}
4.插件Plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

plugins: [
  // 自动生成 HTML 文件
  new HtmlWebpackPlugin({
    template: './src/index.html',
    favicon: './src/favicon.ico'
  }),
  
  // 提取 CSS 到单独文件
  new MiniCssExtractPlugin({
    filename: '[name].[contenthash].css'
  }),
  
  // 清理 dist 目录
  new CleanWebpackPlugin(),
  
  // 定义全局变量
  new webpack.DefinePlugin({
    'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
  }),
  
  // 压缩 JS
  new TerserPlugin({
    parallel: true, // 并行压缩
    terserOptions: {
      compress: {
        drop_console: true // 移除 console
      }
    }
  })
]
5.代码分割
optimization: {
  splitChunks: {
    chunks: 'all', // 对所有类型的 chunk 进行分割
    cacheGroups: {
      vendor: {
        test: /[\\/]node_modules[\\/]/,
        name: 'vendors',
        chunks: 'all'
      }
    }
  },
  runtimeChunk: 'single' // 生成单独的运行时代码
}
3. 开发环境配置
// webpack.dev.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
  mode: 'development',
  devtool: 'inline-source-map', // 生成source map便于调试
  devServer: {                  // 开发服务器配置
    static: './dist',
    hot: true                   // 启用热更新
  }
});
4. 生产环境配置
// webpack.prod.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = merge(common, {
  mode: 'production',
  plugins: [
    new MiniCssExtractPlugin({  // 提取CSS到单独文件
      filename: '[name].[contenthash].css'
    })
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader, // 替代style-loader
          'css-loader'
        ]
      }
    ]
  }
});
5.插件Plugin

Webpack 插件是扩展其功能的核心机制,可在打包过程的特定阶段执行自定义操作(如代码优化、资源管理、环境注入等)。以下是常用插件分类及详细介绍:

1. HTML 处理
  • html-webpack-plugin
    功能:自动生成 HTML 文件并注入打包后的 JS/CSS 文件。
    场景:单页应用(SPA)或多页应用(MPA)的入口 HTML 生成。
    安装

    npm install html-webpack-plugin -D
    

    配置示例

    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html', // 模板文件
          filename: 'index.html', // 输出文件名
          minify: { // 生产环境压缩 HTML
            collapseWhitespace: true,
            removeComments: true
          }
        })
      ]
    };
    
  • html-webpack-externals-plugin
    功能:将特定模块排除在打包外,通过 CDN 引入。
    场景:优化第三方库加载(如 React、Vue)。

2. CSS 处理
  • mini-css-extract-plugin
    功能:将 CSS 提取为独立文件(替代 style-loader)。
    场景:生产环境 CSS 分离(避免 JS 加载后再渲染样式)。
    安装

    npm install mini-css-extract-plugin -D
    

    配置示例

    const MiniCssExtractPlugin = require('mini-css-extract-plugin');
    
    module.exports = {
      module: {
        rules: [
          {
            test: /\.css$/,
            use: [
              MiniCssExtractPlugin.loader, // 替代 style-loader
              'css-loader'
            ]
          }
        ]
      },
      plugins: [
        new MiniCssExtractPlugin({
          filename: '[name].[contenthash].css' // 输出文件名
        })
      ]
    };
    
  • css-minimizer-webpack-plugin
    功能:压缩和优化 CSS 文件。
    场景:生产环境 CSS 体积优化。

3. 代码优化与压缩
  • terser-webpack-plugin
    功能:压缩 JS 代码(Webpack 5 默认内置,生产环境自动启用)。
    场景:生产环境减小 JS 体积。
    配置示例

    const TerserPlugin = require('terser-webpack-plugin');
    
    module.exports = {
      optimization: {
        minimizer: [
          new TerserPlugin({
            terserOptions: {
              compress: {
                drop_console: true // 移除 console 语句
              }
            }
          })
        ]
      }
    };
    
  • image-minimizer-webpack-plugin
    功能:压缩图片(JPEG、PNG、SVG 等)。
    场景:优化静态资源体积。

4. 资源管理
  • clean-webpack-plugin
    功能:每次打包前清理输出目录(如 dist)。
    场景:避免旧文件残留。
    安装

    npm install clean-webpack-plugin -D
    

    配置示例

    const { CleanWebpackPlugin } = require('clean-webpack-plugin');
    
    module.exports = {
      plugins: [
        new CleanWebpackPlugin()
      ]
    };
    
  • copy-webpack-plugin
    功能:复制未经过 Webpack 处理的文件(如静态资源)。
    场景:复制 public 目录到 dist

5. 环境变量与全局变量
  • webpack.DefinePlugin
    功能:定义编译时可访问的全局变量(如 API 地址、环境标识)。
    场景:区分开发/生产环境配置。
    配置示例
    const webpack = require('webpack');
    
    module.exports = {
      plugins: [
        new webpack.DefinePlugin({
          'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
          'API_BASE_URL': JSON.stringify('https://api.example.com')
        })
      ]
    };
    
6. 代码分割与懒加载
  • split-chunks-plugin
    功能:自动分割代码(Webpack 5 内置,无需额外安装)。
    场景:分离第三方库、提取公共模块。
    配置示例
    module.exports = {
      optimization: {
        splitChunks: {
          chunks: 'all', // 分割所有类型的 chunks
          cacheGroups: {
            vendor: {
              test: /[\\/]node_modules[\\/]/,
              name: 'vendors', // 生成 vendor.js
              chunks: 'all'
            }
          }
        }
      }
    };
    
7. 类型检查与编译
  • fork-ts-checker-webpack-plugin
    功能:在 Webpack 构建过程中并行检查 TypeScript 类型(提高编译速度)。
    场景:TypeScript 项目。
8. HMR(热模块替换)
  • webpack.HotModuleReplacementPlugin
    功能:开发环境下无需刷新页面即可替换模块。
    场景:提升开发体验。
    配置示例
    module.exports = {
      devServer: {
        hot: true // 启用 HMR
      },
      plugins: [
        new webpack.HotModuleReplacementPlugin()
      ]
    };
    
9.插件执行顺序与生命周期

Webpack 插件通过监听**编译钩子(Hook)**在特定阶段执行,常见钩子:

  1. 编译前entry-optioncompile
  2. 构建模块时normal-module-loader
  3. 生成资源时emit
  4. 完成后done

示例:自定义插件监听 emit 钩子,在生成文件前修改资源:

class MyPlugin {
  apply(compiler) {
    // 监听 emit 钩子(生成资源到输出目录前)
    compiler.hooks.emit.tap('MyPlugin', (compilation) => {
      // 修改或删除资源
      console.log('生成文件前执行...');
    });
  }
}

// 在配置中使用
module.exports = {
  plugins: [new MyPlugin()]
};
10、自定义插件开发

插件本质是包含 apply 方法的类,通过监听钩子实现功能。
示例:创建一个简单的插件,在打包完成后输出统计信息:

class StatsPlugin {
  constructor(options) {
    this.options = options;
  }
  
  apply(compiler) {
    // 监听编译完成钩子
    compiler.hooks.done.tap('StatsPlugin', (stats) => {
      console.log(`打包完成!共生成 ${stats.compilation.assetsCount} 个文件`);
      console.log(`耗时:${stats.endTime - stats.startTime}ms`);
      
      if (this.options.verbose) {
        // 输出详细信息
        console.log('生成的文件:');
        Object.keys(stats.compilation.assets).forEach((filename) => {
          console.log(`- ${filename} (${stats.compilation.assets[filename].size()} bytes)`);
        });
      }
    });
  }
}

// 使用插件
module.exports = {
  plugins: [new StatsPlugin({ verbose: true })]
};
6. Webpack 生产构建
# 执行生产构建
npm run build

# 构建结果
dist/
├── index.html
├── main.[hash].js
├── vendor.[hash].js
├── styles.[hash].css
└── assets/
    ├── image1.png
    └── font.woff
7.过程
1.创建进入一个目录并初始化 npm,然后 安装 webpack,接着安装 webpack-cli(此工具用于在命令行中运行 webpack):

mkdir project
cd project(创建项目,可以手动完成)

npm init -y (初始化项目/npm init)
# -y 表示默认配置,跳过手动输入
npm install webpack webpack-cli --save-dev(安装,dev开发)
# 开发环境依赖(--save-dev 缩写 -D)
# npm install webpack webpack-cli -D

2.创建以下目录结构、文件和内容

project/
+├── src/                # 源代码目录(必须)
+│   ├── index.js        # 入口文件(Webpack 打包起点)
+│   └── other.js        # 其他模块(如工具函数、组件等)
 ├── dist/               # 打包输出目录(自动生成)
+├── webpack.config.js   # Webpack 配置文件(核心)
 ├── package.json        # 项目依赖和脚本配置
 └── package-lock.json

3.还需要调整 package.json 文件以确保安装包是私有的,并移除 main 入口,这可以防止意外发布代码。
 {
   "name": "project",
   "version": "1.0.0",
   "description": "",
 - "main": "index.js",(删除)
 + "private": true,(添加)
   "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1"
   },
   "keywords": [],
   "author": "",
   "license": "MIT",
   "devDependencies": {
     "webpack": "^5.38.1",
     "webpack-cli": "^4.7.2"
   }
 }
 ---------------------------------------------------
 在 package.json 中添加 scripts 脚本,简化打包命令:
 {
  "scripts": {
    "build": "webpack"  // 执行 npm run build 时,会自动调用 webpack 命令
  }
}
4.创建的webpack.config.js文件配置
// 引入 Node.js 路径模块(处理文件路径)
const path = require('path');

module.exports = {
  // 1. 入口文件(必填):指定 Webpack 从哪个文件开始打包
  entry: './src/index.js',

  // 2. 输出配置(必填):指定打包后文件的输出路径和名称
  output: {
    path: path.resolve(__dirname, 'dist'), // 输出目录(必须是绝对路径,用 path 模块处理)
    filename: 'bundle.js' // 输出文件名(默认 main.js,可自定义)
  },

  // 3. 模式(必填):指定环境,影响打包优化策略
  mode: 'development'  // 可选值:'development'(开发,未压缩)、'production'(生产,自动压缩)
};
(开发环境的包时应该使用 npm install --save-dev。)
npm install --save lodash(根据需要安装)
5.打包

终端运行以下命令,Webpack 会根据 webpack.config.js 配置进行打包:
npm run build
1.Webpack 只能直接处理 JS 和 JSON 文件,需通过 loader 处理其他类型文件
# 处理 CSS:css-loader(解析 CSS 模块)、style-loader(注入 CSS 到页面)
npm install css-loader style-loader -D
# 处理图片:file-loader(复制文件)或 url-loader(小图转 base64)
npm install file-loader url-loader -D
2.生成 HTML 自动引入打包文件
使用 html-webpack-plugin 自动生成 index.html 并引入打包后的 JS/CSS。
安装插件
npm install html-webpack-plugin -D
打包结果:
成功后会生成 dist 目录,其中 bundle.js 即为打包后的合并文件。
可新建 dist/index.html 引入该 JS 文件测试:
<script src="./bundle.js"></script>

三、Vite 基础配置

1. 安装依赖
npm install vite --save-dev
2. 基本配置文件 vite.config.js
import { defineConfig } from 'vite';

export default defineConfig({
  root: './src',              // 项目根目录
  base: '/',                  // 公共基础路径
  build: {
    outDir: '../dist',        // 输出目录
    assetsDir: 'assets',      // 静态资源目录
    sourcemap: true           // 生成source map
  },
  server: {
    port: 3000,               // 开发服务器端口
    open: true,               // 自动打开浏览器
    proxy: {                  // 代理配置
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  }
});
1.
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
  plugins: [
    react(), // 支持 React JSX
  ],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'), // 路径别名
    },
  },
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@import "@/styles/variables.scss";` // 全局引入 SCSS 变量
      }
    }
  },
  server: {
    host: '0.0.0.0', // 允许外部访问
    port: 5173,
    strictPort: true, // 端口被占用时直接退出
  },
  build: {
    minify: 'terser', // 使用 terser 替代 esbuild 压缩 JS
    terserOptions: {
      compress: {
        drop_console: true
      }
    },
    rollupOptions: {
      output: {
        manualChunks(id) {
          if (id.includes('node_modules')) {
            return id.toString().split('node_modules/')[1].split('/')[0].toString();
          }
        }
      }
    }
  }
});
2.插件
# React 支持
npm install @vitejs/plugin-react --save-dev

# Vue 支持
npm install @vitejs/plugin-vue --save-dev

# TypeScript 支持
npm install @rollup/plugin-typescript --save-dev

# ESLint 集成
npm install vite-plugin-eslint --save-dev

# PWA 支持
npm install vite-plugin-pwa --save-dev
3.自定义插件
// 简单的文件替换插件
const fileReplacementPlugin = (options) => {
  return {
    name: 'file-replacement',
    enforce: 'pre',
    resolveId(id) {
      if (options[id]) {
        return options[id];
      }
      return null;
    }
  };
};

// 在 vite.config.js 中使用
plugins: [
  fileReplacementPlugin({
    './src/env.js': './src/env.prod.js'
  })
]
3. 环境变量配置

在 Vite 中,环境变量的命名规则默认为 VITE_ 开头,例如 VITE_API_URL

 VITE_SOME_KEY=123
 DB_PASSWORD=foobar

只有 VITE_SOME_KEY 会被暴露为 import.meta.env.VITE_SOME_KEY 提供给客户端源码,而 DB_PASSWORD 则不会。

 console.log(import.meta.env.VITE_SOME_KEY) // 123
 console.log(import.meta.env.DB_PASSWORD) // undefined

创建 .env 文件:

# .env.development(开发环境)
VITE_APP_TITLE=My App (Dev)
VITE_API_BASE_URL=http://dev-api.example.com

# .env.production(生产环境)
VITE_APP_TITLE=My App (Prod)
VITE_API_BASE_URL=http://api.example.com

在代码中使用:
(在代码中通过 import.meta.env 访问)

console.log(import.meta.env.VITE_APP_TITLE);
console.log(import.meta.env.VITE_API_URL);
4.Vite 生产构建
# 执行生产构建
npm run build

# 构建结果
dist/
├── index.html      # 入口 HTML(自动注入资源)
├── assets/
│   ├── index.[hash].js   # 打包后的 JS 文件
│   ├── index.[hash].css  # 打包后的 CSS 文件
│   ├── react.[hash].js
│   └── image1.[hash].png   # 图片、字体等资源
└── manifest.json
5.过程
1.创建 Vite 项目
# 方式一:使用 npm 7+(带 -- 前缀)
npm init vite@latest my-vite-app -- --template react

# 方式二:使用 yarn
(如:要构建一个 Vite + Vue 项目)
yarn create vite my-vite-app --template vue
或者直接

npm create vite@latest

yarn create vite

2.进入项目并安装依赖
cd my-vite-app
npm install
3.Vite 项目的典型结构:
my-vite-app/
├── public/            # 静态资源(直接复制,不经过打包)
│   └── favicon.ico
├── src/               # 源代码
│   ├── main.js        # 入口文件
│   ├── App.vue/.js    # 根组件
│   ├── index.html     # 主 HTML(必须)
│   └── style.css      # 样式文件
├── package.json       # 依赖和脚本配置
├── vite.config.js     # Vite 配置文件(核心)
└── tsconfig.json      # TypeScript 配置(可选)
4.Vite 核心配置(vite.config.js)
// vite.config.js
import { defineConfig } from 'vite';

export default defineConfig({
  // 项目根目录(默认:当前工作目录)
  root: process.cwd(),
  
  // 开发服务器配置
  server: {
    port: 3000,           // 端口号
    open: true,           // 自动打开浏览器
    proxy: {              // 代理配置(解决跨域)
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  },
  
  // 构建配置
  build: {
    outDir: 'dist',       // 输出目录
    assetsDir: 'assets',  // 静态资源目录
    minify: 'terser',     // 压缩工具(默认 esbuild,terser 压缩率更高)
    terserOptions: {
      compress: {
        drop_console: true,  // 生产环境移除 console
        drop_debugger: true  // 生产环境移除 debugger
      }
    }
  },
  
  // 别名配置(路径映射)
  resolve: {
    alias: {
      '@': '/src',         // 示例:@ 指向 src 目录
    }
  },
  
  // CSS 配置
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@import "@/styles/variables.scss";`  // 全局引入 SCSS 变量
      }
    }
  }
});

5.打包
npm run build  # 或 yarn build 或 pnpm build

6.总结

Vite 打包流程简洁高效,核心步骤
1.初始化项目:使用 npm init vite 创建项目;
2.配置:通过 vite.config.js 定制开发和构建选项;
3.开发:使用 npm run dev 启动开发服务器;
4. 打包:执行 npm run build 生成生产文件;
5.部署:将 dist 目录部署到静态服务器。

四、多环境配置实践

1. Webpack 环境变量配置
// webpack.common.js
const path = require('path');
const Dotenv = require('dotenv-webpack');

module.exports = {
  // ...其他配置
  plugins: [
    new Dotenv({
      path: path.resolve(__dirname, `.env.${process.env.NODE_ENV}`),
      safe: true // 加载.env.example作为安全验证
    })
  ]
};
2. Vite 环境变量配置
// vite.config.js
import { defineConfig, loadEnv } from 'vite';

export default ({ mode }) => {
  const env = loadEnv(mode, process.cwd(), 'VITE_');
  
  return defineConfig({
    // ...其他配置
    define: {
      'process.env': env
    }
  });
};
3. 运行命令配置
// package.json
{
  "scripts": {
    // Webpack
    "dev:webpack": "webpack serve --config webpack.dev.js",
    "build:webpack": "NODE_ENV=production webpack --config webpack.prod.js",
    
    // Vite
    "dev:vite": "vite",
    "build:vite": "vite build --mode production",
    "preview:vite": "vite preview"
  }
}

五、资源处理配置

1. CSS 处理(Webpack)
// webpack.common.js
module.exports = {
  // ...其他配置
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',     // 开发环境使用
          // MiniCssExtractPlugin.loader, // 生产环境使用
          'css-loader',
          'postcss-loader'    // 处理CSS前缀等
        ]
      },
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          'css-loader',
          'sass-loader'       // 编译SCSS
        ]
      }
    ]
  }
};
2. 静态资源处理(Vite)
// vite.config.js
export default defineConfig({
  // ...其他配置
  assetsInclude: ['**/*.png', '**/*.jpg', '**/*.svg'],
  build: {
    assetsInlineLimit: 4096, // 小于4KB的资源内联为base64
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'] // 单独打包第三方库
        }
      }
    }
  }
});

六、注意

  1. 选择合适的工具

    • 新项目优先考虑 Vite(开发体验更好)
    • 复杂项目或已有 Webpack 项目继续使用 Webpack
  2. 环境变量管理

    • 使用 .env 文件管理环境特定配置
    • 通过 process.env.NODE_ENV 区分开发/生产环境
    • 敏感信息不要直接暴露在客户端代码中
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值