一、核心概念
1、entry
entry:指定打包入口(指定打包的源代码)
- 单入口:
entry是一个字符串
modules.exports = {
entry: "./src/index.js",
}
- 多入口文件:
entry是一个对象
modules.exports = {
entry: {
index: "./src/index.js",
search: "./scr/search.js"
},
}
2、output
output 用来告诉 webpack 如何将编译后的文件输出到磁盘。(指定打包输出的结果代码)
- 单入口配置
modules.exports = {
entry: "./src/index.js",
output: {
filename: "bundle.js",
path: __dirname + '/dist'
}
}
- 多入口配置
module.exports = {
entry: {
index: './src/index.js',
search: './src/search.js'
},
output: {
path: path.join(__dirname, 'dist'),
// 使用 [name]占位符
filename: '[name].js'
},
mode: 'production'
}
使用 [name]占位符,通过占位符确保文件名称的唯一
占位符详细见文件指纹部分
3、Loaders
- 处理webpack不能识别的文件
- 开箱即用只支持JS和JSON两种文件类型
- 在loader中添加其他文件类型,可转换成有效模块
- 本身是一个函数,接受源文件作为参数,返回转换的结果
常见的Loaders
| 名称 | 描述 |
|---|---|
| babel-loader | 转换ES6、ES7等JS新特性语法 |
| css-loader | 支持.css文件的加载和解析 |
| less-loader | 将less文件转换成css |
| ts-loader | 将TS转换成JS |
| file-loader | 进行图片、字体等打包 |
| raw-loader | 将文件以字符串的形式导入 |
| thread-loader | 多进程打包JS和CSS |
用法
modules.exports = {
entry: "./src/index.js",
output: {
filename: "bundle.js",
path: __dirname + '/dist'
},
module: {
rules: [
// test:指定匹配规则 ,use 指定使⽤的 loader 名称
{ test: /\.txt$/,use: 'raw-loader' }
]
}
}
4、Plugins
- 增强 webpack 的功能,优化打包输出,资源管理、环境变量注入
- 作用于整个构建过程
常见的Plugins
用法
plugins 为一个数组,将所有的 plugins 放在数组里即可。
5、mode
用来指定当前构建的环境变量:production、development和none
设置mode,可以使用webpack内置的函数,默认值是production
module.exports = {
entry: {
index: './src/index.js',
search: './src/search.js'
},
output: {
path: path.join(__dirname, 'dist'),
// 使用 [name]占位符
filename: '[name].js'
},
mode: 'production',
}
二、资源解析
Babel 是一个 JavaScript 的转译器。它的主要功能就是把 ES2015+ 的代码转换为 ES5 或更低版本的 JavaScript 代码。有了 Babel,您就可以使用最新的 JavaScript 语法,不用太担心浏览器的兼容问题。
babel 两个重要的部分 plugins 和 presets 。
- plugins:一个
plugins对应一个功能 - presets:是一系列 plugins 的集合。(在babel编译之前,babel需要知道你的编译规则,到底是以什么样的规范去编译。)
webpack 中配置 babel 的相关参考文档:
1、解析ES6
解析 es6 需要安装的插件
npm install @babel/core @babel/preset-env babel-loader -D
新建.babelrc文件,配置babel preset:
{
"presets": [
"@babel/preset-env"
]
}
在webpack.config.js 中配置 babel
module.exports = {
entry: {
index: './src/index.js',
search: './src/search.js'
},
output: {
path: path.join(__dirname, 'dist'),
// 使用 [name]占位符
filename: '[name].js'
},
mode: 'production',
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader'
}
]
}
}
2、解析React JSX
下载相关插件
npm i react react-dom @babel/preset-react -D
增加 react 的babel preset配置
//.babelrc
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
举个🌰:
1、新建search.js文件:
'use strict';
import React from 'react';
import ReactDom from 'react-dom';
class Search extends React.Component{
render() {
return <div>Search Text</div>
}
}
ReactDom.render(
<Search/>,
document.getElementById('root')
)
2、通过 npm run build打包后,在dist目录下新建一个search.html文件,引入打包后的search.js文件,会显示“Search Text”,说明编译成功。
3、search.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>Document</title>
</head>
<body>
<div id="root"></div>
</body>
<script src="./search.js" type="text/javascript"></script>
</html>
3、解析CSS
css-loader用于加载.css文件,并且转换成commonjs对象(加载时机:代码里有引入css文件)style-loader将样式通过<style></style>标签插入到 head 中 安装插件
npm i style-loader css-loader -D
在webpack中配置:(webpack.config.js)
module.export = {
...
...
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader'
},
{
test: /\.css$/,
// 注意先后顺序
use: ['style-loader', 'css-loader']
},
]
}
}
配置css相关的loader需要注意先后顺序:
loader的调用是链式调用,执行顺序是从右往左调用- 原理是:先通过
css-loader解析 css ,然后将解析好的 css 传递给style-loader,将 css 插入到 head 中 - 编写顺序:style-loader =》 css-loader
4、解析Less和Sass
less-loader,用于将 less 转转成 css 安装插件
npm i less less-loader -D
在 webpack 中配置:(webpack.config.js)
module.export = {
...
...
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader'
},
{
test: /\.css$/,
// 注意先后顺序
use: ['style-loader', 'css-loader']
},
{
test: /\.less$/,
// 注意先后顺序
use: ['style-loader', 'css-loader', 'less-loader']
},
]
}
}
5、解析图片
使用 url-loader 处理图片
npm i url-loader -D
在webpack中配置:(webpack.config.js)
module.export = {
...
...
module: {
...
...
rules: [
{
test: /\.(png|jpg|gif|jpeg)$/,
use: 'url-loader'
},
]
}
}
6、解析字体
使用file-loader处理文件
npm i file-loader -D
在webpack中配置:(webpack.config.js)
module.export = {
...
...
module: {
...
...
rules: {
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: ['file-loader']
},
}
}
}
三、webpack文件监听
源代码发生改变时,自动重新构建出新的输出文件。
webpack开启自动监听的模式,有两种方式:
- 启动
webpack命令时,带上--watch参数 - 配置
webpack.config.js中设置watch:true
启动:
npm run watch ;
页面改变,手动刷新,页面会刷新。
缺陷:需要手动刷新浏览器
文件监听的原理:
四、webpack的自动编译
为了完成自动编译,webpack提供了三种可选的方式:
- Webpack watch mode
- Webpack-dev-server
- Webpack-dev-middleware
1、watch mode
- 在该模式下,webpack依赖图中所有文件,只要有一个发生了更新,那么代码将被重新编译
- 不需要手动
npm run build - 没有自动刷新浏览的功能
- 开启方式:
- ①在配置文件中增加配置项:
"watch": true - ②在启动webapck的命令中,添加
--watch标识
- ①在配置文件中增加配置项:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"watch": "webpack --watch"
},
2、WDS
在webpack.config.js中配置 webpack-dev-server简称 WDS,可实现热更新的效果。
下载:
npm install --save webpack-dev-server
webpack.config.js中的配置
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"dev": "webpack-dev-server --open"
},
打包更新命令:
npm run dev
--open :意思是构建完成,自动开启浏览器
WDS 热更新特点:
-
①不刷新浏览器,不输出文件,而是放在内存中(构建速度快)
-
②
webpack-dev-server配置,会自动添加HotModuleReplacementPlugin插件HotModuleReplacementPlugin作用:
- webpack 构建出来的 bundle.js 本身是不具备热更新的能力的
- HotModuleReplacementPlugin 的作用就是将 HMR runtime 注入到 bundle.js,使得 bundle.js 可以和 HMR server 建立 websocket 的通信连接
-
③ webpack-dev-server v4.0.0+ 和 webpack >= v5.0.0和 webpack-cli >= v4.7.0 只需要配置
package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --mode=production --config webpack.prod.js",
"dev": "webpack-dev-server --mode=development --config webpack.dev.js",
"watch": "webpack --watch webpack.dev.js"
},
举个例子:
在日常开发过程中,通常会按不同开发环境进行打包配置,这里按区分开发环境和生成环境进行简单配置:
package.json 配置
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --mode=production --config webpack.prod.js",
"dev": "webpack-dev-server --mode=development --config webpack.dev.js",
"watch": "webpack --watch webpack.dev.js"
},
说明:
webpack-dev-server --mode=development --config webpack.dev.js:mode是development时,打包的配置文件是webpack.dev.js,可通过命令:npm run dev打包执行webpack --mode=production --config webpack.prod.js:mode是production时,打包的配置文件是webpack.prod.js,可通过命令:npm run build打包执行
开发环境配置文件:webpack.dev.js
'use strict';
const path = require('path');
module.exports = {
entry: {
index: './src/index.js',
search: './src/search.js'
},
output: {
path: path.join(__dirname, 'dist'),
// 使用 [name]占位符
filename: '[name].js'
},
mode: 'development',
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader'
},
// loader的调用是链式调用,执行顺序是从右往左调用,所以先通过css-loader解析css,然后将解析好的css通传递给style-loader,将css插入到head中
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
},
{
test: /\.(png|jpg|gif|jpeg)$/,
use: 'url-loader'
},
{
test: /\.(woff|woff2|eot|ttf|otf|otf)$/,
use: [
'file-loader'
]
}
]
}
}
生成环境配置文件: webpack.prod.js
'use strict';
const path = require('path');
module.exports = {
entry: {
index: './src/index.js',
search: './src/search.js'
},
output: {
path: path.join(__dirname, 'dist'),
// 使用 [name]占位符 设置文件指纹
filename: '[name]_[chunkhash:8].js'
},
mode: 'production',
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader'
},
// loader的调用是链式调用,执行顺序是从右往左调用,所以先通过css-loader解析css,然后将解析好的css通传递给style-loader,将css插入到head中
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
},
{
test: /\.(png|jpg|gif|jpeg)$/,
use: [
{
loader: 'file-loader',
options: {
// 设置文件指纹
name: '[name]_[hash:8][ext]'
}
}
]
},
{
test: /\.(woff|woff2|eot|ttf|otf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
// 设置文件指纹
name: '[name]_[hash:8][ext]'
}
}
]
}
]
}
}
3、WDM
-
webpack-dev-middleware(WDM)是一个封装器,它可以把webpack处理过的文件发送到一个server(服务器)- webpack-dev-server在内部使用了它,然而它也可以作为一个单独的package来使用,以便根据需求进行更多自定义配置
- 搭配一个服务器来使用它,比如express.
npm install --save express webpack-dev-middleware
-
编写Server.js
const express = require("express")
const webpack = require("webpack")
const webpackDevMiddleware = require("webpack-dev-middleware")
const app = express()
const config = require("./webpack.config")
const compiler = webpack(config)
app.use(webpackDevMiddleware(compiler,{
publicPath:config.output.publicPath
}),()=>{
console.log("这里是回调函数")
})
app.listen(3000,()=>{
console.log("Server running")
})
- Node Server.js即可运行起一个服务,并监听文件更改和刷新浏览器。
五、热更新原理
1、HMR
webpack-dev-server 配置,会自动添加HotModuleReplacementPlugin (简称HMR)插件。
HotModuleReplacementPlugin作用:
- webpack 构建出来的 bundle.js 本身是不具备热更新的能力的
- HotModuleReplacementPlugin 的作用就是将
HMR runtime注入到bundle.js,使得 bundle.js 可以和HMR server建立websocket的通信连接
2、热更新原理分析
- Wepack Compile:是
webpack的编译器,将JS源代码编译成Bundle.js(最终打包输出的文件) - Bundle Server:提供文件在浏览器的访问。将编译好的
Bundle.js文件通过服务器的方式访问,比如:localhaost:8080/bundle.js。也就是我们平时能够正常通过localhost访问我们本地网站的原因 - HMR Server(服务端):将热更新的文件(变化的 js 模块)通过
websocket的消息通知HMR Runtime(浏览器端) - HMR Runtime(浏览器端):
- 用于接收
HMR Server传递的模块数据,浏览器端可以看到.hot-update.json的文件过来。 - 在开发的打包阶段,
HMR Runtime会被注入到浏览器的bundle.js里面,此时,HMR Runtime会跟服务器建立一个websocket连接,收到更新数据时,会自动更新文件。
- 用于接收
- Bundle.js:构建输出的文件。
- HotModuleReplacementPlugin的作用:webpack 构建出来的 bundle.js 本身是不具备热更新的能力的,HotModuleReplacementPlugin 的作用就是将 HMR runtime 注入到 bundle.js,使得bundle.js可以和HMR server建立websocket的通信连接
- webpack-dev-server和hot-module-replacement-plugin之间的关系:
- webpack-dev-server(WDS)的功能提供 bundle server的能力,就是生成的 bundle.js 文件可以通过 localhost://xxx 的方式去访问,另外 WDS 也提供 livereload(浏览器的自动刷新)。
- hot-module-replacement-plugin 的作用是提供 HMR 的 runtime,并且将 runtime 注入到 bundle.js 代码里面去。一旦磁盘里面的文件修改,那么 HMR server(服务端) 会将有修改的 js module 信息发送给 HMR runtime(客户端),然后 HMR runtime 去局部更新页面的代码。因此这种方式可以不用刷新浏览器。
- 单独写两个包也是出于功能的解耦来考虑的。简单来说就是:hot-module-replacement-plugin 包给 webpack-dev-server 提供了热更新的能力。
3、热更新的过程
启动阶段:
- 在文件系统里进行编译,将初始代码经过
webpack complier进行打包。 - 打包完成之后,将编译好的文件传输给
bundle server(服务器),bundle server可以让打包好的bundle.js文件通过服务器的方式进行访问(localhaost:8080/bundle.js)。 - 图中展示:1 -> 2 -> A -> B 文件更新阶段:(本地文件改变,更新)
- 本地开发,文件更新之后,文件系统变化后,代码会经过webpack compiler进行编译。
- 编译之后的代码发送给HMR Server(服务端),HMR Server会知道哪些js源代码模块进行了改变,并将改变的模块通过json的格式进行传输,传输给HMR Runtime(浏览器端)。
- HMR Runtime会局部更新代码,并且不需要刷新页面,因为有hot-module-replacement-plugin插件
- 图中展示:1 -> 2 -> 3 -> 4
参考文档:
六、文件指纹
定义:打包后输出的文件名的后缀。
常见的文件指纹:
- ①Hash:和整个项目的构建相关,只要项目文件有修改,整个项目构建的hash值就会更改
- ②Chunkhash:和
webpack打包的chunk(模块)有关,不同的entry会生成不同的chunkhash值。某个文件发生变化,不会影响其他的问题件。(js文件指纹采用)
module.export = {
...
output: {
path: path.join(__dirname, 'dist'),
// 使用 [name]占位符 设置文件指纹
filename: '[name]_[chunkhash:8].js'
},
}
- ③Contenthash:根据文件内容来定义
hash,文件内容不变,则 contenthash 不变。(css文件指纹采用)
设置文件指纹时,占位符对应表:
举个例子-css文件指纹的设置
安装插件
npm i mini-css-extract-plugin -D
webpack.prod.js
// webpack.prod.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.export = {
...
// 压缩css
module: {
rules: [
{
test: /\.css$/,
use: [
// style-loader是把css插入到header里,而MiniCssExtractPlugin是将css提取到一个独立的文件里,所以两者存在冲突
// 'style-loader',
MiniCssExtractPlugin.loader,
'css-loader'
]
},
]
},
plugins: [
// 设置css文件指纹
new MiniCssExtractPlugin({
// name 文件名称占位符
// contenthash:8 采用contenthash的前八位
filename: '[name]_[contenthash:8]'
})
]
}
七、代码压缩
JS文件压缩
webpack 内置了 uglifyjs-webpack-plugin 插件进行js压缩
CSS文件压缩
安装optimize-css-assets-webpack-plugin和cssnano(css预处理器)
npm install cssnano -D
安装css压缩插件时会报错
解决方法一:降低npm版本
解决方法二:
npm install optimize-css-assets-webpack-plugin -D -- force
// 或者
npm install optimize-css-assets-webpack-plugin -D --legacy-peer-deps
css压缩配置:
// webpack.prod.js
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
plugins: [
new MiniCssExtractPlugin({
filename: '[name]_[contenthash:8].css'
}),
// css压缩
new OptimizeCSSAssetsPlugin(),
]
}
打包后(npm run build),就可以看到压缩后的css文件
备注:webpack5.0使用的插件官方文档
HTML文件的压缩
插件安装:
npm i html-webpack-plugin -D
html压缩配置
// webpack.prod.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
new MiniCssExtractPlugin({
filename: '[name]_[contenthash:8].css'
}),
new OptimizeCSSAssetsPlugin(),
// html压缩配置 一个页面对应一个HtmlWebpackPlugin
new HtmlWebpackPlugin({
// 模板所在位置
template: path.join(__dirname, 'src/index.html'),
// 指定打包后的文件名称
filename: 'index.html',
// 指定生成的html需要哪些chunk
chunks: ['index'],
// 打包后的chunk会自动注入到html中
inject: true,
minify: {
html5: true,
collapseWhitespace: true,
preserveLineBreaks: false,
minifyCSS: true,
minifyJS: true,
removeComments: false
}
}),
// html压缩配置 一个页面对应一个HtmlWebpackPlugin
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src/search.html'),
filename: 'search.html',
chunks: ['search'],
inject: true,
minify: {
html5: true,
collapseWhitespace: true,
preserveLineBreaks: false,
minifyCSS: true,
minifyJS: true,
removeComments: false
}
})
]
执行npm run build 会生成经过压缩的文件