webpack是一个静态模块打包器,此处的模块可以是任意文件,包括Sass、TypeScript、模板和图像等。webpack可根据输入文件的依赖关系,打包输出浏览器可识别的JavaScript、CSS和HTML等文件,并且能对图像做优化处理,如图1所示。
:-: ![](https://img.kancloud.cn/a0/69/a0698348b7b3da6c2eed00fb40f2edb0_600x229.png)
图1 webpack打包
  目前,webpack的最新版本是4.33,其配置文件(webpack.config.js)的基本结构如下所示,包含了它的4个核心概念:入口(entry)、输出(output)、加载器(loader)和插件(plugin)。
~~~
module.exports = {
entry: {}, //入口
output: {}, //输出
module: { rules: [] }, //加载器
plugins: [] //插件
};
~~~
## 一、安装
  如果要安装最新版本的webpack,那么可以运行下面这条命令。
~~~
npm install --save-dev webpack
~~~
  当使用的是webpack 4+版本时,还需要再安装它的命令行工具,安装命令如下所示。
~~~
npm install --save-dev webpack-cli
~~~
  接下来就可以运行webpack的打包命令了,如下所示,其中“--config”参数后面会跟着配置文件的路径。
~~~
npx webpack --config webpack.config.js
~~~
  通过package.json的scripts字段可声明自定义的脚本任务(如下代码所示),从而就能快捷的执行打包命令,例如“npm run build”。
~~~
{
scripts: {
build: "npx webpack --config webpack.config.js"
}
}
~~~
## 二、入口
  在webpack.config.js中,entry字段是一个入口,记录着需要处理的模块。从这个入口开始,webpack会递归地构建出模块之间的依赖关系。
**1)单个入口**
  当entry字段是一个字符串类型时,其值就是模块的相对或绝对路径,如下所示。
~~~
module.exports = {
entry: "./index.js"
};
~~~
  这其实是一种简写,等价于下面的对象形式。对象的键就是chunk的名称,[chunk](https://webpack.docschina.org/glossary)是webpack的特定术语,通常一个chunk对应或多个chunk组成一个bundle(即打包生成的文件)。
~~~
module.exports = {
entry: {
main: "./index.js"
}
};
~~~
  entry字段的值既可以是对象和字符串,也可以是由模块路径组成的数组,如下代码所示,其中index.js和list.js两个文件会被合并成一个chunk。
~~~
module.exports = {
entry: ["./index.js", "./list.js"]
};
~~~
**2)多个入口**
  只要在entry的对象中添加多个属性就能设置多个入口,如下所示。
~~~
module.exports = {
entry: {
index: "./index.js",
list: __dirname + "/list.js"
}
};
~~~
  两个chunk会被分别命名为index和list,其中\_\_dirname是Node.js内置的全局变量,保存着当前文件所处目录的绝对路径。
## 三、输出
  在webpack.config.js中,output字段是一个对象,用于配置输出的信息。它的filename属性可声明输出的文件名,而另一个path属性可配置输出目录的绝对路径。与入口不同,在配置文件中只能存在一个输出。
**1)filename**
  如果在入口中声明了多个chunk,那么在配置输出时得用占位符来表示对应的bundle名称,如下所示。
~~~
module.exports = {
entry: {
index: "./index.js",
list: "./list.js"
},
output: {
filename: "[name].bundle.js"
}
};
~~~
  \[name\]表示chunk的名称,例如index和list,还有另外三个常用的占位符,如表3所示。
:-: ![](https://img.kancloud.cn/07/39/073987347eadf2065033402b831e7634_600x301.png)
表3 filename中的占位符
  \[hash\]常与\[name\]配合使用(如下所示),在运行打包命令后,默认会在配置文件所处的位置创建dist目录,并且把生成的bundle文件放置其中。
~~~
module.exports = {
output: {
filename: "[name].[hash].bundle.js"
}
};
~~~
**2)path**
  如果要更改默认的输出路径(即不想在dist目录下生成bundle文件),那么可以通过设置path实现。但要注意,它的值必须是绝对路径,不能是相对路径,如下所示。
