起步

webpack 用于编译 JavaScript 模块。一旦完成 安装 就可以通过 webpack CLIAPI 与其配合交互。如果还不熟悉 webpack,请阅读 核心概念对比 了解为什么是使用 webpack 而非社区中的其他工具。

基础设置

首先创建进入一个目录并初始化 npm,然后 在本地安装 webpack,接着安装 webpack-cli(此工具用于在命令行中运行 webpack):

mkdir webpack-demo
cd webpack-demo
npm init -y
npm install webpack webpack-cli --save-dev

整篇指南将使用 diff 代码块展示对目录、文件和代码所做的修改。例如:

+ 这是需要复制到代码的新的一行
- 这是需要从代码中移除的一行
  这是不需要调整的一行

现在创建以下目录结构、文件和内容:

project

  webpack-demo
  |- package.json
  |- package-lock.json
+ |- index.html
+ |- /src
+   |- index.js

src/index.js

function component() {
  const element = document.createElement('div');

  // 执行这一行需要引入 lodash(目前通过 script 脚本引入)
  element.innerHTML = _.join(['Hello', 'webpack'], ' ');

  return element;
}

document.body.appendChild(component());

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>起步</title>
    <script src="https://unpkg.com/lodash@4.17.20"></script>
  </head>
  <body>
    <script src="./src/index.js"></script>
  </body>
</html>

还需要调整 package.json 文件以确保安装包是私有的,并移除 main 入口,这可以防止意外发布代码。

package.json

 {
   "name": "webpack-demo",
   "version": "1.0.0",
   "description": "",
-  "main": "index.js",
+  "private": true,
   "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1"
   },
   "keywords": [],
   "author": "",
   "license": "MIT",
   "devDependencies": {
     "webpack": "^5.38.1",
     "webpack-cli": "^4.7.2"
   }
 }

在此示例中,<script> 标签之间存在隐式依赖关系。在 index.js 文件执行之前,还需要在页面中先引入 lodashindex.js 并未显式声明需要 lodash,而是假定推测已经存在一个全局变量 _

使用这种方式管理 JavaScript 项目存在一些问题:

  • 无法直接体现脚本的执行依赖于外部库。
  • 如果依赖不存在,或者引入顺序错误,应用程序将无法正常运行。
  • 如果依赖被引入但是并没有使用,浏览器将被迫下载无用代码。

试试使用 webpack 管理这些脚本。

创建一个 bundle

首先稍微调整下目录结构,创建 ./dist 文件夹用于存放分发(distribution)代码,而 ./src 文件夹仍存放源代码。源代码是指用于书写和编辑的代码;分发代码是指在构建过程中,经过最小化和优化后产生的输出结果,分发代码最终将在浏览器中加载。调整后的目录结构如下:

project

  webpack-demo
  |- package.json
  |- package-lock.json
+ |- /dist
+   |- index.html
- |- index.html
  |- /src
    |- index.js

在本地安装 lodash 以在 index.js 中打包它:

npm install --save lodash

现在在脚本中导入 lodash

src/index.js

+import _ from 'lodash';
+
 function component() {
   const element = document.createElement('div');

-  // 执行这一行需要引入 lodash(目前通过 script 脚本引入)
+  // lodash 现在使用 import 引入
   element.innerHTML = _.join(['Hello', 'webpack'], ' ');

   return element;
 }

 document.body.appendChild(component());

现在将打包所有脚本,因此需要更新 index.html 文件。由于现在是通过 import 引入 lodash,所以应将 lodash <script> 删除;然后修改另一个 <script> 标签来加载 bundle,而不是原始的 ./src 文件:

dist/index.html

 <!DOCTYPE html>
 <html>
   <head>
     <meta charset="utf-8" />
     <title>起步</title>
-    <script src="https://unpkg.com/lodash@4.17.20"></script>
   </head>
   <body>
-    <script src="./src/index.js"></script>
+    <script src="main.js"></script>
   </body>
 </html>

在此设置中 index.js 显式要求引入的 lodash 必须存在,然后将它绑定为 _(没有全局作用域污染)。webpack 能够利用模块生命的依赖信息构建依赖图,然后使用图生成一个优化过的 bundle,并能保证以正确顺序执行。

换言之,执行 npx webpack 会将脚本 src/index.js 作为 入口起点,也会生成 dist/main.js 作为 输出。Node 8.2/npm 5.2.0 及以上版本提供的 npx 命令,可以运行在最初安装的 webpack 包中的 webpack 二进制文件(即 ./node_modules/.bin/webpack):

