webpack配置全面讲解【完整篇】

文章目录

前言

Webpack 是一个现代 JavaScript 应用程序的静态模块打包工具,它可以将项目中的所有模块(JavaScript、图片、CSS 等)打包成一个或多个 bundle

Webpack 4 是一个重要的版本,它提供了零配置、合理的默认设置、性能改进和开箱即用的优化工具。本文将对 webpack 4 进行介绍,后面会对其他版本特性进行补充

webpack 核心包:

  1. webpack : 这是 Webpack 的核心包,提供了模块打包的能力。它能够处理项目中 JavaScript 模块的依赖关系,并生成最终的静态资源文件。

  2. webpack-cli : 这是 Webpack 的命令行工具,允许你通过命令行界面来运行 Webpack。它提供了各种命令来控制 Webpack 的构建过程。

  3. webpack-dev-server : 这是一个小型的 Express 开发服务器,用于开发环境。它提供了热模块替换(HMR)功能,允许在开发过程中实时重新加载模块,而无需刷新整个页面。

配置文件导出三种方式:

默认使用的是 webpack.config.js 文件,也可以是其他名字,webpack -c xxx,只需要通过-c置顶文件就行,配置文件默认有是三种导出格式:

  • 对象或数组:单个配置或多个配置
  • 函数: 可以根据环境变量和条件导出不同的配置
  • Promise:用于远程加载其他配置参数,最终返回结果

代码实例:

js 复制代码
// 对象或数组
module.exports = [
  {
    output: {
      filename: "./dist.js",
    },
    entry: "./app.js",
  },
  {
    output: {
      filename: "./dist-test.js",
    },
    entry: "./app.js",
  },
];
// 函数
module.exports = function (env, argv) {
  return {
    mode: env.production ? "production" : "development",
    devtool: env.production ? "source-map" : "eval",
    plugins: [
      new TerserPlugin({
        terserOptions: {
          compress: argv.mode === "production", // only if `--mode production` was passed
        },
      }),
    ],
  };
};
// promise
module.exports = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({
        entry: "./app.js",
        /* ... */
      });
    }, 5000);
  });
};

在线配置 webpack

配置文件解析:

入口(Entry):

  • 入口点是应用程序的起点,Webpack 从这些点开始构建其依赖图。入口可以是一个文件或者一个文件数组。
js 复制代码
// 单入口
entry: {
   main:'./index.js',
}
// 多入口
entry: {
   main: './src/app.js',
   foo: './src/foo.js',
},

输出(Output):

  • 输出属性指定了打包后的文件将如何被写入到磁盘上。它定义了输出文件的名称、路径以及它们在浏览器中被引用的方式。
js 复制代码
output: {
    filename: '[name].js', //hash选择:[hash],[contenthash] 或者 [chunkhash] 的长度可以使用 [hash:16]
    path: __dirname + '/dist', //输出目录
    publicPath: 'https://cdn.example.com/assets/[fullhash]/' // 设置cdn 和 hash
  },

加载器(Loaders):

  • Webpack 只能理解 JavaScript,Loaders 允许 Webpack 处理其他类型的文件,并将它们转换为 Webpack 能够理解的模块【模块转化】
  • loader 可以使你在 import 模块时预处理文件。因此,loader 类似于其他构建工具中"任务(task)",并提供了处理前端构建步骤的得力方式
  • loader 支持配置和内联的方式书写
  • loader 执行顺序: 从右到左(或从下到上)的执行(execute)
js 复制代码
 module: {
    rules: [
      {
        test: /\.css$/,
        exclude: /node_modules/,
        use: [
          { loader: 'style-loader' },
          {
            loader: 'css-loader',
            options: {
              modules: true,  // css模块化
            },
          },
          { loader: 'sass-loader' },
        ],
      },
    ],
  }