~~~
module.exports = {
output: {
path: __dirname + "/build"
}
};
~~~
## 四、加载器
  加载器(loader)能在webpack加载模块时对其进行预处理,即对模块的源码进行转换,下面列出加载器的几个比较典型的用途。
  (1)将浏览器无法识别的JSX、Sass等语言转换成JavaScript、CSS等语言。
  (2)把图像转换成Data URI格式嵌入到JavaScript文件中。
  (3)用ES6的import关键字将CSS文件导入到JavaScript中。
**1)配置**
  加载器不仅需要单独安装,还得在webpack.config.js中配置module字段。下面是一个简单的配置示例,其作用是让file-loader加载器处理四种类型的图像。
~~~
module.exports = {
module: {
rules: [{
test: /\.(png|svg|jpg|gif)$/,
use: [ "file-loader" ]
}]
}
};
~~~
  module的值是一个对象,包含一个rules属性,记录了模块匹配的规则,而这些规则又会以对象的形式组成一个数组,作为rules属性的值。每条规则必须包含test和use两个属性,前者是一个正则表达式,可设置匹配条件;后者是一个由字符串或对象组成的数组,可指定要使用的加载器。只有当test属性中的条件匹配成功后,才会让use属性中的加载器处理相应的模块。
  接下来将展示加载器的用法,以上面的file-loader为例,首先通过命令将其安装,如下所示。
~~~
npm install --save-dev file-loader
~~~
  然后创建一个index.js文件,其内容就是引入一张avatar.jpg图像,并将实例化的Image对象添加到页面的元素内,如下所示。
~~~
import src from "./avatar.jpg";
var img = new Image();
img.src = src;
document.body.appendChild(img);
~~~
  再创建一张index.html页面,引用打包生成的index.bundle.js文件,如下所示。
~~~
<body>
<script src="dist/index.bundle.js"></script>
</body>
~~~
  最后执行webpack的打包命令,就会在index.html的元素中添加下面的元素。
~~~
<img src="68fd51ab711118f323bdddf6de7a0175.jpg" />
~~~
  注意,默认情况下,图像会随着bundle文件一起被放置到dist目录下。这样的话,上述元素将无法读取到图像,得为其src属性加上路径。为了解决该问题,可以利用file-loader提供的配置参数。下面将use属性中的规则修改成对象形式,用options参数记录publicPath选项,即定义图像的发布目录。
~~~
module.exports = {
module: {
rules: [{
test: /\.(png|svg|jpg|gif)$/,
use: [{
loader: "file-loader",
options: { publicPath: "./dist" }
}]
}]
}
};
~~~
  当一个条件对应多个加载器时,其执行顺序是从右到左。以下面的加载器为例,先执行url-loader,后执行file-loader。
~~~
module.exports = {
module: {
rules: [{
test: /\.(png|svg|jpg|gif)$/,
use: [ "file-loader", "url-loader" ]
}]
}
};
~~~
  由于目前市面上的加载器有可能无法满足实际需求,因此官方提供了自定义加载器的方法,具体可参考[相关文档](https://www.webpackjs.com/contribute/writing-a-loader/)。
**2)Babel**
  在之前的[第2篇](https://www.cnblogs.com/strick/p/10984632.html)中,对Babel做了专门的讲解,现在利用加载器可以将Babel的配置写到webpack.config.js中,接下来将演示在webpack中使用Babel。
  首先安装babel-loader、Babel的核心包以及集合了ES语法的预设,命令如下所示。
~~~
npm install --save-dev babel-loader @babel/core @babel/preset-env
~~~
  然后在webpack.config.js的module字段中配置规则,如下所示。
~~~
module.exports = {
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
use: [{
loader: "babel-loader",
options: {
presets: ['@babel/preset-env']
}
}]
}]
}
};
~~~
  rules中的exclude属性定义了忽略的条件,即不会对node\_modules目录中的脚本文件执行babel-loader。在babel-loader的presets选项中声明了所使用的预设。当执行打包命令后,就会对下面这样的ES6语法进行编译,转换成低版本的ES语法。