$ npx webpack
[webpack-cli] Compilation finished
asset main.js 69.3 KiB [emitted] [minimized] (name: main) 1 related asset
runtime modules 1000 bytes 5 modules
cacheable modules 530 KiB
  ./src/index.js 257 bytes [built] [code generated]
  ./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
webpack 5.4.0 compiled successfully in 1851 ms

在浏览器中打开 dist 目录下的 index.html,如果一切正常就能看到以下文本:'Hello webpack'

模块

尽管大多数浏览器还无法支持在 ES2015 中已经标准化的 importexport,但 webpack 能够提供开箱即用般的支持。

事实上,webpack 会将代码转译以便可以在旧版本浏览器中执行。检查 dist/main.js 你可以看到 webpack 具体是如何工作的,这是独创精巧的设计!除了 importexport,webpack 还能够很好地支持多种其他模块语法,查看 模块 API 以了解更多相关信息。

注意,webpack 不会更改代码中除 importexport 语句以外的部分。如果正在使用其它 ES2015 特性,请确保 webpack 的 loader 系统 中使用了像 Babel 一样的 转译器

使用配置文件

webpack v4 无须任何配置即可运行,然而大多数项目会需要很复杂的设置,因此 webpack 仍然支持 配置文件,这比在终端中手动输入大量命令更加高效。接下来创建一个 webpack 配置文件:

project

  webpack-demo
  |- package.json
  |- package-lock.json
+ |- webpack.config.js
  |- /dist
    |- index.html
  |- /src
    |- index.js

webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
  },
};

现在,让我们通过新的配置文件再次执行构建:

$ npx webpack --config webpack.config.js
[webpack-cli] Compilation finished
asset main.js 69.3 KiB [compared for emit] [minimized] (name: main) 1 related asset
runtime modules 1000 bytes 5 modules
cacheable modules 530 KiB
  ./src/index.js 257 bytes [built] [code generated]
  ./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
webpack 5.4.0 compiled successfully in 1934 ms

比起 CLI 这种简单直接的使用方式,配置文件更加灵活。开发者可以在配置文件中指定 loader 规则、插件、resolve 选项,以及许多其他增强功能。请查看 配置文档 以了解更多相关信息。

npm scripts

考虑到使用 CLI 这种方式运行本地 webpack 副本不是特别方便,可以在 package.json 文件中添加 npm script 以设置一个快捷方式:

package.json

 {
   "name": "webpack-demo",
   "version": "1.0.0",
   "description": "",
   "private": true,
   "scripts": {
-    "test": "echo \"Error: no test specified\" && exit 1"
+    "test": "echo \"Error: no test specified\" && exit 1",
+    "build": "webpack"
   },
   "keywords": [],
   "author": "",
   "license": "ISC",
   "devDependencies": {
     "webpack": "^5.4.0",
     "webpack-cli": "^4.2.0"
   },
   "dependencies": {
     "lodash": "^4.17.20"
   }
 }

现在可以使用 npm run build 命令替代之前使用的 npx 命令。注意,使用 npm scripts 便可以像使用 npx 那样通过模块名引用本地安装的 npm 包。这是大多数基于 npm 的项目遵循的标准,因为它允许所有贡献者使用同一组通用脚本。

运行以下命令看看脚本别名是否正常运行:

$ npm run build

...

[webpack-cli] Compilation finished
asset main.js 69.3 KiB [compared for emit] [minimized] (name: main) 1 related asset
runtime modules 1000 bytes 5 modules
cacheable modules 530 KiB
  ./src/index.js 257 bytes [built] [code generated]
  ./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
webpack 5.4.0 compiled successfully in 1940 ms

总结

现在已经有了一个基础构建配置,可以移至下一指南——资源管理,以了解如何通过 webpack 管理诸如图像、图标等资源。此刻你的项目目录看起来应该如下:

project

webpack-demo
|- package.json
|- package-lock.json
|- webpack.config.js
|- /dist
  |- main.js
  |- index.html
|- /src
  |- index.js
|- /node_modules

如果想要了解 webpack 设计思想,可以查看 基本概念配置 页面。此外也能够在 API 章节深入了解 webpack 提供的各种接口。

24 位贡献者

bebrawcntanglijunchrisVillanuevajohnstewsimon04aaronangTheDutchCodersudarsangpVanguard90chenxsanEugeneHlushkoATGardnerayvarotbjarkiztomaszeSpiral90210byzykwizardofhogwartsmyshovanshumanvd3lmsnitin315Etheryen