什么是 webpack?
webpack 是一款为现代 JavaScript 应用设计的静态资源打包工具。
作为 JavaScript 工程师,我们知道模块是什么,但是在 webpack 中稍微有些不同。它们可以是:
● ES 模块 - import 语句
● Common JS 模块 - require() 语句
● AMD 模块 - define 和 require 语句
● CSS 文件 - 在 css/sass/less 文件中的 @import 语句
● 图片 URL - url(...) 或
webpack 把这些不同的模块进行了统一处理,可以把它们导入到 JavaScript 代码里。
我应该学习 webpack 吗?
现在大多数应用是用 React/Vue 或其他框架编写的。它们提供了命令行工具(如 create-react-app,@vue/cli)来创建应用。这些命令行工具简化了大部分的配置,提供了默认的配置。但是我们作为开发者,理解它的工作原理是必要的,因为我们早晚都需要修改一些默认的配置。
开始学习
我们会创建一个简单的应用来展示 webpack 的用法。首先新建一个目录并初始化一个 npm 项目。假设你进入了新建的目录,在命令行中执行:
现在安装所需的webpack包:
打开 package.json 文件,删除其中的 test 脚本命令,然后增加一个新的脚本命令 dev 以在开发模式下运行 webpack。我们在本地开发测试时需要用到这个命令。现在你的 package.json 文件看起来如下:
如果现在你运行 npm run dev,会遇到一个烦人的报错:Entry module not found。这是因为默认情况,webpack 会使用 src/index.js 这个文件作为入口。此外,webpack 在默认情况下把打包后的文件输出到名为 dist 的目录里。
好了,现在我们创建一个目录 src 并在里面新建一个文件 index.js。现在我们暂时在这个文件里只写一句 console.log(‘hello’)。这样我们就可以解决这个报错。
现在再运行 npm run dev,这次不会遇到错误,同时你会发现打包后的文件 main.js 出现在了名为 dist 的目录里。
配置 webpack
要配置 webpack,你需要在项目的根目录里含有一个名为 webpack.config.js 的配置文件。在此文件中,我们需要导出一个配置对象。
一些最常用到的术语有:
● Entry point - 指定 webpack 的入口,从此入口找到的所有模块依赖都会收集起来。这些依赖关系构成了依赖图谱。
● Output - 用于指定输出的 JS 和静态文件存放的位置。
● Loaders - 帮助 webpack 处理各种文件后缀的第三方扩展。它们会把非 JS 文件转化为模块。
● Plugins - 可以修改 webpack 行为的第三方扩展。
● Mode - 用于指定两种模式,开发模式和线上模式。默认是线上模式。
现在我们配置一下入口和输出的目录。
修改入口
如果我们想让 webpack 以 source/index.js 作为入口,而不是默认的 src/index.js 文件。需要在导出的对象里增加一个名为 entry 的属性。
也可以这样写:
修改输出的目录
假设我们想把打包的文件输出到另一个名为 build 的目录里,而不是默认的 dist 目录。我们这样设置 output 属性如下:
在 HTM 文件里引入打包后的文件
在每个 web 应用里,至少有一个 HTML 文件。要实现这个目标,我们需要用到一个名为 html-webpack-plugin 的插件。首先执行如下命令安装插件:
这个插件究竟是干什么的?
它会加载我们的 HTML 文件,然后把打包的文件注入到 HTML 页面里。
我们先新建一个简单的 HTML 文件 index.html 放在 source 目录中,在里面填写一些简单的示例代码。现在修改我们的 webpack 配置如下:
这样文件已经就绪了,我们需要一个服务器来展示这个页面。现在可以用 webpack-dev-server 来启动服务器,我们已经在之前的步骤中安装好了。
Webpack dev server
要配置 webpack-dev-server,我们需要打开 package.json 文件,然后增加一个新的脚本命令来启动服务器。例如,我们增加一个名为 start 的命令如下:
现在在命令行执行这个命令:
打开浏览器访问 localhost:8080,你会看到我们的 index.html 文件,打开浏览器的开发者工具可以看到打包后的 main.js 已经嵌入到了此文件中。
使用 webpack 加载器
如前所述,webpack中的加载器是处理各种其他文件后缀的第三方扩展。webpack有很多加载器可供使用。
让我们继续在webpack配置文件中配置加载器。 module 字段里有一个 rules 的属性,它是一个由加载器组成的数组。对于想要作为模块处理的文件,我们需要把它们作为对象放在 rules 数组里。每个对象由两个属性组成:test 定义文件的类型,use 是一个由加载器组成的数组。需要注意的是在 use 数组里的加载器是从右向左加载的。定义加载器时的顺序很重要。建议使用加载器前阅读对应的文档。
在配置文件中加载器的配置句法类似于这样:
处理 CSS
为了能在 webpack 中处理 CSS,我们需要两个加载器:css-loader 和 style-loader。首先在命令行中安装它们:
我们在 source 目录中新建一个 styles.css 文件,然后添加一些样式,在构建完成后会应用到 index.html 中。接下来我们要在 index.js 文件中引入它,而不是在 index.html 中。所以现在 index.js 看起来如下:
还有最后一步要做,那就是在 webpack.config.js 中配置加载器。我们的配置文件如下:
css-loader 用于加载 CSS 文件,而 style-loader 用于在 DOM 中加载样式。
重新运行 npm start,可以在浏览器中看到变化:
处理 SASS 文件
要处理 SASS(.scss) 文件,我们需要三个加载器:sass-loader,css-loader 和 style-loader,此外还需要一个 NPM 包 sass。这里的 sass-loader 用于导入 SASS 文件。由于另外两个加载器已安装,现在只需安装其他包即可。
我们在 source 目录下新建 styles.scss 文件,然后增加如下代码。
接下来修改 index.js 文件,在文件的开头使用 import ‘./styles.scss’ 导入此文件。
现在 webpack.config.js 中增加加载器。我们的加载器看起来如下:
重启服务器,检查一下页面是否发生了变化。
处理现代 JavaScript 句法
Webpack 自身不知道如何把现代 JavaScript 句法转成所有浏览器支持的格式。所以它使用了 Babel 来解决这个问题。我们需要安装如下包:@babel/core,它是实际的处理引擎;babel-loader,是webpack使用的加载器;@babel-preset-env 用于把 JavaScript 代码转成 ES5。我们运行如下命令安装依赖包:
下一步是配置 Babel,我们需要在项目根目录新增一个文件 babel.config.json。在这里,我们配置 Babel 使用刚安装的 preset-env:
然后在 webpack.config.js 文件里增加我们刚安装的加载器。
现在我们的 JS 代码中使用 ES6 句法,然后运行 npm run dev 重新构建,可以在生成的 main.js 里看到已经自动转换成了浏览器支持的 ES5 代码。
在 webpack 里指定模式
在 webpack 里有两种模式:开发模式和线上模式。
在开发模式下,不会执行代码压缩。webpack 只是把我们编写的 JS 直接加载入浏览器中,所以在浏览器中刷新应用很快。
在线上模式下,webpack 会做很多优化。它会自动使用 terser-webpack-plugin 来压缩代码减少打包后文件的体积。还会设置 process.env.NODE_ENV 为 production。这个环境变量很有用,我们可以根据不同的执行环境切换不同的任务。
要在线上模式下使用 webpack,让我们增加另外一个脚本命令到 package.json 文件里,我们把它命名为 build。现在脚本命令看起来如下:
优化 - 代码分拆
代码分拆或延迟加载是一项优化技术,用来避免包文件体积过大,也可以避免依赖重复打包。采用此机制后,我们可以按需加载代码,例如当用户点击一个按钮时,路由变化时。代码片段也称之为 chunk。
在 webpack 中如果打包后文件体积超过 244KB 可能会出现警告信息。有三种方式可以在 webpack 中实现代码分拆:
1. 配置多个入口文件
2. 使用 optimization.splitChunks
3. 动态导入
第一种方法对于小型项目很合适,但是对于复杂项目不够灵活。在 webpack 的配置文件中指定多个入口即可。
使用 optimization.splitChunks
有时候我们需要在web应用里用到很多依赖。例如,我们要使用一个流行的日期库 Moment。我选择这个库是因为它有点大。让我们安装它然后在 index.js 中导入它,然后运行 build 命令。
Moment 会成功安装。现在 index.js 中导入这个库。
现在运行 build 命令。
你会在终端看到这个警告信息:
我们该怎么解决这个问题呢?很简单。让我们在 webpack 配置文件中增加一个 optimization 以及另一个名为 splitChunks 的属性:
你会发现入口文件的体积极大得缩小了。
动态导入
动态导入用于按条件加载代码。这种方式广泛用于 React 和 Vue 项目里。我们可以基于用户操作或者路由改变加载代码。
为了演示这种方式,我们在页面上增加一个按钮,点击后会查询获得一个帖子列表。用于查询的代码放在另一个单独的文件里,然后在 index.js 中动态导入它。
在 source 目录下新建一个 api.js 文件,在里面使用 fetch API 查询帖子列表。在这个文件中,我们导出一个函数,这个函数调用后返回一个 promise。
我们在 index.html 中增加一个按钮,设置它的 id 为 btn。
在 index.js 文件里,使用一个函数来动态导入 api.js 文件。
此外,在 index.js 的结尾增加这段逻辑记录获取的数据。
这段代码调用 getTodos 函数,会导入这个文件。在导入之后,我们从中解构出 fetchTodos 属性,调用此函数,然后成功后记录响应值。
我们编译文件,运行服务器,确保开发者工具是开启的并切换到其中的 Network 面板。你会发现点击后这个新建的 JS文件被动态加载了。
图片中的 0.main.js 就是那个被动态加载的文件。如果你想让这个文件名更可读,只需要在动态加载时增加一个注释即可:
现在再次重复这个过程,浏览器会加载名为 postAPI.js 的文件,而不是 0.main.js。
结语
这只是 webpack 的一个简单的介绍。就像我们之前说的,这篇文章主要是写给开始学习 webpack 的初中级 web 开发工程师的。当然了,在 webpack 中有许多可以学习的知识点。我们只是过了一下最基础的部分。如果你对 webpack 感兴趣,想学习更多内容,请参考 webpack 官方文档。
热门跟贴