~~~
let fn = () => true;
~~~
## 五、插件
  插件能够借助webpack引擎的能力,将自定义的行为注入到webpack的构建流程中,解决加载器无法实现的功能,例如分离打包、压缩文件等。插件不仅能处理模块和编译过的资源,还能监控文件的变化。与加载器一样,插件也可根据特定需求实现自定义,具体可参考[官方文档](https://www.webpackjs.com/contribute/writing-a-plugin/)。
  插件的配置被放在了plugins字段中,它的值是一个由插件实例组成的数组。接下来将通过html-webpack-plugin来演示插件的用法。
**1)配置**
  html-webpack-plugin不仅能根据模板生成一个HTML文件,还能自动引入所需的bundle文件,对于那些随着编译而发生名称变化的bundle文件特别有用。
  要使用它,首先需要将其安装,相关的命令如下所示。
~~~
npm install --save-dev html-webpack-plugin
~~~
  然后在webpack.config.js中声明插件的实例,如下配置所示,在初始化插件时,没有为其传递任何参数。
~~~
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
index: "./index.js"
},
output: {
filename: "[name].[hash].bundle.js"
},
plugins: [
new HtmlWebpackPlugin()
]
};
~~~
  最后执行打包命令,生成的HTML文件如下所示,其中脚本文件的名称包含了一个hash值。
