webpack

70 阅读12分钟

webpack project setup

在本练习中,我们将从一个空文件夹开始,创建一个 Webpack 项目

  1. 配置package.json

webpack是一个Node.js包,要使用node.js包,我们需要package.json来存储项目的元数据

在终端运行以下命令来初始化一个 package.json 文件:

npm init -y

-y 标志表示使用默认值,无需手动输入信息。

  1. 安装 Webpack 和 Webpack CLI

我们需要安装两个软件包:

  • webpack(Webpack 的核心功能)
  • webpack-cli(提供命令行访问 Webpack 的能力)

因为这些工具仅在开发环境中使用,不会影响最终的产品,所以我们将它们作为 开发依赖(devDependencies) 安装:

npm install --save-dev webpack webpack-cli

安装后,package.json 文件中的 devDependencies 字段会列出这些依赖项。

  1. 创建 Webpack 入口文件

webpack需要一个入口文件(entry point)作为项目的主文件,如果没有入口文件,webpack会报出错误。Webpack 默认的入口文件src/index.js,我们需要手动创建它:

mkdir src
touch src/index.js

webpack是一个 模块打包工具(module bundler) ,它的主要功能是分析依赖关系并打包代码。Webpack 需要一个起点(入口文件,entry point) 来确定从哪里开始分析依赖,然后构建整个依赖图(dependency graph) ,并把所有用到的模块打包成一个或多个文件。 **简单来说,入口文件的作用是: ** ✅ 告诉 Webpack 从哪里开始 分析项目
✅ 让 Webpack 找到所有被依赖的文件
✅ 生成最终的 打包文件(bundle.js 或其他格式)

  1. 定义build命令

package.jsonscripts 部分,我们可以添加一个 build 命令,方便运行 Webpack:

"scripts": {
  "build": "webpack --watch"
}
  • webpack:运行 Webpack
  • --watch:监听文件变化,自动重新打包
  1. 运行webpack构建

现在,我们可以在终端运行以下命令来执行 build

npm run build

打包一个JavaScript文件

在本次练习中,我们将用Webpack来处理项目中的一个JavaScript文件。

大文件加载时间比较长,然而,随着项目变得越来越庞大和复杂,代码量也会随之增加。我们可以尝试用尽可能少的字符来编写代码,但这样会使代码变得难以阅读。那么,有没有办法既能写出易读的代码,又能让它下载得更快呢?

构建工具(Build Tools) 让我们能够做到这一点。我们可以使用函数注释等方式来提高代码的可读性,而 Webpack 在创建供最终用户使用的内容时,会移除大部分不必要的部分。这样,我们可以编写易读的代码,并通过 Webpack 处理它,然后将优化后的版本提供给用户。两者显示的内容相同,而用户不会察觉其中的区别。

现在,让我们来练习使用 Webpack 处理 JavaScript 文件,并查看 Webpack 优化后的输出!

运行npm run build

index.js:

const greetUser = () => {
  console.log('Hello User!');
}
const askUserStatus = () => {
  console.log('How are you?');
}
greetUser();
askUserStatus();

webpack处理后的代码:

console.log("Hello User!"),console.log("How are you?");

打包多个JavaScript文件

在本练习中,我们将练习使用 Webpack 来打包多个 JavaScript 文件

点击右侧的文件夹图标,查看项目的文件结构。我们的 src 目录中有三个 JavaScript 文件:greetUser.jsmyUser.js 提供工具函数和数据,而 index.js 负责使用这些功能。

在传统的静态 Web 项目中,我们通常需要在 HTML 页面中手动引入所有这三个脚本文件,并且要确保它们按照正确的顺序加载。随着项目变得越来越复杂,管理和导入这些脚本会变得更加困难。而 Webpack 这样的构建工具可以让资源的引入更加简单。

Webpack 允许我们在前端文件(不仅限于 JavaScript)中使用 importexport 语句。当我们运行 Webpack 进行构建时,它会自动将所有文件整合在一起,就像它们原本是一个单独的文件一样。如果你需要复习 ES6 模块语法,可以参考**《使用 ES6 语法实现模块化》**文章。

现在,让我们来练习使用 Webpack 打包多个 JavaScript 文件吧!

  1. 导入greetUser和myUser中的数据到index greetUser:
const greet = (username, firstName) => {
  console.log(`Hello ${username}, or should I call you ${firstName}?`);
}

export {greet};

myUser:

onst user = {
  username: 'Beepum',
  firstName: 'Gert'
};

export {user};

index.js:

import {user} from './myUser.js';
import {greet} from './greetUser.js';

greet(user.username, user.firstName);

npm run build之后,可以看到webpack打包后的结果:

(()=>{"use strict";console.log("Hello Beepum, or should I call you Gert?")})();

创建webpack config

到目前为止,我们一直在没有任何配置或设置的情况下使用webpack,现在让我们开始自定义webpack

Webpack 会自动查找名为 webpack.config.js 的配置文件。我们可以使用 module.exports 语法在其中定义 Webpack 的设置。可以先定义一个空的配置文件,如下所示:

module.exports = {

}

Webpack 一直在警告我们未设置 mode,你可能已经在输出中看到类似的提示:

WARNING in configuration  
The 'mode' option has not been set, webpack will fallback to 'production' for this value.

现在,我们有了一个配置文件,可以在其中设置 mode

module.exports = {  
  mode: 'development'  
}

development 模式用于开发阶段,会生成更易读的输出版本。而当我们的项目开发完成后,我们会切换到 production 模式,使输出更紧凑,减少文件大小,提高性能。你可以在 Webpack 官方文档中了解更多关于 mode 的信息。

定义入口和出口文件

在本练习中,我们将使用 Webpack 的配置文件来更改 Webpack 查找要打包的文件的位置,以及存放输出文件的位置。

到目前为止,我们一直使用 Webpack 的默认入口文件 ./src/index.js。然而,在项目开发中,主文件通常存储在不同的文件夹或采用不同的命名方式。

假设我们希望在 application/home.js 中编写我们的主要应用程序代码。我们可以在 Webpack 配置文件中定义一个入口点,告诉 Webpack 这是主文件,并应从这里开始打包:

entry: './application/home.js'

入口点允许我们定义一个相对于 webpack.config.js 位置的路径。

此外,我们可能还想指定输出文件的名称或存放的目录。例如,如果我们希望打包后的 JavaScript 文件存放在 built 目录下,并命名为 fast.js,则可以定义一个出口点

与入口点不同,出口点需要一个绝对路径,最好的做法是使用 Node 的 path 模块来指定路径。我们可以在 output 选项下进行如下设置:

const path = require('path');

module.exports = {
  entry: './application/home.js',
  output: {
    filename: 'fast.js',
    path: path.resolve(__dirname, 'built'),
  },
  // ...
}

const path = require('path');
module.exports = {
  entry: './uncooked/ingredients.js',
  mode: 'development',
  output: {
    filename: 'soup.js',
    path: path.resolve(__dirname, 'steamy')
  }
}
  • filename 指定打包后的 JavaScript 文件名称。
  • path 指定打包文件存放的目录,path.resolve(__dirname, 'built') 会将当前目录路径 (__dirname) 与 built 目录合并,生成绝对路径。
  • path.resolve(__dirname, 存放文件目录)

根据以上配置,运行 Webpack 后会生成 built/fast.js 文件。

使用 Webpack Dev Server 预览我们的应用

在本练习中,我们将使用一个名为 webpack-dev-server 的工具来预览代码,并在我们进行更改时自动更新页面。

  1. 安装 webpack-dev-server 首先,我们需要将 webpack-dev-server 作为开发依赖项安装到本地环境中:
npm install --save-dev webpack-dev-server
  1. 创建 HTML 文件 接下来,我们需要一个 HTML 文件,并且它应该引入 Webpack 生成的 JavaScript 文件(也就是出口文件):
<script src="./dist/main.js"></script>

  1. 修改 package.json 添加启动命令

我们需要在 package.json 文件的 scripts 部分添加 start 命令,以便使用 webpack-dev-server

"scripts": {
  "build": "webpack --watch",
  "start": "webpack serve"
}

  1. 运行 Webpack Dev Server

这两个命令的作用:

  • build 命令用于编译项目,并在文件变更时重新构建。
  • start 命令用于运行 Webpack 开发服务器,并在构建变更时自动刷新页面

为了使项目在我们修改代码时自动更新,通常我们需要在 两个终端窗口 中运行这些命令:

  1. 在第一个终端窗口 运行 build 命令,创建 bundle.js 并监听更新:
npm run build
  1. 在第二个终端窗口 运行 start 命令,启动 Webpack 开发服务器并在浏览器中预览:
npm run start

webpack规则

在本练习中,我们将介绍 Webpack 的规则系统,并练习为项目中的 .txt 文件定义一条规则。

Webpack 使用 规则(rules) 来决定如何处理不同类型的文件。Webpack 期望在 module 配置选项中提供一个规则数组,其语法如下:

module.exports = {
  module: {
    rules: []
  }
}

定义规则

规则中需要定义 test 选项,它是一个 正则表达式。如果某个文件的名称匹配该正则表达式,Webpack 就会对该文件应用相应的规则。例如,如果我们定义 test/.txt$/i,那么这个规则就会适用于所有以 .txt 结尾的文件。

除了 test 之外,我们还需要告诉 Webpack 如何处理匹配的文件。不同的文件类型,处理方式也不同。

对于 .txt 文件,我们可以在 rules 数组中添加以下规则:

rules: [
  {
    test: /\.txt$/i,
    type: 'asset/source'
  }
]

其中,type: 'asset/source' 表示 .txt 文件是一个 资源文件,可以直接作为 源代码 引入,无需额外处理。

使用规则 定义规则后,我们可以在代码中导入 .txt 文件,例如:

import Text from './example.txt';
document.querySelector('h1').innerHTML = Text;

运行后,页面上 h1 元素的内容将被 .txt 文件的内容替换。

将css添加到webpack构建中

现在,我们将把 CSS 样式表 添加到 Webpack 项目中。

CSS 规则的配置

CSS 规则的 test 选项结构与 .txt 文件规则类似:

test: /\.css$/i

不过,与 .txt 文件不同,CSS 文件 需要加载器(loaders) 来让 Webpack 处理。对于需要加载器的文件,规则中不能使用 type 选项,而是要使用 use 选项。

CSS 需要 两个加载器

  • css-loader:从 .css 文件中提取 CSS 并添加到 JavaScript 代码中。
  • style-loader:将 css-loader 处理后的 CSS 代码注入到 HTML 的 <style> 标签中。

注意:Webpack 按从右到左的顺序 应用加载器,因此我们要 使用 css-loader,再使用 style-loader

完整的 CSS 规则如下:

{
  test: /\.css$/i,
  use: ['style-loader', 'css-loader']
}

我们可以将这条规则添加到 rules 数组中的任意位置。

安装 CSS 加载器

在本地环境下,我们需要将这些加载器安装为 开发依赖(devDependencies)

npm install --save-dev style-loader css-loader

在 JavaScript 中导入 CSS

安装完成后,我们可以直接在 JavaScript 文件中 导入 CSS,方法如下:

import './style.css';
import Text from './example.txt';
document.querySelector('h1').innerHTML = Text;

当我们构建项目并启动 Webpack 预览服务器后,CSS 就会自动应用到 HTML 页面上!

通过link导入css与在webpack通过JavaScript导入css的区别

  1. 代码模块化 & 依赖管理

Webpack 允许我们 在组件级别 直接导入 CSS,而不是全局引入。

假设我们有一个 Button.js 组件,并且它有自己的样式 button.css

import './button.css';

export function Button() {
  const btn = document.createElement('button');
  btn.textContent = 'Click Me!';
  return btn;
}

这样,每个组件可以管理自己的 CSS,而不需要在 HTML 中手动引入所有的样式文件。

  1. CSS 代码优化 & 按需加载

当 Webpack 处理 CSS 时,它可以:
去掉未使用的 CSS 代码(Tree Shaking)
自动优化 CSS(压缩、去除空格等)
支持代码拆分(Code Splitting) ,只加载当前页面需要的 CSS,而不是整个站点的所有 CSS。

例如,在一个单页应用(SPA)中,Webpack 可以让不同页面的 CSS 按需加载,减少首屏加载时间。

  1. 让 CSS 兼容所有浏览器

通过 css-loader,Webpack 可以自动处理 CSS 兼容性问题,比如:

  • 自动添加厂商前缀(Autoprefixer) ,让 CSS 适配不同浏览器:
.box {
  display: flex;
}

经过处理后,Webpack 可能会自动生成:

.box {
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
}

  1. 适配不同的构建环境

