模块打包工具

打包工具概述

模块打包工具的作用和原理。

为什么需要打包

// 问题:
// 1. 模块数量多,HTTP请求多
// 2. 浏览器兼容性问题
// 3. 代码压缩和优化
// 4. 资源处理(CSS、图片等)
// 5. 开发体验(热更新、调试)

// 打包工具的作用:
// 1. 模块打包:合并多个模块
// 2. 代码转换:ES6+转ES5
// 3. 代码压缩:减小体积
// 4. 资源处理:处理非JS资源
// 5. 开发服务器:本地开发
// 6. 代码分割:按需加载

主流打包工具

// Webpack
// - 功能最全面
// - 生态最成熟
// - 配置复杂

// Rollup
// - 适合库开发
// - Tree-shaking优秀
// - 配置简单

// Vite
// - 开发体验好
// - 基于ES Modules
// - 构建使用Rollup

// Parcel
// - 零配置
// - 开箱即用
// - 适合小型项目

// esbuild
// - 极快的速度
// - Go语言编写
// - 现代工具链

Webpack

最流行的模块打包工具。

基本配置

// webpack.config.js
const path = require("path");

module.exports = {
    // 入口
    entry: "./src/index.js",
    
    // 输出
    output: {
        filename: "bundle.js",
        path: path.resolve(__dirname, "dist"),
        clean: true
    },
    
    // 模式
    mode: "development",  // development | production | none
    
    // 模块规则
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: "babel-loader"
            },
            {
                test: /\.css$/,
                use: ["style-loader", "css-loader"]
            },
            {
                test: /\.(png|jpg|gif)$/,
                type: "asset"
            }
        ]
    },
    
    // 插件
    plugins: [
        new HtmlWebpackPlugin({
            template: "./src/index.html"
        })
    ],
    
    // 开发服务器
    devServer: {
        static: "./dist",
        hot: true,
        port: 3000
    }
};

Loader配置

// babel-loader: 转换ES6+
{
    test: /\.js$/,
    exclude: /node_modules/,
    use: {
        loader: "babel-loader",
        options: {
            presets: ["@babel/preset-env"]
        }
    }
}

// css-loader + style-loader: 处理CSS
{
    test: /\.css$/,
    use: ["style-loader", "css-loader"]
}

// sass-loader: 处理Sass
{
    test: /\.scss$/,
    use: ["style-loader", "css-loader", "sass-loader"]
}

// file-loader: 处理文件
{
    test: /\.(png|jpg|gif)$/,
    type: "asset/resource"
}

// url-loader: 小文件转base64
{
    test: /\.(png|jpg|gif)$/,
    type: "asset",
    parser: {
        dataUrlCondition: {
            maxSize: 8 * 1024  // 8kb以下转base64
        }
    }
}

Plugin配置

const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
    plugins: [
        // 清理dist目录
        new CleanWebpackPlugin(),
        
        // 生成HTML
        new HtmlWebpackPlugin({
            template: "./src/index.html",
            filename: "index.html"
        }),
        
        // 提取CSS
        new MiniCssExtractPlugin({
            filename: "css/[name].[contenthash].css"
        })
    ]
};

代码分割

module.exports = {
    optimization: {
        splitChunks: {
            chunks: "all",
            cacheGroups: {
                vendors: {
                    test: /[\\/]node_modules[\\/]/,
                    name: "vendors",
                    chunks: "all"
                }
            }
        }
    }
};

// 动态导入
import("./module.js").then(module => {
    module.init();
});

// 魔法注释
import(
    /* webpackChunkName: "user" */
    "./user.js"
);

Rollup

适合库开发的打包工具。

基本配置

// rollup.config.js
export default {
    input: "src/index.js",
    output: [
        {
            file: "dist/bundle.js",
            format: "cjs"  // CommonJS
        },
        {
            file: "dist/bundle.esm.js",
            format: "esm"  // ES Modules
        },
        {
            file: "dist/bundle.umd.js",
            format: "umd",
            name: "MyLibrary"  // UMD
        }
    ]
};

插件配置

import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import babel from "@rollup/plugin-babel";
import terser from "@rollup/plugin-terser";

