webpack的四篇学习笔记
配置详解
- entry的不同配置写法

- ouput的不同配置写法

- module的不同配置的写法

- resolve的不同配置的写法

- devServer的不同配置的写法

- optimization的不同配置的写法

entry的不同配置写法
- 第一种写法:字符串写法(单入口),
即entry:'./src/index.js'
的配置写法,构建目录和具体的配置代码如下:

- optimization的不同配置的写法
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry:'./src/index.js',
output: {
filename: 'built.js',
path: resolve(__dirname, 'build')
},
plugins: [
new HtmlWebpackPlugin()
],
mode: 'development'
};
----------------------------------------------
import add from './add';
import count from './count';
console.log('index.js文件加载了~');
console.log(add(1, 2));
console.log(count(3, 2));
----------------------------------------------
function add(x, y) {
return x + y;
}
export default add;
----------------------------------------------
function count(x, y) {
return x - y;
}
export default count;
- 第一种写法:执行webpack指令之后,我们会发现webpack自动构建输出了一个JS文件(
即build/built.js文件
),又因为我们使用了插件new HtmlWebpackPlugin()
,这个插件会自动帮我们生成了build/index.html
并自动帮我们引入了打包输出的所有资源(这里指的是build/built.js
)。
- 第一种写法的总结:单入口写法即(
entry:'./src/index.js'
),打包形成一个chunk,输出一个bundle文件,此时chunk的名称默认是 main 。(在上面的webpack.config.js配置中我们指定了输出的文件名称
)

- 第一种写法的注意点:如果我们使用
output:{filename: '[name].js',path:resolve(__dirname, 'build')},
的配置写法,则输出的该JS文件的名称是默认的名称(即build/main.js
),验证了chunk的名称默认是 main。代码如下:
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry:'./src/index.js',
output: {
filename: '[name].js',
path: resolve(__dirname, 'build')
},
plugins: [new HtmlWebpackPlugin()],
mode: 'development'
};

- 第二种写法:数组写法(多入口)。即
entry:['./src/index.js', './src/count.js']
的配置写法。数组写法的特点是所有写在数组中的入口文件最终只会形成一个chunk, 输出出去依旧是只有一个bundle文件。
- 第二种写法的注意点:数组写法里的各个文件,打包构建后所有下标大于0的文件资源都会被一起打包整合进下标为0的文件中,如下图所示(
默认输出的chunk names叫做main
):

- 第二种写法的注意点:回顾之前的使用HMR优化打包构建速度的代码中,我们因为使用HMR功能导致了源目录下的
src/index.html
文件不仅使用不了HMR,而且还丧失了WDS的功能。由于HTML页面本身不需要做HMR功能,所以我们只要让其恢复WDS的功能即可,我们的解决方式如下:
- 第二种写法的注意点:数组写法通常都是用于解决HTML页面因为引入HMR功能后WDS功能失效的问题。
entry: ['./src/js/index.js', './src/index.html']
- 第三种写法:对象写法(多入口)。对象写法的
key
对应着名称,value
对应着文件路径。此时有几个入口文件就形成几个chunk,输出几个bundle文件。注意:chunk names的名称是 key。
- 我们先来看第三种写法下的第一种情况,代码如下:
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
index: './src/index.js',
add: './src/add.js'
},
output: {
filename: '[name].js',
path: resolve(__dirname, 'build')
},
plugins: [new HtmlWebpackPlugin()],
mode: 'development'
};

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
index: ['./src/index.js', './src/count.js'],
add: './src/add.js'
},
output: {
filename: '[name].js',
path: resolve(__dirname, 'build')
},
plugins: [new HtmlWebpackPlugin()],
mode: 'development'
};
- 第三种写法注意点:entry对象写法下
index: ['./src/index.js', './src/count.js']
所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件。
- 第三种写法注意点:entry对象写法下
add: './src/add.js'
形成一个chunk,输出一个bundle文件。
- 第三种写法的启迪:在使用dll来优化打包构建速度中我们配置
webpack.dll.js文件
时,可以采用entry对象写法下的对象写法,具体代码如下:
const { resolve } = require('path');
const webpack = require('webpack');
module.exports = {
entry: {
jquery: ['jquery'],
react:['react','react-dom','react-router-dom']
},
output: {
filename: '[name].js',
path: resolve(__dirname, 'dll'),
library: '[name]_[hash]'
},
plugins: [
new webpack.DllPlugin({
name: '[name]_[hash]',
path: resolve(__dirname, 'dll/manifest.json')
})
],
mode: 'production'
};
- entry的不同配置写法的总结:

ouput的不同配置写法
- 场景:我们接下来在单入口(
即entry: './src/index.js'
)的场景下来研究ouput的不同配置写法。我们可以先回顾一下我们之前写过的ouput的配置写法,代码如下:
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'js/[name].js',
path: resolve(__dirname, 'build'),
},
plugins: [new HtmlWebpackPlugin()],
mode: 'development'
};
- ouput的配置之publicPath:一般适用于生产环境,可以指定所有资源引入的公共路径的前缀。例如当我配置
publicPath: '/'
后,我原先的路径 'imgs/a.jpg'
经过它的处理会变成'/imgs/a.jpg'
,路径的转变会导致服务器在请求文件时找的地址发生对应的改变。(决定的是资源引入的路径而不是输出的路径
)
- ouput的配置之chunkFilename:指定非入口chunk的名称,在这里我们配置了
entry: './src/index.js',
,所以src/index.js
文件就是入口chunk
(多入口时也就是多入口chunk),其他就是非入口chunk。
- ouput的配置之chunkFilename的注意点:什么chunk的名称命名会遵循chunkFilename的配置呢?
- 一个是通过import语法将文件单独分割为一个chunk时该chunk的命名会走chunkFilename的配置。
- 一个是通过
optimization: {splitChunks: {chunks: 'all'}},
将依赖于node_modules中的第三方库的代码单独打包一个chunk最终输出(单独生成一个文件)的时候会走chunkFilename的配置。
- 如果不配置chunkFilename则输出的文件名都是走filename配置
- 构造目录和代码如下:

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'js/[name].js',
path: resolve(__dirname, 'build'),
publicPath: '/',
chunkFilename: 'js/[name]_chunk.js',
},
plugins: [new HtmlWebpackPlugin()],
mode: 'development'
};
----------------------------------------------
import count from './count';
console.log('index.js文件加载了~');
import('./add').then(({ default: add }) => {
console.log(add(1, 2));
});
console.log(count(3, 2));
----------------------------------------------
function add(x, y) {
return x + y;
}
export default add;
----------------------------------------------
function count(x, y) {
return x - y;
}
export default count;
- ouput的配置之library和libraryTarget:它们的作用分别是指定整个库向外暴露的变量名和变量名添加到哪个上
(window\global\commonjs
)

const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'js/[name].js',
path: resolve(__dirname, 'build'),
publicPath: '/',
chunkFilename: 'js/[name]_chunk.js',
},
plugins: [new HtmlWebpackPlugin()],
mode: 'development'
};
- ouput的不同配置写法的总结:

module的不同配置的写法
{
test: /\.css$/,
// 多个loader用use
use: ['style-loader', 'css-loader']
},
{
test: /\.js$/,
loader: 'eslint-loader'
},
- 使用loader配置时额外添加其他配置选项的写法:
{
test: /\.js$/,
exclude: /node_modules/,
include: resolve(__dirname, 'src'),
enforce: 'pre',
loader: 'eslint-loader',
options: {}
},
- 使用loader配置时配合oneOf规则使用的写法:
{
// 以下loader匹配命中一个文件后后续的loader不会在对这个文件进行检查
// 注意:不能有两个配置处理同一种类型文件
oneOf: [
{
test: /\.(jpg|png|gif)/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
outputPath: 'imgs',
esModule: false
}
},
{
test: /\.html$/,
loader: 'html-loader'
},
{
exclude: /\.(js|css|less|html|jpg|png|gif)/,
loader: 'file-loader',
options: {
outputPath: 'media'
}
}
]
},
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'js/[name].js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.js$/,
exclude: /node_modules/,
include: resolve(__dirname, 'src'),
enforce: 'pre',
loader: 'eslint-loader',
options: {}
},
{
oneOf: []
}
]
},
plugins: [new HtmlWebpackPlugin()],
mode: 'development'
};

