
至今,有很多小伙伴们都不太了解Webpack打包工具,今天广州蓝景的小编跟大家普及下技术点的教学,请大家收藏好哦
前言
众所周知,前端项目使用 JS 只能通过在 HTML 文件中使用<script>标签:
<script>
console.log('hello');
</script>
<script src="./index.js"></script>
JS 原本的设计初衷只是为了解决一些简单的网页交互(比如:表单验证),可设计者做梦也想
不到,JavaScript 的发展非常快,能做的事情也越来越多,网页的代码量也对越来越多。这
个时候如果将所有的 JS 代码都放在<script>标签中或者存放到一个 JS 文件中,就会使得我
们的代码看起来非常臃肿。首先出现的做法就是分文件,通过命名不同 JS 文件将不同功能
的 JS 代码存放到对应的文件中。这样的话就能解决所有的代码都堆积在一起,造成代码臃
肿。但是这样的做法同样也存在问题:
- 浏览器同域名请求的最大并发数限制 。HTTP 客户端一般对同一服务器的并发连接个数都是有限制(不同浏览器的限制个数可能存在不同),如果遇到有的 JS 文件处理的任务耗时过长、网络速度不佳等情况,那么后面的其余文件就只能等待前面文件加载完成,造成网页加载速度慢,出现白屏情况。
- 全局环境污染。 通过<script>标签链入 JS 文件,变量都会存在全局环境中,造成变量重名、污染全局环境等情况。
为了解决全局污染的问题,我们会采用下面两种方法:
- 使用自执行函数(IIFE)
- ES moudle
let a = (function () {
let a = 'hello JS';
return {
b: 'IIFE',
c: 'IIFE',
};
})();
通过自执行函数创建函数作用域,可以防止变量污染全局环境。然后将需要使用的变量通过
return 的方式返回给外面使用。但是这样同样会存在问题,当项目很大时,我们使用了很多
文件来编写不同功能的代码,然后再将文件引入到 HTML 中。当项目中需要通过某个变量来
定位问题,那么这个时候我们很难去定位到这个全局变量存在于那个 JS 文件中,因为模块
之间的依赖关系并不是很清晰,而且不同模块之间还是存在变量名重复的问题。
随着网页代码越来越庞大,JavaScript 模块化编程就变得越来越迫切了。在 ES2015 属于 Ja
vaScript 的模块化写法终于出现。
// a.js
import { b } from './b.js';
// b.js
export const b = 'ES module';
通过export去暴露变量,然后通过import去引入变量。通过这样可以清楚的知道变量来自于
哪个模块,也不会存在命名冲突的问题,污染全局环境等问题。虽然 ES module 在不同浏览
器的支持程度不一,但是我们可以通过 babel 将 ES6+代码转成 ES5,用来兼容不同的浏览
器。
使用模块化开发项目可以方便我们维护代码,定位项目问题。但是模块化开发也会增加需要
引入文件的数量,那么模块化会出现请求并发数限制的问题,造成项目加载慢,首屏白屏时
间过长。这个时候我们就需要一个工具在项目上线时帮助我们自动化执行文件合并、
转译ES6+代码、优化项目性能。在众多打包工具中,webpack无疑是现在最耀眼的。
正文
本文相关代码支持webpack4、webpack5,特定版本写法会在对应位置写明。