~~~
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack App</title>
</head>
<body>
<script src="index.4ee657c406f9babd171a.bundle.js"></script>
</body>
</html>
~~~
**2)自定义模板**
  除了上面所使用的默认模板之外,html-webpack-plugin还提供了自定义模板的功能。默认情况下,html-webpack-plugin采用的是EJS模板引擎,声明的加载器是ejs-loader。如果要使用其它模板引擎,那么必须得把相应的加载器添加到配置文件中。
  下面利用[EJS模板的语法](https://ejs.co/#docs)插入页面标题,首先创建一个template.html模板文件,如下所示。
~~~
<!DOCTYPE html>
<html>
<head>
<title><%= htmlWebpackPlugin.options.title %></title>
<meta charset="utf-8" />
</head>
<body>
<div>模板内容</div>
</body>
</html>
~~~
  然后向插件传递两个参数:title和template,前者就是页面标题,后者是模板路径。如此设置之后,就能渲染出所需要的页面了。
~~~
module.exports = {
plugins: [
new HtmlWebpackPlugin({
title: "模板",
template: "./template.html"
})
]
};
~~~
## 六、模块化
  webpack实现了一套兼容所有模块化方案的机制,这让任意文件皆有可能成为模块,而构建依赖图和按需打包等功能则都由webpack内部完成。
  在webpack中,能够表达模块之间依赖关系的方式有多种,例如下面所列的。
  (1)CommonJS规范的require()函数。
  (2)AMD规范的define()函数。
  (3)ES6标准的import语句。
  (4)Sass和Less中的@import语句。
  (5)CSS中引用图像的url()函数。
  (6)元素用于加载图像的src属性。
*****
> 原文出处:
[博客园-前端利器躬行记](https://www.cnblogs.com/strick/category/1472499.html)
[知乎专栏-前端利器躬行记](https://zhuanlan.zhihu.com/pwtool)
已建立一个微信前端交流群,如要进群,请先加微信号freedom20180706或扫描下面的二维码,请求中需注明“看云加群”,在通过请求后就会把你拉进来。还搜集整理了一套[面试资料](https://github.com/pwstrick/daily),欢迎浏览。
![](https://box.kancloud.cn/2e1f8ecf9512ecdd2fcaae8250e7d48a_430x430.jpg =200x200)
推荐一款前端监控脚本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不仅能监控前端的错误、通信、打印等行为,还能计算各类性能参数,包括 FMP、LCP、FP 等。
- ES6
- 1、let和const
- 2、扩展运算符和剩余参数
- 3、解构
- 4、模板字面量
- 5、对象字面量的扩展
- 6、Symbol
- 7、代码模块化
- 8、数字
- 9、字符串
- 10、正则表达式
- 11、对象
- 12、数组
- 13、类型化数组
- 14、函数
- 15、箭头函数和尾调用优化
- 16、Set
- 17、Map
- 18、迭代器
- 19、生成器
- 20、类
- 21、类的继承
- 22、Promise
- 23、Promise的静态方法和应用
- 24、代理和反射
- HTML
- 1、SVG
- 2、WebRTC基础实践
- 3、WebRTC视频通话
- 4、Web音视频基础
- CSS进阶
- 1、CSS基础拾遗
- 2、伪类和伪元素
- 3、CSS属性拾遗
- 4、浮动形状
- 5、渐变
- 6、滤镜
- 7、合成
- 8、裁剪和遮罩
- 9、网格布局
- 10、CSS方法论
- 11、管理后台响应式改造
- React
- 1、函数式编程
- 2、JSX
- 3、组件
- 4、生命周期
- 5、React和DOM
- 6、事件
- 7、表单
- 8、样式
- 9、组件通信
- 10、高阶组件
- 11、Redux基础
- 12、Redux中间件
- 13、React Router
- 14、测试框架
- 15、React Hooks
- 16、React源码分析
- 利器
- 1、npm
- 2、Babel
- 3、webpack基础
- 4、webpack进阶
- 5、Git
- 6、Fiddler
- 7、自制脚手架
- 8、VSCode插件研发
- 9、WebView中的页面调试方法
- Vue.js
- 1、数据绑定
- 2、指令
- 3、样式和表单
- 4、组件
- 5、组件通信
- 6、内容分发
- 7、渲染函数和JSX
- 8、Vue Router
- 9、Vuex
- TypeScript
- 1、数据类型
- 2、接口
- 3、类
- 4、泛型
- 5、类型兼容性
- 6、高级类型
- 7、命名空间
- 8、装饰器
- Node.js
- 1、Buffer、流和EventEmitter
- 2、文件系统和网络
- 3、命令行工具
- 4、自建前端监控系统
- 5、定时任务的调试
- 6、自制短链系统
- 7、定时任务的进化史
- 8、通用接口
- 9、微前端实践
- 10、接口日志查询
- 11、E2E测试
- 12、BFF
- 13、MySQL归档
- 14、压力测试
- 15、活动规则引擎
- 16、活动配置化
- 17、UmiJS版本升级
- 18、半吊子的可视化搭建系统
- 19、KOA源码分析(上)
- 20、KOA源码分析(下)
- 21、花10分钟入门Node.js
- 22、Node环境升级日志
- 23、Worker threads
- 24、低代码
- 25、Web自动化测试
- 26、接口拦截和页面回放实验
- 27、接口管理
- 28、Cypress自动化测试实践
- 29、基于Electron的开播助手
- Node.js精进
- 1、模块化
- 2、异步编程
- 3、流
- 4、事件触发器
- 5、HTTP
- 6、文件
- 7、日志
- 8、错误处理
- 9、性能监控(上)
- 10、性能监控(下)
- 11、Socket.IO
- 12、ElasticSearch
- 监控系统
- 1、SDK
- 2、存储和分析
- 3、性能监控
- 4、内存泄漏
- 5、小程序
- 6、较长的白屏时间
- 7、页面奔溃
- 8、shin-monitor源码分析
- 前端性能精进
- 1、优化方法论之测量
- 2、优化方法论之分析
- 3、浏览器之图像
- 4、浏览器之呈现
- 5、浏览器之JavaScript
- 6、网络
- 7、构建
- 前端体验优化
- 1、概述
- 2、基建
- 3、后端
- 4、数据
- 5、后台
- Web优化
- 1、CSS优化
- 2、JavaScript优化
- 3、图像和网络
- 4、用户体验和工具
- 5、网站优化
- 6、优化闭环实践
- 数据结构与算法
- 1、链表
- 2、栈、队列、散列表和位运算
- 3、二叉树
- 4、二分查找
- 5、回溯算法
- 6、贪心算法
- 7、分治算法
- 8、动态规划
- 程序员之路
- 大学
- 2011年
- 2012年
- 2013年
- 2014年
- 项目反思
- 前端基础学习分享
- 2015年
- 再一次项目反思
- 然并卵
- PC网站CSS分享
- 2016年
- 制造自己的榫卯
- PrimusUI
- 2017年
- 工匠精神
- 2018年
- 2019年
- 前端学习之路分享
- 2020年
- 2021年
- 2022年
- 2023年
- 日志
- 2020