resolve的不同配置的写法

- 场景回顾:我们在入口文件中(
这里是src/js/index.js文件
)去引入样式文件(这里是src/css/index.css文件
),正常来说,我们的代码如下:
import '../css/index.css'
- 场景回顾:如果我们的项目十分的庞大,需要引用的文件的路径十分的深层,上面的写法是比较繁琐的。我们可以在
webpack.config.js
中去使用resolve
,来定义解析模块的规则。
- 下面是使用resolve配置的
webpack.config.js
的代码:
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/[name].js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [new HtmlWebpackPlugin()],
mode: 'development',
resolve: {
alias: {
$css: resolve(__dirname, 'src/css')
},
extensions: ['.js', '.json', '.jsx', '.css'],
modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
}
};
- 在
webpack.config.js
中配置resolve
后我们再来看我们的入口文件(src/js/index.js
)引入样式文件(src/css/index.css
)的路径写法:
-------------------------------------------------
import '$css/index';
- resolve的不同配置的写法的总结:

devServer的不同配置的写法
- 场景回顾:我们先来回顾一下之前在开发环境下对
devServer
的做过的配置,代码如下:
devServer: {
contentBase: resolve(__dirname, 'build'),
compress:true,
port:3000,
open:true
}
- 接着我们来看更多的关于
devServer
的不同配置的写法,代码如下:
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/[name].js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [new HtmlWebpackPlugin()],
mode: 'development',
devServer: {
contentBase: resolve(__dirname, 'build'),
watchContentBase: true,
watchOptions: {
ignored: /node_modules/
},
compress: true,
port: 5000,
host: 'localhost',
open: true,
hot: true,
clientLogLevel: 'none',
quiet: true,
overlay: false,
proxy: {
'/api': {
target: 'http://localhost:3000',
pathRewrite: {
'^/api': ''
}
}
}
}
};
devServer
的配置中proxy(服务器代理
)和hot(开启HMR功能
)和port(端口号
)和host(域名
)和contentBase(托管服务器的目录
)这几个选项是经常需要用到的。

optimization的不同配置的写法
- 场景回顾:我们之前在
生产环境下
使用code split来优化代码运行的性能使用过optimization的配置,代码如下:
//webpack.config.js的代码
optimization: {
splitChunks: {
chunks: 'all'
}
},
- 接着我们来研究下
optimization
配置里面的其他选项配置信息。我们先来看optimization
配置下splitChunks
里面的其他可选配置信息,代码如下:
optimization: {
splitChunks: {
chunks: 'all',
/* // 下面都是默认值可以不写
// 只有在你打算修改默认值时才需要写
// 分割的chunk最小为30kb
minSize: 30 * 1024,
maxSiza: 0,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
// 要提取的chunk最少被引用2次
minChunks: 2,
// 优先级
priority: -20,
// 如果当前要打包的模块,
// 和之前已经被提取的模块是同一个
// 就会复用,而不是重新打包模块
reuseExistingChunk: true
}
}
*/
},
}
- 我们在通过一个场景来看下
optimization
配置下runtimeChunk
里面的其他可选配置信息的作用:场景是我们在src/js/index.js文件
中通过import的方式去引入src/js/a.js文件
,代码如下:

import('./a.js').then(({ add }) => {
console.log(add(1, 2));
});
---------------------------------------------------
export function add(x, y) {
return x + y;
}
---------------------------------------------------
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin')
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/[name].[contenthash:10].js',
path: resolve(__dirname, 'build'),
chunkFilename: 'js/[name].[contenthash:10]_chunk.js'
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [new HtmlWebpackPlugin()],
mode: 'production',
optimization: {
splitChunks: {
chunks: 'all'
}
}
};
- 在上述的场景中,我们在我们在
output
里配置了chunkFilename: 'js/[name].[contenthash:10]_chunk.js'
。因为通过import语法
将文件单独分割为一个chunk时该chunk的命名会走chunkFilename
的配置。执行webpack指令后,该构建后的目录结构是:

- 我们可以在该场景中分析得到:因为
src/js/index.js引入了a.js
,所以a.js文件依赖于index.js文件
,执行wepack之后打包构建后的js/main.[contenthash:10].js文件记录着js/a.[contenthash:10].js文件
的contenthash值,在只修改js/a.[contenthash:10].js文件
的情况下,会导致js/main.[contenthash:10].js文件
也跟着改变。这种问题我们是可以解决的。
- 该场景的解决思路:我们可以将打包构建后的
js/main.[contenthash:10].js
文件记录着的js/a.[contenthash:10].js文件
中的contenthash值给单独提取出来。这样子js/main.[contenthash:10].js
文件就不去记录着js/a.[contenthash:10].js文件
的contenthash值。现在js/a.[contenthash:10].js文件
的内容改变后就不会影响到js/main.[contenthash:10].js
文件了
- 该场景的解决实现:使用
optimization
配置下的runtimeChunk
,代码如下:
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin')
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/[name].[contenthash:10].js',
path: resolve(__dirname, 'build'),
chunkFilename: 'js/[name].[contenthash:10]_chunk.js'
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [new HtmlWebpackPlugin()],
mode: 'production',
optimization: {
splitChunks: {
chunks: 'all'
},
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`
}
}
};
- 场景总结:当
js/a.[contenthash:10].js文件
改变时,我们重新执行webpack指令后,只有js/a.[contenthash:10].js文件
和runtime文件
发生改变了,js/main.[contenthash:10].js文件
是不会改变的。

- 我们最后在看下
optimization配置
下的minimizer
配置(这里用到了'terser-webpack-plugin'即需要cnpm i 'terser-webpack-plugin' -D
)。代码如下:(生产环境)
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const TerserWebpackPlugin = require('terser-webpack-plugin')
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/[name].[contenthash:10].js',
path: resolve(__dirname, 'build'),
chunkFilename: 'js/[name].[contenthash:10]_chunk.js'
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [new HtmlWebpackPlugin()],
mode: 'production',
optimization: {
splitChunks: {
chunks: 'all'
},
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`
},
minimizer: [
new TerserWebpackPlugin({
cache: true,
parallel: true,
sourceMap: true
})
]
}
};
- 现在生产环境的压缩方案:压缩js和css请使用
'terser-webpack-plugin'
,尽量不要在使用UglifyJsPlugin
了。
- optimization的不同配置的写法的总结:

- 该项目的package.json文件的代码内容为:
{
"name": "webpack_code",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.8.4",
"@babel/polyfill": "^7.8.3",
"@babel/preset-env": "^7.8.4",
"add-asset-html-webpack-plugin": "^3.1.3",
"babel": "^6.23.0",
"babel-loader": "^8.0.6",
"core-js": "^3.6.4",
"css-loader": "^3.4.2",
"eslint": "^6.8.0",
"eslint-config-airbnb-base": "^14.0.0",
"eslint-loader": "^3.0.3",
"eslint-plugin-import": "^2.20.1",
"file-loader": "^5.0.2",
"html-loader": "^0.5.5",
"html-webpack-plugin": "^3.2.0",
"less": "^3.11.1",
"less-loader": "^5.0.0",
"mini-css-extract-plugin": "^0.9.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"postcss-loader": "^3.0.0",
"postcss-preset-env": "^6.7.0",
"style-loader": "^1.1.3",
"terser-webpack-plugin": "^2.3.5",
"thread-loader": "^2.1.3",
"url-loader": "^3.0.0",
"webpack": "^4.41.6",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.3",
"workbox-webpack-plugin": "^5.0.0"
},
"dependencies": {
"jquery": "^3.4.1"
},
"browserslist": {
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production": [
">0.2%",
"not dead",
"not op_mini all"
]
},
"eslintConfig": {
"extends": "airbnb-base",
"env": {
"browser": true
}
},
"sideEffects": [
"*.css"
]
}