Webpack 基础入门篇

Webpack 是面向现代 JavaScript 应用的静态模块打包工具。在处理项目时,Webpack 会从一个或多个入口构建完整的依赖图(dependency graph),把项目用到的全部模块打包输出为若干份静态 bundle 文件,用于浏览器页面加载使用。

image-20260825202914463.png

模块打包简单理解:识别模块之间的依赖关系,按照约定规则将模块合并处理为 JavaScript 文件,也可以拆分输出为多种不同类型的资源文件。

Webpack 的设计理念是万物皆模块,JS 文件、CSS、图片、字体等资源,在 Webpack 眼中都属于模块。

核心概念

  • 入口(entry):指定构建的起始模块,作为依赖图的起点,一般为 JS 文件。
  • 输出(output):配置打包产物的输出路径、文件命名规则,也支持配置类库导出相关参数。
  • loader:模块转换器,处理非 JS 类型模块,将各类资源转为 Webpack 可识别的有效模块。
  • 插件(plugin):执行范围更广的构建任务,弥补 loader 能力边界,用于打包优化、资源处理、环境变量注入等。
  • 模式(mode):可选值 developmentproductionnone,Webpack 根据模式启用对应的内置优化策略。

浏览器兼容性:Webpack 支持所有符合 ES5 标准的浏览器,不支持 IE8 及更低版本。其中 import()require.ensure() 依赖 Promise;如果需要兼容老旧浏览器,要提前引入 polyfill。

快速上手示例

Webpack 基于 Node.js,运行前请确保本地已经安装 Node.js 环境。

npm i webpack webpack-cli -g
// src/assets/js/a.js
console.log(1)
// src/index.js
import './assets/js/a.js'
console.log(2)

Webpack 支持零配置运行:默认入口文件为 src/index.js,产物输出到 dist/main.js;生产模式会自动开启代码压缩优化。实际项目一般会编写配置文件扩展能力。

配置文件默认放在项目根目录,命名为 webpack.config.js;也可以自定义存放路径,执行构建时通过 --config 参数指定。

备注:配置文件支持导出普通对象、函数、Promise,也可以导出配置数组实现多份构建。

build/webpack.demo.config.js 示例:

const Path = require('path')
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'index.js'
  }
}

执行构建命令:

webpack --config ./build/webpack.demo.config.js

entry 入口配置

entry 是 Webpack 构建依赖图的起点。Webpack 从入口模块递归查找所有直接、间接依赖的模块与第三方库。

默认入口:./src/index.js,通过 entry 属性自定义入口。

字符串形式:单入口,生成单个 bundle

module.exports = {
  entry: './path/to/my/entry/file.js'
};
// 等价简写
module.exports = {
  entry: {
    main: './path/to/my/entry/file.js'
  }
};

数组形式:多入口合并输出为单个 bundle

module.exports = {
  entry: ['./src/index.js', './src/assets/js/b.js']
};

对象形式:多入口生成多个独立 bundle

使用对象多入口时,output.filename 必须使用占位符区分文件名。

module.exports = {
  entry: {
    main: './src/index.js',
    otherB: './src/assets/js/b.js'
  },
  output: {
    filename: '[name]-[hash:16]-[id].js'
  }
}

注意key 中包含 / 会解析为目录路径

module.exports = {
  entry: {
    'main/assets/index': './src/index.js',
  }
}

构建输出目录结构:

dist
└── main
    └── assets
        └── index.js

函数、Promise:动态获取入口。支持返回函数或者 Promise,适合运行时动态计算入口路径。

output 输出配置

output 用来定义打包产物输出位置、文件命名规则。单份 Webpack 配置只能有一个 output

默认主产物输出:./dist/main.js,全部产物默认放置于 dist 目录。

const path = require('path');
module.exports = {
  entry: './path/to/my/entry/file.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js'
  }
};

filename

指定输出 bundle 的文件名;多入口场景必须使用占位符保证文件名唯一。