Webpack 可以根据不同的模式developmentproduction)使用不同的 CSS 处理方式:

  • 开发环境:通过 style-loader 直接在 HTML 里插入 <style>,支持热更新(HMR)。
  • 生产环境:用 MiniCssExtractPlugin 把 CSS 提取到独立文件,减少 JS 体积,提高性能。

将图片添加到 Webpack 构建中

让我们看看如何使用 Webpack 打包图片资源。

Webpack 5 以来,图片和字体不再需要额外的 loader,而是可以直接使用 Webpack 的 asset 资源系统。其规则与 .txt 文件的处理方式类似:

{
  test: /\.png$/i,
  type: 'asset/resource'
}

请注意,我们使用 type: 'asset/resource' 而不是 asset/source

  • asset/resource 会在构建时生成一个独立的文件,并将其作为 URL 引入代码中
  • 如果你想了解更多 Webpack 资源类型,可以进行深入探索。

上面的规则仅适用于 .png 文件,但我们也可以让它同时处理多种图片格式,如:

{
  test: /\.(png|svg|jpg|jpeg|gif)$/i,
  type: 'asset/resource'
}

如何在 JavaScript 中使用图片?

import Square from '../square.png';

const img = document.createElement('img');
img.src = Square;

const body = document.querySelector('body');
body.appendChild(img);

这样,当我们执行 Webpack 构建时,图片将会被打包并正确加载到网站中!

webpack添加图片的好处

  1. 资源路径处理

HTML 方式 (<img src="xxx.png">)

  • 你必须手动确保 src 指向正确的路径,比如 src="./images/pic.png"

  • 如果文件路径变了(比如 Webpack 生成的路径是 dist/abc123.png),你需要手动更新 HTML 代码,否则图片可能无法加载。

Webpack 方式 (import Square from '../square.png')

  • Webpack 会自动处理路径,比如它可能会重命名图片文件(如 abc123.png)并自动更新 src,避免缓存问题。

  • 你不用关心图片的最终存放位置,Webpack 会在 dist/ 目录下生成正确的文件,并自动调整引用路径。

  1. 图片优化
  • HTML 方式

    • 浏览器直接请求 img src="xxx.png",不会进行额外优化。
    • 需要手动使用工具(如 TinyPNG)压缩图片,或者使用 WebP 等格式。
  • Webpack 方式

    • Webpack 可以自动优化图片,比如:

      • 压缩图片(需要 image-webpack-loader
      • 转换格式(比如 pngwebp
      • 懒加载(lazy load) ,减少不必要的加载,提高性能。
  1. 代码可维护性
  • HTML 方式

    • 适用于静态图片(如 logo)。

    • 但如果是动态内容(比如用户头像、商品图片等),你可能需要用 JavaScript 手动修改 src,例如:

      js
      复制编辑
      document.querySelector('img').src = 'new-image.png';
      
  • Webpack 方式

    • 适用于动态管理资源,比如根据逻辑动态加载不同的图片:

      js
      复制编辑
      import DayImage from './day.png';
      import NightImage from './night.png';
      
      const img = document.createElement('img');
      img.src = isDaytime ? DayImage : NightImage;
      document.body.appendChild(img);
      
    • Webpack 还能配合 tree shaking,只打包真正用到的图片,而不会把所有图片都塞进最终构建。

将字体添加到我们的构建中

在本练习中,我们将向项目中添加字体。

字体的 Webpack 规则与图片的规则类似:

{
  test: /\.ttf$/i,
  type: 'asset/resource'
}

上面的规则用于处理 .ttf 格式的字体文件,但我们可能希望支持多种字体格式:

{
  test: /\.(woff|woff2|eot|ttf|otf)$/i,
  type: 'asset/resource'
}

上面的规则可以处理 .woff.woff2.eot.ttf.otf 格式的字体文件。

与我们之前见过的其他资源不同,字体文件通常在 CSS 中导入,而不是在 JavaScript 中。我们可以在 CSS 中这样使用字体:

@font-face {
  font-family: 'Roboto-Black';
  src: url('../Roboto-Black.ttf');
}

h1 {
  font-family: 'Roboto-Black';
}

url 指定了字体文件的位置,font-family 定义了字体的名称,我们可以在 CSS 其他地方引用它。

当我们构建项目后,就可以看到字体效果了!