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("编译出错");
  }
});
相关推荐
沉默璇年12 分钟前
react中useMemo的使用场景
前端·react.js·前端框架
yqcoder18 分钟前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript
2401_8827275727 分钟前
BY组态-低代码web可视化组件
前端·后端·物联网·低代码·数学建模·前端框架
会发光的猪。1 小时前
css使用弹性盒,让每个子元素平均等分父元素的4/1大小
前端·javascript·vue.js
天下代码客1 小时前
【vue】vue中.sync修饰符如何使用--详细代码对比
前端·javascript·vue.js
猫爪笔记1 小时前
前端:HTML (学习笔记)【1】
前端·笔记·学习·html
前端李易安2 小时前
Webpack 热更新(HMR)详解:原理与实现
前端·webpack·node.js
红绿鲤鱼2 小时前
React-自定义Hook与逻辑共享
前端·react.js·前端框架
Domain-zhuo2 小时前
什么是JavaScript原型链?
开发语言·前端·javascript·jvm·ecmascript·原型模式
小丁爱养花2 小时前
前端三剑客(三):JavaScript
开发语言·前端·javascript