模板 说明
[hash] 项目整体构建 hash;项目任意文件变更,hash 全部更新
[chunkhash] 对应 chunk 内容的 hash,chunk 内容不变则 hash 不变
[name] 入口 chunk 的名称,对应 entry 对象 key
[id] chunk 的标识符
[query] 文件名后 ? 携带的查询字符串

hash 长度可指定,例如 [hash:16],默认长度 20;也可以全局配置 hashDigestLength 修改。

filename 还支持传入函数,返回带占位符的字符串。

chunkFilename

配置非入口 chunk(异步动态导入)的文件名。

默认值:[id].js,或从 filename 推断。

备注:异步 chunk 的文件名运行时才确定,占位符会写入运行时代码,会增大 bundle 体积。

path

输出目录,必须是绝对路径,默认 dist

const Path = require('path')
module.exports = {
  output: {
    filename: '[name]-[hash:16]-[id].js',
    path: Path.resolve(__dirname, '../abcd/assets')
  }
}

publicPath

浏览器访问静态资源的基础路径,会作用于 html 内 script、link 等资源地址。支持相对路径、服务根路径、协议相对路径、完整 CDN 绝对路径。

module.exports = {
  entry: './src/index.js',
  output: {
    // publicPath: './assets/js', // 相对 HTML 的相对路径
    // publicPath: '/assets/js',  // 服务根路径
    // publicPath: '//cdn.example.com', // 协议相对路径
    publicPath: 'https://cdn.example.com', // CDN 绝对路径
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ]
}

生成 HTML:

<script src="https://cdn.example.com/main.js"></script>

编译阶段不确定 publicPath 时,可以在入口 JS 使用内置变量 __webpack_public_path__ 在运行时动态赋值。

__webpack_public_path__ = myRuntimePublicPath;

library、libraryExport、libraryTarget

该组配置用于打包第三方类库、SDK,把代码输出为可供外部调用的模块,普通业务 SPA 项目一般不需要配置。

  • library:定义对外暴露的库名称,可以是字符串,也可以是对象,为不同模块规范设置不同导出名。
  • libraryTarget:指定输出的模块规范,决定产物以什么形式对外导出。常用取值:
    • umd:通用模块定义,同时兼容浏览器全局、AMD、CommonJS,写类库最常用;
    • commonjs2:输出 Node.js 的 module.exports
    • amd:输出 AMD 规范模块;
    • window:挂载到浏览器全局 window 对象。
  • libraryExport:指定导出模块内部哪一部分,默认导出整个模块;可指定 default 只导出默认导出,也支持数组路径导出嵌套对象。

umd 多环境兼容输出示例:

module.exports = {
  output: {
    library: {
      root: 'MyLibrary',
      amd: 'my-library',
      commonjs: 'my-common-library'
    },
    libraryTarget: 'umd',
    libraryExport: 'default' // 只导出模块的 default 导出内容
  }
}

sourceMapFilename

source-map 文件输出文件名,仅开启 devtool source-map 时生效,默认 [file].map

占位符支持 [name][id][hash][chunkhash][file][filebase]

其他 output 属性

  • sourcePrefix:修改 bundle 每行字符串前缀,美化 sourcemap,默认空字符串。
  • auxiliaryComment:给库导出容器插入注释。
  • chunkLoadTimeout:异步 chunk 请求超时毫秒,默认 120000
  • crossOriginLoading:script 加载异步 chunk 的 crossorigin 属性。
  • jsonpScriptType:异步 chunk 的 script 标签 type。
  • devtoolModuleFilenameTemplatedevtoolFallbackModuleFilenameTemplate:source-map sources 路径模板。
  • devtoolNamespace:source-map 模块命名空间,防止多库 sourcemap 冲突。
  • hashDigesthashDigestLengthhashFunctionhashSalt:hash 算法与编码相关配置。
  • hotUpdateChunkFilenamehotUpdateMainFilename:热更新相关文件名。
  • hotUpdateFunction:热更新 JSONP 回调函数。
  • jsonpFunction:按需加载 chunk JSONP 回调函数。
  • pathinfo:bundle 内输出模块注释;development 默认开启,production 默认关闭。
  • strictModuleExceptionHandling:require 模块报错时清理模块缓存,默认关闭。
  • umdNamedDefine:umd 模式下给 AMD 模块命名。