插件(Plugins):

  • 插件在 Webpack 构建过程中提供了更多的功能,如优化、压缩、分割代码等。插件可以在整个构建过程中的特定时机注入上下文。
  • Webpack 内部广泛使用 Tapable (一个 Node.js 库),它提供了一个发布-订阅(pub-sub)机制,允许用户在特定的钩子(hook)上注册监听器(listener)
js 复制代码
const HtmlWebpackPlugin = require("html-webpack-plugin");
const webpack = require("webpack"); // 访问内置的插件

plugins: [
  new webpack.ProgressPlugin(),
  new HtmlWebpackPlugin({ template: "./src/index.html" }),
];

模式(Mode):

  • 模式决定了 Webpack 是为开发环境还是生产环境构建应用程序。它可以是 developmentproductionnone。模式影响 Webpack 的优化和警告。
js 复制代码
  // 模式
  mode: 'production', //development

目标(Target):

用于告知 Webpack 为哪个环境或目标(target)进行构建编译 , 默认情况下,target 的值是 "web"

js 复制代码
  // 模式
  target: 'web', //node、webworker 。。。。

webpack 的 runtime 和 manifest

  • manifest 包含文件一般包含 ,如模块及模块依赖、每个模块的源码映射、异步加载的模块,等。
  • runtime 会通过 manifest 来解析和加载模块。

服务器

配置开发服务器,包括代理、热模块替换等

js 复制代码
  devServer: {
  // 设置开发服务器监听的主机地址
  // '0.0.0.0' 表示服务器将接受所有可用网络接口的请求
  host: '0.0.0.0',
  // 设置开发服务器监听的端口号
  port: 3000,
  // 设置为 true 时,当 webpack 编译完成后,会自动在浏览器中打开应用
  open: true,
  // 启用模块热替换(HMR),允许在不刷新整个页面的情况下,实时更新模块
  hot: true,
  // 为单页应用(SPA)提供支持,当访问不存在的路由时,会返回 index.html
  historyApiFallback: true,
  // 开启 gzip 压缩,提高传输效率
  compress: true,
  // 指定了 public 目录下的文件作为静态资源
  // 当你访问如 /js/index.js 这样的路径时,devServer 会从 public 目录提供文件
  static: {
    directory: path.join(__dirname, 'public')
  },

  // 配置代理,用于开发环境的后端接口代理
  proxy: {
    // 当请求前缀为 /api 时,触发代理
    '/api': {
      // 代理目标服务器的地址
      target: 'http://backend.server.com',
      // 配置是否改变请求头中的origin字段
      changeOrigin: true,
      // URL 重写,这里将请求中的 /api 替换为空字符串   例如,将 /api/user 重写为 /user
      pathRewrite: { '^/api': '' },
    },
  },
},

模块解析配置

js 复制代码
resolve: {
    alias: { //创建 import 或 require 的别名
      @: path.resolve(__dirname, 'src/'),
      _: path.resolve(__dirname, 'src/utils/'),
    },

    extensions: ['.js', '.jsx', '.ts','.tsx'], //尝试按顺序解析这些后缀名
    modules: [path.resolve(__dirname, 'src'), 'node_modules'], //告诉 webpack 解析模块时应该搜索的目录。
  },

优化配置

配置代码压缩、优化分割代码块。