export default {
    input: "src/index.js",
    output: {
        file: "dist/bundle.js",
        format: "esm"
    },
    plugins: [
        resolve(),
        commonjs(),
        babel({ babelHelpers: "bundled" }),
        terser()
    ]
};

Tree-shaking

// Rollup的Tree-shaking非常优秀
// utils.js
export function used() {
    console.log("使用");
}

export function unused() {
    console.log("未使用");
}

// main.js
import { used } from "./utils.js";
used();

// 打包后只包含used函数

Vite

新一代前端构建工具。

基本配置

// vite.config.js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
    plugins: [react()],
    root: "src",
    build: {
        outDir: "../dist",
        sourcemap: true
    },
    server: {
        port: 3000,
        open: true
    }
});

开发体验

// Vite特点:
// 1. 开发服务器极快启动
// 2. 基于ES Modules,无需打包
// 3. HMR快速更新
// 4. 生产构建使用Rollup

// 开发模式:直接使用ES Modules
// 生产模式:打包优化

// 支持多种格式
import "./style.css";
import logo from "./logo.png";
import data from "./data.json";

配置示例

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import path from "path";

export default defineConfig({
    plugins: [vue()],
    
    resolve: {
        alias: {
            "@": path.resolve(__dirname, "src")
        }
    },
    
    css: {
        preprocessorOptions: {
            scss: {
                additionalData: `@import "@/styles/variables.scss";`
            }
        }
    },
    
    build: {
        rollupOptions: {
            output: {
                manualChunks: {
                    vendor: ["vue", "vue-router"],
                    utils: ["lodash", "axios"]
                }
            }
        }
    }
});

esbuild

极速的JavaScript打包器。

基本使用

const esbuild = require("esbuild");

// 直接构建
esbuild.build({
    entryPoints: ["src/index.js"],
    bundle: true,
    outfile: "dist/bundle.js",
    minify: true,
    sourcemap: true,
    target: ["es2020"]
});

// API调用
async function build() {
    const result = await esbuild.build({
        entryPoints: ["src/index.js"],
        bundle: true,
        write: false
    });
    
    console.log(result.outputFiles[0].text);
}

特点

// esbuild特点:
// 1. 极快的速度(Go编写)
// 2. 内置支持TypeScript
// 3. 内置支持JSX
// 4. Tree-shaking
// 5. 代码分割

// 限制:
// 1. 不支持完整的TypeScript类型检查
// 2. 插件生态较小
// 3. 某些高级功能不支持

打包优化

优化打包结果的方法。

体积优化

// 1. Tree-shaking
// 使用ES Modules语法
export { used };
import { used } from "./utils";

// 2. 代码压缩
mode: "production"  // 自动启用

// 3. 按需加载
const Module = lazy(() => import("./Module"));

// 4. 外部依赖
externals: {
    react: "React",
    "react-dom": "ReactDOM"
}

// 5. 分析工具
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
plugins: [new BundleAnalyzerPlugin()]

性能优化

// 1. 缓存
cache: {
    type: "filesystem"
}

// 2. 多线程
module: {
    rules: [{
        test: /\.js$/,
        use: [{
            loader: "thread-loader"
        }, {
            loader: "babel-loader"
        }]
    }]
}

// 3. 缩小范围
module: {
    rules: [{
        test: /\.js$/,
        exclude: /node_modules/,
        include: path.resolve("src")
    }]
}

// 4. DLL预编译
// webpack.dll.config.js
new webpack.DllPlugin({
    name: "[name]",
    path: path.join(__dirname, "[name]-manifest.json")
});

下一步

掌握了模块打包工具后,让我们继续学习:

  1. 函数式编程基础 - 学习函数式编程
  2. 函数式编程技术 - 学习高级函数式编程
  3. 错误处理 - 学习错误处理最佳实践

东巴文(db-w.cn) - 让编程学习更简单

🎯 东巴文寄语:模块打包工具是现代前端工程化的核心,Webpack功能全面、Rollup适合库开发、Vite开发体验极佳。在 db-w.cn,我们帮你掌握前端工程化工具链!