module 模块配置

module 配置 Webpack 如何处理各类模块文件。

noParse

标记某些文件不需要 Webpack 解析;文件内不能包含 import、require、define。支持字符串、正则、数组、函数。

module.exports = {
  module: {
    noParse: 'jquery',
    noParse: ['jquery', 'lodash'],
    noParse: /jquery|lodash/,
    noParse: (content) => /jquery|lodash/.test(content)
  }
};

rules

模块处理规则集合,为不同文件指定 loader、parser。

一条 rule 由三部分构成:

  • 条件 condition:匹配模块。分为 resource(被加载的文件路径)、issuer(导入该文件的父模块路径)。testincludeexclude 匹配 resourceissuer 匹配父模块。多个条件需要全部满足。
  • 结果 result:指定 loader 或者 parser 配置。
  • 嵌套规则:支持 rulesoneOf 做更细粒度分支匹配。

loader 加载器

Webpack 原生只识别 JS、JSON 文件。loader 将其它类型文件转为 Webpack 可识别的模块,加入依赖图

备注:loader 属于 Webpack 特有扩展,其他打包工具不一定支持。

两种使用方式

配置方式(webpack.config.js)test 匹配文件,use 指定 loader;多个 loader 顺序从右向左执行

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

备注:正则不要加引号,/\.txt$/ 代表匹配后缀 txt 的文件。

内联方式 import 中书写:使用 ! 分隔多个 loader,执行顺序从右向左

import Styles from 'style-loader!css-loader?modules!./styles.css';

前缀可以覆盖配置内 loader:

  • !xxx:关闭普通 normal loader
  • !!xxx:关闭全部 pre、normal、post loader
  • -!xxx:关闭 pre、normal,保留 post loader
import Styles from '!style-loader!css-loader?modules!./styles.css';

参数支持 query 字符串,或者 JSON 对象,例如 ?key=value?{"importLoaders":1}

rules 条件语法

属于 rule.condition 的扩展配置,用来精细化控制文件匹配逻辑,写在 rule 顶层。

条件支持字符串、正则、函数、条件数组、条件对象;支持 testincludeexcludeandornot

resourceQuery 匹配 URL 查询参数,例如 import './foo.css?inline'

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        resourceQuery: /inline/,
        use: 'url-loader'
      }
    ]
  }
}

use

指定 loader 数组,从右向左(由后往前)执行;支持字符串、对象、返回数组的函数。

module.exports = {
  module: {
    rules: [
      {
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 1
            }
          },
          {
            loader: 'less-loader',
            options: {
              noIeCompat: true
            }
          }
        ]
      }
    ]
  }
};

备注Rule.loader 等价 Rule.use:[{loader}]Rule.options 等价 Rule.use:[{options}]

parser

开启、关闭模块解析语法(amd、commonjs、harmony、require.context 等)。

module.exports = {
  module: {
    rules: [
      {
        parser: {
          amd: false,
          commonjs: false,
          harmony: false
        }
      }
    ]
  }
}

oneOf

匹配多条规则时,只取第一条命中的规则。适合同一个后缀,根据 query 参数走不同 loader。

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        oneOf: [
          {
            resourceQuery: /inline/,
            use: 'url-loader'
          },
          {
            resourceQuery: /external/,
            use: 'file-loader'
          }
        ]
      }
    ]
  }
};

rule 其他属性

  • enforcepre 前置 loader、post 后置 loader;不设置为普通 loader。
  • issuer:匹配导入当前模块的父模块路径。
  • type:设置模块类型,javascript/autojsonwebassembly/experimental,覆盖默认解析行为。

plugin 插件

loader 负责模块转换,plugin 负责更广泛的构建任务,做 loader 无法实现的能力:打包优化、资源输出、环境变量注入、文件复制等。