js 复制代码
  // 优化
 optimization: {
  minimize:true, // 开启优化
  // minimizer 属性用于配置压缩工具,用于压缩 JavaScript 和 CSS 文件
  minimizer: [
    // TerserPlugin 是一个插件,用于压缩 JavaScript 文件
    new TerserPlugin({
      // terserOptions 用于传递给 Terser 的选项
      terserOptions: {
        // compress 属性用于配置压缩选项
        compress: {
          // drop_console 选项设置为 true,将删除所有的 console 语句
          drop_console: true
        }
      },
      // extractComments 选项设置为 false,以防止插件提取注释到单独文件
      extractComments: false
    }),
    // OptimizeCSSAssetsPlugin 用于压缩 CSS 文件
    new OptimizeCSSAssetsPlugin({})
  ],

  // splitChunks 属性用于配置代码分割选项
  splitChunks: {
    // chunks 属性用于指定应该分割哪些 chunks,'all' 表示所有 chunks
    chunks: 'all',
    // minSize 属性定义了分割出的 chunk 的最小大小,这里是 20000 字节
    minSize: 20000,
    // minRemainingSize 属性定义了分割后剩余模块的最小大小,这里设置为 0
    minRemainingSize: 0,
    // minChunks 属性定义了模块应该被分割的最小 chunk 数量,这里是 1
    minChunks: 1,
    // maxAsyncRequests 属性定义了按需加载时的最大并行请求数,这里是 5
    maxAsyncRequests: 5,
    // maxInitialRequests 属性定义了初始加载时的最大并行请求数,这里是 3
    maxInitialRequests: 3,
    // automaticNameDelimiter 属性定义了自动生成的 chunk 名称之间的分隔符
    automaticNameDelimiter: '~',
    // automaticNameMaxLength 属性定义了自动生成的 chunk 名称的最大长度,这里是 30
    automaticNameMaxLength: 30,
    // cacheGroups 属性用于配置缓存组,用于更细致地控制代码分割
    cacheGroups: {
      // defaultVendors 缓存组用于处理来自 node_modules 的第三方库
      defaultVendors: {
        // test 属性用于定义哪些模块应该被包含在这个缓存组
        test: /[\\/]node_modules[\\/]/,
        // priority 属性定义了这个缓存组的优先级,-10 表示较低优先级
        priority: -10,
        // reuseExistingChunk 选项设置为 true,表示尽可能重用现有的 chunk
        reuseExistingChunk: true
      },
      // default 缓存组用于处理应用程序代码
      default: {
        // minChunks 属性定义了模块应该被分割的最小 chunk 数量,这里是 2
        minChunks: 2,
        // priority 属性定义了这个缓存组的优先级,-20 表示较低优先级
        priority: -20,
        // reuseExistingChunk 选项设置为 true,表示尽可能重用现有的 chunk
        reuseExistingChunk: true
      }
    }
  }
},

热模块替换(Hot Module Replacement, HMR):

  • HMR 是一种功能,允许在应用程序运行时替换、添加或删除模块,而无需重新加载整个页面。
  • 原理:
    1. 浏览器和服务器建立socket 通道
    2. 修改代码后,webpack compiler 发出update事件,
    3. hmr 请求 manifest和chunk,根据页面内容配置,更新模块
js 复制代码
// webpack.config.js

devServer:{
  hot:true
}

plugins: [
  new webpack.HotModuleReplacementPlugin()
]


// component.js
 function component() {
    const element = document.createElement('input');
    element.value = 123;
    return element;
  }

  let element = component();
  document.body.appendChild(element);

  if (module.hot) {
    module.hot.accept('./print.js', function() {
      console.log('热更新进入!');
      const value = document.getElementsByTagName("input")[0].value.value;
      element = component(); // 重新渲染 component 以更新点击事件处理程序
      document.body.appendChild(element);
      // 回显数据
      document.getElementsByTagName("input")[0].value.value = value
    })
  }

代码分割(Code Splitting):

复制代码
- 代码分割是 Webpack 的一个特性,可以将代码分割成多个块,这些块可以按需加载或者并行加载,从而优化加载时间和性能。
- 代码拆分手段:import 动态导入、ensure方法、SplitChunksPlugin插件、多入口
  1. 动态导入(Dynamic Imports)

动态导入是实现代码分割最简单的方式之一。你可以使用 import() 函数来动态地加载模块。

javascript 复制代码
// 1. 直接使用
if (someCondition) {
  import("./otherModule.js").then((otherModule) => {
    otherModule.doSomething();
  });
}

// 2. promise 返回
function loadModule(modulePath) {
  return new Promise((resolve, reject) => {
    import(modulePath).then(
      (module) => {
        resolve(module);
      },
      (err) => {
        reject(err);
      }
    );
  });
}

