Skip to content
webpack 基本用法:配置文件与常见资源处理
初始化项目并安装 webpack 与 webpack-cli
webpack 运行在 Node.js 环境,项目需要先创建 package.json。在项目根目录执行:
shell
npm init -y这会生成一个默认的包描述文件,随后安装 webpack 本体和它的命令行工具:
shell
npm install --save-dev webpack webpack-cliwebpack-cli 负责把命令行参数转换成对 webpack 编译器的调用。安装完成后,可以直接通过 npx webpack 来运行构建,而不需要全局安装任何东西。
创建最小配置文件
webpack 4 之后支持零配置运行,默认入口是 src/index.js,默认输出是 dist/main.js。实际开发中通常会用到配置文件,这里直接建立一个 webpack.config.js。
配置文件本身是一个运行在 Node.js 中的 CommonJS 模块,必须通过 module.exports 导出配置对象:
js
// webpack.config.js
const path = require('path');
module.exports = {
mode: 'none',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};mode 是 webpack 内建的优化策略开关。none 表示不做任何内置优化,方便观察打包结果。development 会启用有助于调试的默认配置(如保持模块名称),production 则开启代码压缩等优化。这两种模式的具体行为会在后续深入,目前用 none 可以看清楚 loader 和资源模块的真实输出。
入口与出口
entry 是依赖图的起点,webpack 从它开始递归地解析 import 或 require,把所有关联模块都拉进来。值的写法除了字符串,还可以是数组或对象,这里先使用最简单的字符串形式。
output 决定最终生成文件的存放位置。output.path 必须是绝对路径,因此通常使用 path.resolve(__dirname, 'dist') 这样的写法来拼接。output.filename 用于指定输出文件名,可以包含占位符(如 [name].bundle.js),但单入口时直接给一个固定名称就行。
有了这份配置,在 src/index.js 中写一段简单的 ES6 代码,像这样:
js
// src/index.js
const message = 'hello webpack';
console.log(message);执行 npx webpack,会在 dist/ 目录下生成 bundle.js。用 Node 运行它:
shell
node dist/bundle.js控制台会打印 hello webpack。这个阶段还没有引入任何 loader,如果 src/index.js 里使用了箭头函数等 ES6 语法,打包后的代码会原样保留。在需要兼容老版本浏览器时就会出问题,这正是需要通过 loader 转换的场景。
module.rules 与 loader 执行顺序
webpack 的模块系统只能处理 JavaScript 和 JSON 文件,所有其他类型的文件(CSS、图片、ES6+ 语法转换)都要靠 loader 来“翻译”成 webpack 可识别的模块。
loader 通过 module.rules 数组配置,每个规则对象中最关键的字段是:
test:匹配文件名的正则表达式。use:要使用的 loader,可以是字符串、对象或数组。多个 loader 会组成一个从右向左的链式调用。
执行顺序是从右向左,类似于函数组合:数据先经过最后面的 loader,再依次经过前面的 loader,最终将结果交给 webpack。一个典型的 CSS 规则就是这样:
js
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}这里的处理顺序是:.css 文件先被 css-loader 解析(处理 @import 和 url()),然后把结果传给 style-loader,由后者把 CSS 字符串注入到 DOM 的 <style> 标签里。若顺序写反,style-loader 无法直接解析 CSS 文件,构建会报错。
后续 Babel 和 CSS 的配置都会沿用这种写法,理解执行顺序是排查 loader 问题的基础。
使用 Babel 处理 ES6 代码
Babel 是一个负责把新标准 JavaScript 转成向下兼容版本的编译器,而 webpack 通过 babel-loader 调用 Babel。安装三个必要包:
shell
npm install --save-dev babel-loader @babel/core @babel/preset-env@babel/core 是 Babel 的核心编译器,@babel/preset-env 是一组预先配置好的插件集合,能根据环境自动确定需要转换的语法。
然后在 webpack.config.js 里添加 rule:
js
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
}exclude: /node_modules/ 可以避免去处理别人已经转译好的第三方包,提升构建速度。options.presets 也可以写在一个独立的 .babelrc 或 babel.config.json 里,但直接放在配置中比较直观。
现在把 src/index.js 改成明显的 ES6 语法:
js
const greet = () => {
console.log('hello babel');
};
greet();执行 npx webpack 后,打开 dist/bundle.js(由于 mode: 'none' 不会有压缩)就能看到箭头函数被转换为了普通函数,const 也变成了 var。运行 node dist/bundle.js 同样会打印 hello babel。
使用 style-loader 与 css-loader 处理 CSS
要让 webpack 把 CSS 当作模块的一部分,需要在入口文件里显式 import 样式。先在项目中新建 src/style.css:
css
body {
background-color: #f4f4f4;
color: #333;
}然后在 src/index.js 里引入:
js
import './style.css';接下来安装对应的 loader:
shell
npm install --save-dev style-loader css-loader最后在 module.rules 中添加规则:
js
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}重新构建 npx webpack,会在生成的 bundle.js 中找到一段将样式字符串插入 DOM 的逻辑。具体来说,css-loader 负责把 CSS 文件解析成字符串,而 style-loader 通过 JavaScript 在运行时创建一个 <style> 标签并填入这些字符串。这样就实现了“在浏览器中看到样式效果”的目的,同时样式也成为了依赖图的一部分。
注意 style-loader 的输出是运行时代码,所以它在 Node.js 环境下直接执行 node dist/bundle.js 会报错,因为 document 不存在。这也说明这里生成的产物只能在浏览器环境(或有 DOM 的环境)里跑。
使用 asset modules 处理图片资源
webpack 5 内置了一种处理静态资源的新机制——asset modules,用来代替以前需要用 file-loader、url-loader 之类的场景。不用额外安装任何包。
在 src/index.js 里导入一张图片:
js
import logo from './logo.png';
const img = document.createElement('img');
img.src = logo;
document.body.appendChild(img);然后配置资源模块类型:
js
module: {
rules: [
// ...其他规则
{
test: /\.(png|jpe?g|gif|svg)$/i,
type: 'asset/resource'
}
]
}asset/resource 会为每个匹配的文件生成一个独立的输出文件,并返回其路径。除此之外,还有 asset/inline(转成 data URI)、asset(在两者之间自动选择)等类型。构建后,图片会被丢进 dist/ 目录,文件名默认是带哈希的,避免缓存冲突。
npx webpack 之后,dist/bundle.js 里对图片的引用会被替换成实际的输出路径,例如 'dist/[hash].png'。同样,这个示例需要在浏览器环境运行,因为用到了 DOM API。
执行构建并验证输出
经过上面几步,webpack.config.js 的完整内容大概是这样的:
js
const path = require('path');
module.exports = {
mode: 'none',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
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|jpe?g|gif|svg)$/i,
type: 'asset/resource'
}
]
}
};在项目根目录执行:
shell
npx webpack输出类似于:
asset bundle.js 8.23 KiB [emitted] (name: main)
asset abc123.png 12.4 KiB [emitted]
runtime modules 937 bytes 4 modules
modules by path ./src/ 935 bytes
./src/index.js 215 bytes [built] [code generated]
./src/style.css 83 bytes [built] [code generated]
./src/logo.png 42 bytes (javascript) 12.4 KiB (asset) [built] [code generated]
webpack 5.x.x compiled successfully in 456 msdist/ 文件夹里会出现 bundle.js 和一张带哈希的图片。如果直接在支持 ES module 的浏览器中打开一个包含这个 bundle 的 HTML 页面(需要自己写一个简单 HTML 引入 bundle.js),就能看到样式和图片都生效了。
因为没有配置开发服务器,目前只能通过手动编写 HTML 来验证浏览器端行为。这部分会在后续文章里用 webpack-dev-server 解决。
注意点
- loader 顺序:
use数组里的 loader 执行顺序是从右向左,本质上类似于函数组合。写反了会导致 loader 找不到自己能处理的输入。 - Babel 转译范围:务必通过
exclude: /node_modules/排除node_modules,否则构建时间会大幅增加,还可能引发某些包的转译错误。 - Asset Modules:如果是 webpack 4 及以下,没有内置 asset modules,需要使用
file-loader或url-loader。移动到 webpack 5 后这些 loader 已被内置取代。 - CSS 在 Node 中执行:
style-loader会把 CSS 变成运行时代码,因此不要在 Node 侧直接运行打包结果,除非你预先准备了 DOM 环境或换成其他提取 CSS 的 loader(如MiniCssExtractPlugin.loader,后续 plugin 部分会涉及)。 - mode 选项的细节:
mode设为none时,webpack 不会做任何代码压缩或优化,适合观察 loader 效果。development模式下 webpack 会启用 NamedModulesPlugin 等调试辅助,但源码映射(source map)默认不会生成,需要配合devtool选项使用。production会启用 TerserPlugin 等压缩工具,但构建时间会变长。