使用插件:require 引入插件包,在 plugins 数组实例化;同一个插件可以多次 new 实例。

const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
  module: {
    rules: [
      { test: /\.txt$/, use: 'raw-loader' }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]
};

resolve 模块解析

控制 Webpack 如何寻找 import、require 的模块。

alias

路径别名,简化导入路径。

module.exports = {
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
    }
  }
}

使用别名:

// import utils from '../../../utils/index.js';
import utils from '@/utils/index.js';

extensions

自动补全导入文件扩展名,默认 ['.wasm', '.mjs', '.js', '.json']

mainFields

解析 npm 包时读取 package.json 的字段;target 为 web、webworker 默认 ['browser','module','main'];node 环境默认 ['module','main']

mainFiles

解析目录时默认查找的文件名,默认 ['index']

modules

查找模块的目录,默认 ['node_modules'];支持绝对路径与相对路径。

resolveLoader

专门用于解析 loader 包,配置项和 resolve 一致。可以设置 loader 别名。

module.exports = {
  resolveLoader: {
    alias: {
      txt: 'raw-loader'
    }
  }
};

resolve 其余配置

  • aliasFields:读取 package.json 中的别名字段,典型如 browser,用于浏览器环境替换模块;
  • descriptionFiles:读取包描述的 JSON 文件,默认读取 package.json
  • enforceExtension:是否强制导入文件必须带扩展名;
  • enforceModuleExtension:针对模块导入强制扩展名;
  • symlinks:是否跟随符号链接,开启会解析符号链接指向真实文件,关闭直接使用链接路径;
  • unsafeCache:缓存解析结果,提升构建速度;正则可以指定哪些模块开启缓存;
  • cachePredicate:自定义函数,判断某个模块解析结果是否加入缓存。

externals 外部扩展

将部分依赖排除在打包产物之外,运行时外部 CDN、环境提供该模块,减小打包体积。

例如 HTML 通过 CDN 引入 jQuery:

<script src="https://code.jquery.com/jquery-3.1.0.js"></script>

webpack 配置:

module.exports = {
  externals: {
    jquery: 'jQuery'
  }
};

业务代码仍然可以正常 import,但是产物不包含 jQuery:

import $ from 'jquery';
$('.my-element');

支持配置对象、数组、函数、正则,兼容 rootcommonjscommonjs2amdumd

module.exports = {
  externals: {
    jquery: 'jQuery',
    subtract: ['./math', 'subtract'],
    lodash : {
      commonjs: 'lodash',
      amd: 'lodash',
      root: '_'
    }
  }
};

mode 模式

可选 developmentproductionnone,启用 Webpack 内置优化,默认 production

module.exports = {
  mode: 'production'
};
  • development:设置 process.env.NODE_ENV=development,开启命名模块插件,便于调试。
  • production:设置 process.env.NODE_ENV=production,开启压缩、tree-shaking、模块合并等全套生产优化。
  • none:关闭全部内置优化。

devServer 开发服务器

仅用于开发环境,不要部署生产环境。 webpack-dev-server 会启动内存 web 服务器,提供静态资源访问、热模块替换 HMR、接口代理、history 路由 fallback 等开发能力;默认不会输出文件到本地磁盘,全部资源保存在内存。底层依赖 http-proxy-middleware 中间件。

常用配置

  • contentBase:提供静态资源的目录,推荐绝对路径;新版本 webpack 使用 static 替代该配置。
  • host:服务主机,0.0.0.0 允许局域网其他设备访问本机服务,默认 localhost
  • port:本地监听端口号。
  • open:启动服务后自动打开浏览器;可以指定浏览器名称。
  • hot:开启模块热替换 HMR,修改模块不刷新整个页面,只更新变更模块。
  • hotOnly:构建报错时,依然保留旧模块的 HMR,不整页刷新。
  • overlay:浏览器全屏弹窗展示编译错误与警告。
  • historyApiFallback:SPA history 模式路由,访问路由 404 时重定向到 index.html,也支持自定义重写规则。
  • https:开启 HTTPS,支持使用自签名证书,也可以传入自定义证书密钥。
  • publicPath:开发服务器内打包资源访问基础路径。
  • writeToDisk:把内存中的打包产物写入本地磁盘,一般调试时开启。
  • compress:开启 gzip 压缩,返回压缩后的静态资源。
  • beforeafter:注册自定义中间件,在 dev-server 内部中间件之前、之后执行,可以拦截请求、mock 接口。