// 动态加载模块
loadModule("./otherModule.js")
  .then((module) => {
    module.doSomething();
  })
  .catch((err) => {
    console.error("模块加载失败", err);
  });

在 Webpack 配置中,你不需要做任何特别的配置来处理动态导入,因为 Webpack 已经自动支持了。

  1. 使用 require.ensure

require.ensure 是一个更老的代码分割方法,require.ensure() 是 webpack 特有的,已被 import() 取代。。

javascript 复制代码
// 参数1:  dependencies 依赖数组
// 参数2: callback 回调函数
// 参数3:  errorCallback: 失败回调
// 参数4:  chunkName
// Webpack会创建一个名为 `feature` 的新chunk。
require.ensure(
  [],
  () => {
    const module = require("./module");
    module.someFunction();
  },
  "feature"
);
  1. SplitChunksPlugin

SplitChunksPlugin 是 Webpack 4+ 引入的一个插件,用于控制代码块的分割逻辑。以下是一个基本的配置示例:

javascript 复制代码
const webpack = require("webpack");

module.exports = {
  // ...
  optimization: {
    splitChunks: {
      chunks: "all", // 代码分割的chunk类型,可选值有 'initial'、'async' 或 'all'
      minSize: 20000, // 最小chunk大小,默认单位是byte
      minChunks: 1, // 最小chunk数量,默认是1
      maxAsyncRequests: 5, // 按需加载时的最大并行请求数
      maxInitialRequests: 3, // 入口点的最大并行请求数
      automaticNameDelimiter: "~", // 分割出的chunk文件名的连接符
      cacheGroups: {
        defaultVendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          reuseExistingChunk: true,
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
  },
  // ...
};
  1. 多入口拆分

如果你想要手动拆分第三方库,可以在 entry 属性中指定:

javascript 复制代码
entry: {
  bundle: './src/index.js',
  vendor: ['react', 'react-dom'] // 将React和ReactDOM拆分出去
},

output: {
  filename: '[name].js' // 根据entry的key来命名
},

optimization: {
  splitChunks: {
    cacheGroups: {
      vendor: {
        test: /[\\/]node_modules[\\/]/,
        name: 'vendor',
        chunks: 'all'
      },
    },
  },
},
  1. 摇树优化(Tree Shaking)

    • 摇树优化是一种去除应用程序中未引用代码的技术,通常与 ES6 模块一起使用。
  2. 懒加载(Lazy Loading)

    • 懒加载是一种按需加载模块的技术,可以减少初始加载时间。
  3. 配置(Configuration)

    • Webpack 的配置是通过一个名为 webpack.config.js 的文件来进行的,它定义了入口、输出、加载器、插件等。
  4. 解析(Resolving)

    • 解析是 Webpack 确定如何处理文件的过程,包括如何找到文件、如何解析文件中的模块请求等。
  5. 缓存(Caching)

    • Webpack 通过缓存来提高构建性能,它会跳过未发生变化的模块,避免不必要的重新构建。
  6. 记录(Profiling)

    • Webpack 可以生成构建的记录,帮助开发者分析和优化构建过程。
  7. 持久化(Persistence)

    • Webpack 4 引入了持久化缓存,即使在构建之间也可以保持缓存状态。

在 Node 中使用 webpack

js 复制代码
const webpack = require("webpack"); // 访问 webpack 运行时(runtime)
const configuration = require("./webpack.config.js");

let compiler = webpack(configuration); //传入配置

new webpack.ProgressPlugin().apply(compiler);

compiler.run(function (err, stats) {
  if (err) {
    console.log("编译出错");
  }
});
相关推荐
To_OC7 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
vipbic7 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy7 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志8 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble9 小时前
c端系统,其实更像一个信息展示平台
前端
李姆斯10 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜10 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司11 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能
kaiking_g11 小时前
vue项目中,静态导入 vs 动态导入 详解
前端·javascript·vue.js
江华森11 小时前
Web 开发基础:HTTP 与 REST
前端·网络协议·http