本质上,webpack是一个用于现代 JavaScript 应用程序的静态模块打包工具。当 webpack 处理对应程序时,它会在内部构建一个依赖图,此依赖图对应映射到项目所需的每个模块,并生成一个或多个 bundle。
以上是 webpack 官方文档中概念一章对于 webpack 的描述,描述当中提到 webpack 是一
个现代 JavaScript 应用程序的静态模块打包工具,这里的 静态模块 不单单指 ES module。
在 webpack 中的模块可以是以下几种类型:
- ES2015 import 语句。
- CommonJS require() 语句。
- AMD define 和 require 语句。
- css/sass/less 文件中的 @import 语句。
- stylesheet url() 或者 HTML <img src=""> 文件中的图片链接。
依赖图 :当 webpack 处理应用程序时,它会根据命令行参数中或配置文件中定义的模块列
表开始处理。从入口开始,webpack 会递归的构建一个依赖关系关系图,这个依赖图包含着
应用程序中所需的每个模块,然后将所有模块打包为少量的 bundle。
Bundle :bundle 由许多不同的模块生成,包含已经经过加载和编译过程的源文件的最终版本。
chunk :在打*过包**程中, 模块 会被合并成 chunk , chunk 合并成 chunk 组 。 chunk 组 为 webpack 配置文件 中指定的 entry 字段,每个入口对应一个 chunk 组 。而 chunk 有两种形式:
- initial(初始化) 是入口起点的 main chunk。此 chunk 包含为入口起点指定的所有模块及其依赖项。
- non-initial 是可以延迟加载的块。可能会出现使用 动态导入 或者 SplitChunksPlugin 时。
chunk 对应的是打*过包**程中,对各个依赖模块进行*绑捆**的过程。而 bundle 指的是编译完成生成的最终版本。
简单的了解了 webpack 的相关知识,接下来就进入到 webpack 使用的相关学习:
1.安装 webpack
mkdir webpack-demo
cd webpack-demo
npm init -y
npm install webpack webpack-cli --save-dev
- webpack-cli :用于在命令行中运行 webpack
2.配置入口(entry)和出口(output)
在项目根目录中创建 src 文件夹、webpack.config.js
- webpack-demo
| - src
| - webpack.config.js
| - package.json
配置 webpack 配置文件(webpack.config.js)
// webpack.config.js
const path = require('path');
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/bundle.js',
},
};
通过 module.exports 暴露一个对象,对象中的不同键名用来指定不同 webpack 配置。
entry 用于指定 webpack 编译时的入口文件,webpack 会以入口文件作为起点,生成项目依
赖图,用来打包项目。默认值为 ./src/index.js 。output 用于指定项目编译文件的输出位置。
output 最少需要配置 filename 属性,filename 属性用于指定编译后的 bundle 文件的文件
名。path 用于指定编译后文件输出的位置。默认值为 ./dist
上面代码中的 path 是 node 中处理文件和目录的路径,因为 output 的 path 需要一个绝对路
径,所以这里使用到 node path 的 resolve 方法,这个方法可以将路径片段处理成一个绝对
路径。其中的__dirname指的是对应 js 文件在被调用时所处的绝对路径,
由于 webpack.config.js 处于项目根目录中,那么最后会将编译好的文件生成到根目录下的
dist 文件夹中,如果根目录中没有 dist 文件夹,则会创建该文件夹。
创建 main.js 文件
// src/main.js
console.log('Hello webpack!');
在 package.json 中配置 npm 命令:
{
"scripts": {
"build": "webpack"
}
}
在命令行中运行命令
npm run build
通过执行 build 命令运行 webpack-cli,webpack 默认命令为 build 命令(可以省略),build 命令
会调用 webpack 完成项目的编译。
编译完成后会在根目录生成一个 dist 文件夹,文件夹中一个 js 文件夹里面包含
生成的 bundle.js 。在项目根目录下新建一个 public 文件夹,在该文件夹中
创建 index.html _。然后将 bundle.js 引入到 html 文件中,打开到浏览器,这时候在控制台
可以看到输出的“Hello webpack!”;
上面示例代码中我们只给 entry 配置了一个入口,常见的单入口项目我们称之为
单页应用(SPA),例如:vue-cli 的开发模式就是单页应用。如果我们想配置多入口:
多页应用(MPA),我们可以使用对象的写法:
// webpack.config.js
const path = require('path');
module.exports = {
entry: {
main: './src/main.js',
index: './src/index.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].[contenthash].js',
},
};
// src/index.js
console.log('index.js');
// src/index.js
console.log('index.js');
重新执行 build 命令:
npm run build
运行命令之后会在 dist 生成两个新文件。上面我们修改了 webpack 的配置文件,将 entry 赋
值为对象,对象中的键(key)是 chunk 的名称,值描述了 chunk 的文件路径。由于使用多入
口,所以我们输出文件的文件名不应该固定一个,否则在打*过包**程会报错。webpack 提供了
一种称为 substitution(可替换模板字符串)的方式,通过带括号字符串来模板化文件名。上面
的 [name] :为此 chunk 的名称,否则使用 chunk 的 ID。 [contenthash] :
此 chunk 的 hash 值,值根据资源内容创建出唯一的 hash。
浏览器使用一种名为缓存的技术。可以通过命中缓存,以降低网络流量,使网站加载速度更快,然而如果在部署新版本时不更改资源的文件名,浏览器可能会认为他没有被更新,就会使用它的缓存版本。使用[contenthash]可以根据资源内容创建出唯一的 hash,当内容改变时,文件名会对应做出改变,避免浏览器使用缓存版本。
3.配置常用 loader
webpack 只能处理 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效的模块,以供应用程序使用,以及被添加到依赖图中。
3.1 处理 CSS
创建 css 样式文件,并将样式文件引入到 main.js 中:
/*src/assets/style.css*/
html,
body {
margin: 0;
padding: 0;
}
body {
background-color: skyblue;
}
// src/main.js
import './assets/css/style.css';
console.log('Hello webpack!');
// webpack中能通过import引入css和scss文件
由于 webpack 本身不自带处理 css 文件的能力,所以需要*载下**对应 loader 处理 css 文件,
将 css 文件转换成 webpack 能够处理的模块。
npm install style-loader css-loader --save-dev
- style-loader :把 css 插入到 DOM 中。
- css-loader :对@import 和 url()进行处理,跟 js 解析 import/require()一样。
在 webpack.config.js 中配置 css 解析规则:
// webpack.config.js
const path = require('path');
module.exports = {
entry: {
main: './src/main.js',
index: './src/index.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].[contenthash].js',
},
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
],
},
};
module 选项决定了如何处理项目中的不同类型的模块。rules 用于创建不同类型模块的处理规则。test 选项用于引入所有通过断言测试的模块,然后 use 选项决定了通过断言测试的模块使用那些 loader 进行处理,loader 可以通过传入多个已达到链式调用的效果,loader 的调用顺序为:从右到左(从下到上),每个 loader 将自己处理的结果返回给接下来要处理的 loader。上面示例代码中,css-loader 在编译时,用来处理 main.js 中通过引入 css 文件,然后将处理结果返回给 style-loader,style-loader 可以将处理的 css 代码插入到 DOM 中。
module.exports = {
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader'],
},
{
test: /\.css$/i,
use: ['css-loader'],
},
],
},
};
// 以上代码与上面的代码效果一样,loader的解析顺序为从右到左或者从下到上。
配置完 css 处理规则后,重新运行打包命令,然后将打包出来的文件引入到 index.html 中,可以看到样式通过 style 标签已经被引入到页面中,webpack 通过 css-loader 和 style-loader 能够正确的处理 css 文件。
3.2 处理 scss|sass
如果在项目开发中想使用 scss|sass,那么我们需要安装 sass-loader ,由于 sass-loader 依赖于 Dart Sass 或 Node Sass ,推荐使用 Dart Sass 。
npm install sass-loader sass --save-dev
dart sass:npm install sass --save-devnode sass: npm install node-sass --save-dev
- sass-loader :加载 Sass/SCSS 文件并将它们编译为 CSS
/*src/assets/scss/style.scss*/
$bgcolor: skyblue;
* {
margin: 0;
padding: 0;
}
body {
background-color: $bgcolor;
}
// src/main.js
import './assets/css/style.scss';
console.log('Hello webpack!');
修改 webpack.config.js:
// webpack.config.js
const path = require('path');
module.exports = {
entry: {
main: './src/main.js',
index: './src/index.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].[contenthash].js',
},
module: {
rules: [
{
test: /\.(css|scss|sass)$/i,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
],
},
};
如果你使用的是less,那么你可以使用less-loader。如果你使用的是stylus,那么你可以使用stylus-loader。
3.3 处理图片资源
在 webpack5 之前的版本,webpack 使用 file-loader 和 url-loader 来处理图像资源。
- url-loader :将文件作为 data URL 内联到 bundle 中
- file-loader :将文件发送到输出目录
找一张图片放到 src/assets/images 文件夹,修改 style.scss
// src/assets/css/style.scss
$bgcolor: skyblue;
* {
margin: 0;
padding: 0;
}
body {
width: 100vw;
height: 100vh;
background-color: $bgcolor;
background-image: url('../images/1.jpg');
background-size: cover;
background-repeat: no-repeat;
}
修改 webpack.config.js
// webpack.config.js
// 添加图像资源处理规则
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)$/i,
use: ['file-lodaer'],
},
],
},
};
配置完毕完,重新执行 build 命令。编译完成后可以看到在 scss 文件中引入的图像资源生
成在 dist 文件夹下。file-loader 会将资源发送到输出目录,并将输出之后的正确路径返回给
原本的位置进行替换。
我们还可以通过给 file-loader 配置 options 选项来配置图像资源的输出路径和文件名
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)$/i,
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'images',
},
},
],
},
};
更改 file-loader 的配置选项,提供 options.name 用来自定义输出文件名, [name] 为图像原本的文件名, [ext] 为图像原本的后缀名。 options.output 用于设置 file-loader 输出的文件将放置在那个路径下,实例代码中图像最终的输出路径为 dist/images
file-loader 相当于复制资源文件到输出目录中,然后将对应的引入的图像资源路径解析为正确的路径。而 url-loader 则是将图像资源默认处理成 base64 URL
我们来修改 webpack.config.js 中图像的处理规则:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)$/i,
use: ['url-loader'],
},
],
},
};
重新运行 build 命令,将编译完成的 js 文件引入到 index.html 中。可以看到背景图片的 url
生成了一串 base64 的字符串。如果资源图像体积过大,那么这个时候就不适合使用
url-loader 来生成 base64 字符串了。所以我们需要给 url-loader 设置一个资源体积限制,
只有小于这个限制才会将图像资源转换为 base64 字符串。
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)$/i,
loader: 'url-loader',
options: {
limit: 1024 * 10,
},
},
],
},
};
通过给url-loader设置 options.limit 选项,可以限制url-loader能够处理的资源图像大小。limit 的单位为 B(字节),上面示例代码中,我们设置如果图像资源大于等于 10KB 时将不会使用url-loader将图像资源处理成 base64 字符串,而是会使用file-loader输出图像资源到输出目录中,并解析出对应路径。如果没有*载下**file-loader,在图像超过 limit 限制时,会报错找不到file-loader。
在 webpack5 中,我们处理图像资源不再使用 file-loader 和 url-loader ,而是使用内置的 As
set Modules 替换原本的 loader。
- asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。
- asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现。
- asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader ,并且配置资源体积限制实现。
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)$/i,
type: 'asset',
},
],
},
};
在上面示例代码中,通过type: ‘asset’替换 webpack5 之前使用url-loader和配置limit选项达到一样的效果。type: ‘asset’会在type: asset/resource和type: asset/inline之间进行选择,默认小于 8kb 的文件,会使用type: asset/inline,否则使用type: asset/resource。如果想修改文件大小限制,可以修改Rule.parser.dataUrlCondition.maxSize
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)$/i,
type: 'asset',
generator: {
filename: '[name].[ext]',
},
parser: {
dataUrlCondition: {
maxSize: 1024 * 10,
},
},
},
],
},
};
通过添加 parser.dataUrlCondition.maxSize 来更改默认的 8kb 限制。generator.filename 为设置文件输出名。
3.4 转义 ES6+代码
由于各个浏览器的不同版本对 ECMA script 语法的支持程度不一,所以我们在编译代码时会使用 babel 将 ES6+代码编译成 ES5 代码,以兼容不同浏览器的不同版本。
npm install babel-loader @babel/core @babel/preset-env --save-dev
- babel-core :把 js 代码分析成 ast ,方便各个插件分析语法进行相应的处理
- babel-preset-env :将现代 JS 编译为 ES5
- **babel-loader **:用于 webpack
配置规则:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.js$/i,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
},
},
// 排除node_modules模块中的js,避免浪费性能。
exclude: /node_modules/,
},
],
},
};
配置完上述规则,我们在 main.js 中输入一段箭头函数的代码,看看编译后 babel 会如何处理我们的箭头函数。
我们可以在编译完成的 js 中看到原本的箭头函数已经改成了 ES5 的 function 形式。
4.配置常用 plugins
loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优
化,资源管理,注入环境变量。使用一个插件时,需要 require() 对应插件,然后可以通过
new 操作符创建一个实例,将这个实例添加到 plugins 数组中。
ref="http://webpack.luxiangfan.club/#/?id=_41-htmlwebpckplugin">4.1 HtmlWebpckPlugin
HtmlWebpckPlugin 可以帮助我们在每次打包完毕之后,将打包完的 js 文件引入到 html 文件
中,从而不需要每次打包都手动引入文件。
npm install html-webpack-plugin --save-dev
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
let chunks = ['main', 'index'];
let htmlPlugins = chunks.map((ele) => {
return new HtmlWebpackPlugin({
template: './public/index.html', // 生成html的模板文件,可以在模板中使用<%= htmlWebpackPlugin.options.title%>引入配置中的title字段,指定html的title的内容。
favicon: './public/favicon.ico', // 指定生成html文件中引入该ico图标
title: 'Hello webpack', // 标题
filename: `${ele}.html`, // HTML文件名
chunks: [ele], // 该plugin实例生成的HTML需要引入的chunk组
});
});
module.exports = {
plugins: [...htmlPlugins],
};
<!-- public/index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><%= htmlWebpackPlugin.options.title%></title>
</head>
<body></body>
</html>
上面示例代码中,由于在一开始我们配置了多入口,所以需要创建对应数量的htmlwebpackplugin实例,通过配置对象中的chunk选项来确定每个实例需要引入的chunk组。
ref="http://webpack.luxiangfan.club/#/?id=_42-cleanwebpackplugin">4.2 cleanWebpackPlugin
由于每次运行 build 命令都会生成名字不同的 bundle 文件,所以会导致 dist 文件夹非常
混乱。所以使用 clean-webpack-plugin 在打包之前清除清除 dist 文件夹中的内容。
npm install clean-webpack-plugin --save-dev
现在重新执行 build 命令,dist 文件夹中就只会存在本次编译的文件。
5.devServer
在每次编译代码时,手动运行 npm run build 会显得很麻烦,不利于开发。webpack 提供了
webpack-dev-server 这个工具用来帮助我们在代码发生变化后,自动编译代码,并且刷新浏
览器。
首先我们需要*载下** webpack-dev-server 这个工具
npm install webpack-dev-server --save-dev
配置 serve 命令:
// package.json
{
"scripts": {
"serve": "webpack serve --mode=development --open"
}
}
通过 cli 的方式设置 webpack 的 mode 为 development,关于 mode 下面会讲到。--open:将开启服务的网址在默认浏览器上打开。
运行 npm run serve , webpack-dev-server 会开启服务,服务的地址会打开到默认浏览器中。接下来修改项目中的代码,webpack 会自动重新编译,编译完成后刷新页面。
注意:webpack-dev-server并不会将编译好的文件写入到任何输出目录,而是将 bundle 文件保留在内存中,然后将它们 serve 到 server 中。可以在服务开启的网址后输入webpack-dev-server,就可以看到服务文件的位置。例如:http://localhost:8080/webpack-dev-server
webpack 提供了 devServer 选项,用来改变 webpack-dev-server 的行为。
在 webpack.config.js 中添加 devServer 相关配置:
// webpack.config.js
module.exports = {
devServer: {
open: true, // 自动打开服务网址到默认浏览器,可以像上面在cli中设置,也可以在devServer中设置。除了Boolean,也可以是string,例如 'Chrome'
host: '0.0.0.0',// 指定host,默认为'localhost',如果想让局域网内可访问,可设置为‘0.0.0.0’,这样通过localhost和ip地址、127.0.0.1都能访问的到
hot: true, // 开启模块热更新,当代码更改时,只需要更新对应内容,而不是刷新整个网页
hotOnly: true, // 启用热模块替换
port: 8080 // 设置端口号,默认为:'8080'
// 设置反向代理
proxy: {
'/api': {
target: 'http://localhost:3000',
pathRewrite: {
'^/api': ''
}
}
}
}
}
6.开发模式(development)|生产模式(production)
开发环境和生产环境这两个环境下的构建目标存在差异。开发环境中,需要强大的
source map 和一个有着 live reloading 或 hot module replacement 能力的
localhost server。而生产环境目标则转移至其它方面,例如压缩 bundle,
轻量的 source map,资源优化等。所以通常我们都会将 webpack 的配置以环境作为区分。
在项目根目录中新建 build 文件夹,文件中新建三个 js 文件,分别是 webpack.base.conf.js 、
webpack.dev.conf.js 、 webpack.prod.conf.js
- webpack.base.conf.js :配置生产环境与开发环境共用配置,例如:入口,常用 loader 等
- webpack.dev.conf.js :配置开发环境配置,例如:devServer、mode,source map 等
- webpack.prod.conf.js :配置生产环境配置,例如:cleanWebpackPlugin,mode,资源优化等
在区分环境时,需要*载下** webpack-merge 工具,该用具是用于合并不同文件的 webpack 配置。
npm install webpack-merge --save-dev
除了 webpack-merge ,还需要*载下**一些其它工具:
npm install cross-env postcss-loader postcss autoperfixer mini-css-extract-plugin css-minimizer-webpack-plugin uglifyjs-webpack-plugin --save-dev
- cross-env :该插件用于在不同平台设置或使用正确的环境变量
- postcss-loader :使用 postcss 处理 css 的 loader
- postcss :postcss 是一个允许使用 JS 插件转换样式的工具。例如:添加样式前缀、使用先进的 css 语法等
- autoprefixer :为样式添加不同浏览器前缀
- mini-css-extract-plugin :将 css 提取到单独的文件中。
- css-minimizer-webpack-plugin :使用 cssnano 优化和压缩 CSS。
- uglifyjs-webpack-plugin :使用 uglify-js 压缩 JavaScript。
配置 webpack.base.conf.js :
// build/webpack.conf.js
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); // 提取css样式到单独的css文件中
const HtmlWebpackPlugin = require('html-webpack-plugin');
const htmlPlugins = ['main', 'index'].map((ele) => {
return new HtmlWebpackPlugin({
template: './public/index.html',
favicon: './public/favicon.ico',
title: 'Hello webpack',
filename: `${ele}.html`,
chunks: [ele],
});
});
// 在生产环境下添加MiniCssExtractPlugin
const plugins =
process.env.NODE_ENV === 'production'
? [...htmlPlugins, new MiniCssExtractPlugin()]
: [...htmlPlugins];
module.exports = {
entry: {
index: './src/index.js',
main: './src/main.js'
},
output: {
path: path.resolve(__dirname,'dist'),
filename: '[name].[contenthash].bundle.js'
},
module: {
rules: [
{
test: /\.(css|scss|sass)$/i,
use: [
process.env.NODE_ENV === 'production' ? MiniCssExtractPlugin.loader : 'style-loader',
'css-loader',
'postcss-loader',
'sass-loader'
]
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
type: 'asset',
generator: {
filename: '[name].[ext]',
},
parser: {
dataUrlCondition: {
maxSize: 1024 * 8,
},
},
},
{
test: /\.js$/i,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
},
},
// 排除node_modules模块中的js,避免浪费性能。
exclude: /node_modules/,
},
]
},
plugins: plugins,
resolve: {
alias: {
'@': path.resolve(__dirname, '../src'),
assets: path.resolve(__dirname, '../src/assets'),
},
extensions: ['.mjs', '.js', '.jsx', '.vue', '.json', '.wasm'],
},
}
在 webpack.base.conf.js 中修改了样式文件的处理规则,首先在生产环境下使用
MiniCssExtractPlugin.loader 提取 css 样式文件到单独的 css 文件中,
因为通过 style-loader 添加样式需要等待 js 文件加载完毕之后,通过 js 操作 DOM 生成 style
标签再将样式添加到标签中。所以当 js 加载被阻塞或网速慢时,那么样式就没办法及时生
效,使得页面布局混乱。然后在 css-loader 处理之前添加了 postcss-loader ,该 loader 可以用
来为样式添加浏览器前缀或使用还未被浏览器支持的样式等。这里需要在根目录新建
postcss.config.js , postcss 默认会使用根目录下的该文件作为配置项。在共用配置还新增了
resolve 配置项, resolve.alias 用于配置路径别名,为常用的路径配置别名。
resolve.extensions 用于配置忽略文件后缀时,webpack 会以定义的 resolve.extensions 按顺
序尝试解析,如果多个文件名相同,但是后缀不同,webpack 会以数组中找到的第一个后缀
的文件作为解析对象,其余会跳过。
注意:process 是 node 中一个全局变量,用于提供当前进程相关信息。process.env 返回一个包含用户环境信息的对象。NODE_ENV 并不是 env 本身存在的一个属性,而是一个自定义属性,在前端工程化中大家约定俗成使用该属性设置或获取环境变量。由于在不同系统环境下设置属性会存在差异,为了兼容不同平台,我们会 npm 命令中使用cross-env跨平台设置环境变量。
// postcss.config.js
// autoprefixer:用于添加样式前缀
module.exports = {
plugins: [require('autoprefixer')],
};
完成共用配置后,我们来添加开发环境下的配置。
// build/webpack.dev.conf.js
const { merge } = require('webpack-merge');
const webpackBaseConf = require('./wbepack.base.conf.js');
const webpackDevConf = {
mode: 'development',
devtool: 'eval-source-map',
devServer: {
hot: true,
hotOnly: true,
port: 8090,
host: '0.0.0.0',
overlay: true,
proxy: {
'/api': {
target: 'http://localhost:3000',
pathRewrite: {
'^/api': '',
},
},
},
},
};
module.exports = merge(webpackBaseConf, webpackDevConf);
在开发环境下首先我们将 mode 设置为 development , webpack 为不同模式下内置了相应的优
化。然后设置 devtool 为 eval-source-map , devtool 用于控制是否生成以及如何生成
source map 。 eval-source-map 每个模块使用 eval() 执行,并且将源地图转换为 DataUrl 后添
加到 eval() 中。初始化源地图时比较慢,但是会在重新构建时提供比较快的速度,并生成实
际的文件。会映射到原始代码中。它会生成用于开发环境的最佳品质的源地图。
source map 是一个信息文件,存储着位置信息。用于转换后代码与转换前代码的位置的映射。
生产环境:
// build/webpack.prod.conf.js
const {merge} = require('webpack-merge');
const webpackBaseConf = require('./wbepack.base.conf.js');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); // 压缩js
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); // 压缩css
const webpackProdConf = {
mode: 'production',
plugins: [new CleanWebpackPlugin()],
optimization: {
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
// 在UglifyJs删除没有用到的代码时不输出警告
warnings: false,
compress: {
// 删除所有的 `console` 语句,可以兼容ie浏览器
drop_console: true,
// 内嵌定义了但是只用到一次的变量
collapse_vars: true,
// 提取出出现多次但是没有定义成变量去引用的静态值
reduce_vars: true,
},
output: {
// 最紧凑的输出
beautify: false,
// 删除所有的注释
comments: false,
},
},
}),
new CssMinimizerPlugin(),
],
},
}
module.exports = merge{webpackBaseConf,webpackProdConf};
在生产环境下除了设置 mode,还添加了 clean-webpack-plugin 用于在执行打包命令前,清空
dist 文件中的文件。然后还添加 optimization 选项,该选项用于配置编译代码时一些优化选
项。 optimization.minimizer 选项可以添加一个或多个压缩工具,覆盖默认的压缩工具。其中
使用 uglifyjs 压缩代码、去除注释,console 等,通过这些选项来减少 js 代码体积。css 代码
压缩使用 CssMinimizerPlugin ,该插件使用cssnano压缩代码。
通过环境我们做了 webpack 配置的区分,接下来我们需要去修改 npm 命令,默认执行
webpack 命令时,会解析项目根目录的 webpack.config.js 文件作为 webpack 的配置选项,
如果想指定 webpack 配置文件可以通过 --config
{
"scripts": {
"serve": "cross-env NODE_ENV=development webpack serve --open --config build/webpack.dev.conf.js",
"build": "cross-env NODE_ENV=production webpack --config build/webpack.dev.conf.js"
}
}
cross-env 用于设置 NODE_ENV 变量
以上就是在项目开发中常用的 webpack,更多可以查阅webpack 官网。
想要学习更多前端的知识,可以关注“广州蓝景”微信公众号 进行详细的了解。