proxy 跨域代理

解决前端开发跨域问题,转发 API 请求到后端服务。

简单代理:

// localhost:9600/api/users → http://localhost:3000/api/users
module.exports = {
  devServer: {
    proxy: {
      '/api': 'http://localhost:3000'
    }
  }
};

完整配置,路径重写、修改 cookie:

// localhost:9600/api/users → http://localhost:3000/users
module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'http://localhost:3000',
        changeOrigin: true,
        pathRewrite: {
          '^/api': ''
        },
        cookieDomainRewrite: {
          "old.domain": "new.domain"
        }
      }
    }
  }
};

devServer 其他属性

stats 控制控制台构建日志输出等级、watchContentBase 监听静态目录文件变更、allowedHosts 设置访问域名白名单等。

顶层其他配置

context

基础目录,绝对路径;解析 entry、loader 的相对路径以此目录为基准,默认执行 webpack 的当前工作目录。

target 构建目标

告知 webpack,打包出来的代码运行在哪种宿主环境,webpack 会根据目标自动注入对应 polyfill、修改模块解析规则、加载内置插件。

常用取值:

  • web:默认,输出浏览器环境运行代码;
  • webworker:输出 web worker 脚本;
  • node:输出 Node.js 服务端代码;
  • electron-main:electron 主进程产物;
  • electron-renderer:electron 渲染进程产物;
  • async-node:适配老版本 node,异步加载 chunk。

注意:target 只修改构建适配逻辑,不会自动转换 JS 语法,ES6+ 转 ES5 仍然需要 babel-loader。

应用示例

raw-loader

读取文件内容作为字符串导入。

<script>${require('raw-loader!./meta.html')}</script>

html-webpack-externals-plugin

自动处理 externals,复制 node_modules 静态资源并注入 HTML。

const webpackConfig = {
  plugins: [
    new HtmlWebpackPlugin(),
    new HtmlWebpackExternalsPlugin({
      externals: [
        {
          module: 'vue',
          entry: 'https://unpkg.com/vue@3/dist/vue.global.js',
          global: 'Vue',
        }
      ]
    }),
  ]
}

vue.config.js 开启 eslint-loader 自动修复

module.exports = {
  chainWebpack: config => {
    if (process.env.NODE_ENV !== "production") {
      config.module
        .rule("eslint")
        .use("eslint-loader")
        .options({
          fix: true
        });
    }
  }
};

常用完整配置

完整配置应包含:入口输出、CSS 处理、资源处理、HTML 生成、devServer、sourceMap、环境变量、代码压缩、清理输出目录等高频配置。

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  mode: 'development',
  entry: './src/main.js',
  output: {
    filename: 'js/bundle.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/'
  },
  devtool: 'cheap-module-eval-source-map',
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              ['@babel/preset-env']
            ]
          }
        }
      },
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader'
        ]
      },
      {
        test: /\.(png|jpg|jpeg|gif|svg)$/,
        loader: 'url-loader',
        options: {
          limit: 8 * 1024,
          name: 'images/[name].[hash:8].[ext]'
        }
      },
      {
        test: /\.(woff|woff2|ttf|eot|otf)$/,
        loader: 'file-loader',
        options: {
          name: 'fonts/[name].[hash:8].[ext]'
        }
      }
    ]
  },

  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src')
    },
    extensions: ['.js', '.json']
  },

  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, './src/index.html'),
      filename: 'index.html',
      title: 'Webpack4 Demo',
      minify: false // 开发环境不压缩html
    })
  ],

  devServer: {
    contentBase: path.resolve(__dirname, 'dist'),
    host: '0.0.0.0',
    port: 8080,
    open: true, // 启动自动打开浏览器
    hot: true, // 开启热模块替换 HMR
    compress: true, // gzip压缩
    overlay: true, // 浏览器页面上显示错误
    historyApiFallback: true, // SPA history路由404重定向

    proxy: {
      '/api': {
        target: 'http://127.0.0.1:3000',
        changeOrigin: true,
        pathRewrite: {
          '^/api': ''
        }
      }
    }
  },

  optimization: {
    minimize: false
  }
};

常见问题

Webpack 和 Gulp、Grunt 的区别

Gulp、Grunt 属于任务运行工具(Task Runner),核心是定义一系列自动化任务,按顺序执行。

  • Grunt:基于配置文件描述任务,每个任务处理完把结果写入磁盘临时目录,大量磁盘 IO,构建速度慢;擅长压缩、编译 sass、文件复制、代码校验这类通用文件处理。它不会分析 JS 的 import、require 依赖,只是遍历匹配到的文件,无脑执行任务,不管文件是否真正被项目使用。
  • Gulp:基于 Node 流(Stream),处理流程在内存流转,减少磁盘读写,速度更快。同样是任务驱动,需要手动编排任务执行顺序;本身不做 JS 模块依赖解析。

Webpack 是模块打包工具,和任务工具定位完全不同。

  • 入口文件为起点,自动递归扫描所有 import、require,生成完整依赖图,只打包项目真正用到的代码;
  • 内置模块处理、代码分割、tree-shaking、按需加载能力,专门解决前端 JS、CSS、静态资源的模块依赖问题;
  • 同时也可以借助插件完成压缩、编译、复制文件这类 Gulp、Grunt 的工作。

简单对比:

  • Gulp、Grunt:我拿到一批文件,按照配置对每个文件做 A、B、C 操作;
  • Webpack:我分析代码依赖,只把需要的模块组织合并输出。

在早期项目中,经常会 Gulp + Webpack 配合使用:webpack 负责模块打包,gulp 负责做部署、静态资源拷贝等外围任务。现在很多项目直接使用 webpack 插件、npm scripts 替代 Gulp、Grunt。

Webpack 和 Rollup 的区别

  • Rollup:面向类库开发,原生充分利用 ES Module,打包产物干净扁平,运行时代码少,适合开发工具库。不擅长处理图片、CSS 等异构资源,HMR 能力弱。
  • Webpack:面向复杂 SPA 应用;强大 loader 处理各种异构资源、内置 HMR、代码分割、公共代码提取,适合业务应用项目。

Webpack 识别哪些算模块

  • ES Module importexport
  • CommonJS require()
  • AMD definerequire
  • CSS 的 @import
  • 样式 url()、HTML src

loader 可以扩展识别更多格式模块。

Webpack 模块路径解析逻辑

enhanced-resolve 库负责解析模块绝对路径。

  • 绝对路径:直接使用。
  • 相对路径:以当前导入文件所在目录拼接。
  • 模块路径(不带 ./:去 resolve.modules 目录查找;配合 alias 别名。

找到路径后区分文件、目录:

  • 文件:尝试追加 resolve.extensions 扩展名。
  • 目录:读取 package.json 的 mainFields,找不到则读取目录下 mainFiles

commonjs 与 commonjs2 的区别

  • commonjs:导出为 exports['xxx'] = value
  • commonjs2:导出为 module.exports = value,Node.js 使用这套。

备注:Node.js 中 exports 只是 module.exports 的引用,不能直接赋值覆盖。

// libraryTarget: 'commonjs'
exports['MyLibrary'] = _entry_return_;

// libraryTarget: 'commonjs2'
module.exports = _entry_return_;

参考资料

Webpack 中文文档

© lizhao all right reserved,powered by Gitbook文件修订时间: 2026-08-25 20:54:38

results matching ""

    No results matching ""