一基础概念和配置
1.1 Entry 入口
入口配置有如下四种情况:
(1) 单入口单文件,entry是string类型。entry: './src/index.js'打包形成一个chunk。 输出一个bundle文件。此时chunk的名称默认是 main
(2) 单入口多文件,entry是array类型。entry: ['./src/index.js', './src/count.js']所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件。
(3)多入口单文件,entry是object类型。entry: { index: './src/index.js', add: './src/add.js'}有几个入口文件就形成几个chunk,输出几个bundle文件此时chunk的名称是 key
(4)多入口多文件,entry是object类型。
entry: {
// 所有入口文件最终只会形成一个chunk, 输出出去只有一个bundle文件。
index: ['./src/index.js', './src/count.js'],
// 形成一个chunk,输出一个bundle文件。
add: './src/add.js'
},
1.2 output 出口
output: {
// 文件名称(目录/指定名称)
filename: 'js/[name].js',
// 输出文件目录(将来所有资源输出的公共目录)resolve指向根目录
path: resolve(__dirname, 'build'),
// 所有资源引入公共路径前缀 --> 'imgs/a.jpg' --> '/imgs/a.jpg',在文件打包后文件引入时会在前面添加“/”
publicPath: '/',
chunkFilename: 'js/[name]_chunk.js', // 非入口 chunk 的名称,在文件中用impout()方法引入
// library: '[name]', // 整个库向外暴露的变量名
// libraryTarget: 'window' // 变量名添加到哪个上 browser
// libraryTarget: 'global' // 变量名添加到哪个上 node
// libraryTarget: 'commonjs'
}
1.3 loader
单个loader
module: {
rules: [
{
test: /\.js$/,
// 排除node_modules下的js文件
exclude: /node_modules/,
// 只检查 src 下的js文件
include: resolve(__dirname, 'src'),
// 优先执行
enforce: 'pre',
// 延后执行
// enforce: 'post',
// 单个loader用loader
loader: 'eslint-loader',
//修改配置
options: {}
},
{
// 以下配置只会生效一个,当文件匹配成功后就不会匹配后边的loader,如果一个js文件要匹配两个配置,可以将一个写在如上方
oneOf: []
}
]
}
多个loader,默认配置
module: {
rules: [
// loader的配置
{ test: /\.css$/,
// 多个loader用use
use: ['style-loader', 'css-loader']
}
]
}
多个loader,额外配置
{
test: /\.css$/,
//use是一个数组,其中每一项可以是字符串也可以是对象。
use: [
'css-loader',
{loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
// postcss 的插件
require('postcss-preset-env')()
]
}
}
]
}
1.4 resolve 解析模块的规则
// 解析模块的规则
resolve: {
// 配置解析模块路径别名: 优点简写路径 缺点路径没有提示
alias: {
$css: resolve(__dirname, 'src/css')
},
// 配置省略文件路径的后缀名,如果这样写在同一个路径下不能有同名的js和css文件。
extensions: ['.js', '.json', '.jsx', '.css'],
// 告诉 webpack 解析模块是去找哪个目录
modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
}
1.5 使⽤externals优化cdn静态资源(例如jquery)
在HTML文件引入jquer文件
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
在webpack.config.js文件中
module.exports = {
//...
externals: {
//jquery通过script引⼊之后,全局中即有了 jQuery 变量
'jquery': 'jQuery'
}
}
1.6 使⽤静态资源路径publicPath(CDN)
##webpack.config.js
output:{
publicPath: '//cdnURL.com', //指定存放JS⽂件的CDN地址
}
1.7 devserver
devServer: {
// 运行代码的目录
contentBase: resolve(__dirname, 'build'),
// 监视 contentBase 目录下的所有文件,一旦文件变化就会 reload
watchContentBase: true,
watchOptions: {
// 忽略文件
ignored: /node_modules/
},
// 启动gzip压缩
compress: true,
// 端口号
port: 5000,
// 域名
host: 'localhost',
// 自动打开浏览器
open: true,
// 开启HMR功能
hot: true,
// 不要显示启动服务器日志信息
clientLogLevel: 'none',
// 除了一些基本启动信息以外,其他内容都不要显示
quiet: true,
// 如果出错了,不要全屏提示~
overlay: false,
// 服务器代理 --> 解决开发环境跨域问题
proxy: {
// 一旦devServer(5000)服务器接受到 /api/xxx 的请求,就会把请求转发到另外一个服务器(3000)
'/api': {
target: 'http://localhost:3000',
// 发送请求时,请求路径重写:将 /api/xxx --> /xxx (去掉/api)
pathRewrite: {
'^/api': ''
}
}
}
}
二、 开发环境配置
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
// loader的配置
{
// 处理less资源
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
},
{
// 处理css资源
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
// 处理图片资源
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
name: '[hash:10].[ext]',
// 关闭es6模块化
esModule: false,
outputPath: 'imgs'
}
},
{
// 处理html中img资源
test: /\.html$/,
loader: 'html-loader'
},
{
// 处理其他资源
exclude: /\.(html|js|css|less|jpg|png|gif)/,
loader: 'file-loader',
options: {
name: '[hash:10].[ext]',
outputPath: 'media'
}
}
]
},
plugins: [
// plugins的配置
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'development',
devServer: {
contentBase: resolve(__dirname, 'build'),
compress: true,
port: 3000,
open: true
}
};
三 生产环境配置
3.1 css文件处理
(1) 提取 css 成单独文件,下载插件npm install --save-dev mini-css-extract-plugin
配置
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
//省略出入口和模式配置
module: {
rules: [
{
test: /\.css$/,
use: [
// 创建style标签,将样式放入
// 'style-loader',
// 这个loader取代style-loader。作用:提取js中的css成单独文件
MiniCssExtractPlugin.loader,
// 将css文件整合到js文件中
'css-loader'
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new MiniCssExtractPlugin({
// 对输出的css文件进行重命名
filename: 'css/built.css'
})
],
};
(2) css 兼容性处理 ,下载 loader npm install --save-dev postcss-loader postcss-preset-env
配置
//简化代码只写了css的loader其他配置不变。
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
// 使用loader的默认配置
// 'postcss-loader',
// 修改loader的配置
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [
// postcss的插件
require('postcss-preset-env')()
]
}
}
]
}
修改 package.json
"browserslist": {
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
"production": [
">0.2%",
"not dead",
"not op_mini all"
]
}
(3) 压缩 css ,下载 插件 npm install --save-dev optimize-css-assets-webpack-plugin
配置
//简化代码只写了改变的
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = {
plugins: [
// 压缩 css
new OptimizeCssAssetsWebpackPlugin()
],
};
3.2 js处理
(1)js语法检查 下载安装包npm install --save-dev eslint-loader eslint eslint-config-airbnb-base eslint-plugin-import
配置
////简化代码只写了改变的js loader配置
/*
语法检查: eslint-loader eslint
注意: 只检查自己写的源代码, 第三方的库是不用检查的
设置检查规则:
package.json 中 eslintConfig 中设置~
"eslintConfig": {"extends": "airbnb-base"}
airbnb --> eslint-config-airbnb-base eslint-plugin-import eslint
*/
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint-loader',
options: {
// 自动修复 eslint 的错误
fix: true
}
}
配置 package.json
"eslintConfig": {
"extends": "airbnb-base",
"env": {
"browser": true
}
}
(2)js 兼容性处理 下载安装包npm install --save-dev babel-loader @babel/core @babel/preset-env @babel/polyfill core-js
配置
//简化代码只写了改变的js loader配置
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'eslint-loader',
options: {
// 预设:指示babel做怎么样的兼容性处理
presets: [
[
'@babel/preset-env',
{
// 按需加载
useBuiltIns: 'usage',
// 指定core-js版本
corejs: {
version: 3
},
// 指定兼容性做到哪个版本浏览器
targets: {
chrome: '60',
firefox: '60',
ie: '9',
safari: '10',
edge: '17'
}
}
]
]
}
}
3.3 html
//简化代码只写了改变的html plugins配置
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
// 压缩html代码
minify: {
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComments: true
}
})
]
3.4 生产环境配置
const { resolve } = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 定义nodejs环境变量:决定使用browserslist的哪个环境
process.env.NODE_ENV = 'production';
// 复用loader
const commonCssLoader = [
MiniCssExtractPlugin.loader,
'css-loader',
{
// 还需要在package.json中定义browserslist
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: () => [require('postcss-preset-env')()]
}
}
];
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.css$/,
use: [...commonCssLoader]
},
{
test: /\.less$/,
use: [...commonCssLoader, 'less-loader']
},
/*
正常来讲,一个文件只能被一个loader处理。
当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序:
先执行eslint 在执行babel
*/
{
// 在package.json中eslintConfig --> airbnb
test: /\.js$/,
exclude: /node_modules/,
// 优先执行
enforce: 'pre',
loader: 'eslint-loader',
options: {
fix: true
}
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs: {version: 3},
targets: {
chrome: '60',
firefox: '50'
}
}
]
]
}
},
{
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'
}
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/built.css'
}),
new OptimizeCssAssetsWebpackPlugin(),
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
collapseWhitespace: true,
removeComments: true
}
})
],
mode: 'production'
};
四、优化
4.1 HMR
在devServer中开启热更新hot: true
4.2 source-map 错误提示
module.exports = {
mode: 'production',
devtool: 'eval-source-map'//开发环境用:eval-source-map;生产环境用:source-map
}
4.3 oneOf
一个文件只能被一个 loader 处理。
4.4 缓存
在loader的options配置中加入cacheDirectory: true
/*
缓存:
babel缓存
cacheDirectory: true
--> 让第二次打包构建速度更快
文件资源缓存
hash: 每次wepack构建时会生成一个唯一的hash值。
问题: 因为js和css同时使用一个hash值。
如果重新打包,会导致所有缓存失效。(可能我却只改动一个文件)
chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样
问题: js和css的hash值还是一样的
因为css是在js中被引入的,所以同属于一个chunk
contenthash: 根据文件的内容生成hash值。不同文件hash值一定不一样
--> 让代码上线运行缓存更好使用
*/
module.exports = {
module: {
rules: [
{
// 以下loader只会匹配一个
// 注意:不能有两个配置处理同一种类型文件
oneOf: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
//省略 presets 配置
// 开启babel缓存
// 第二次构建时,会读取之前的缓存
cacheDirectory: true
}
}
]
},
};
4.5 tree shaking
树摇,去除无用代码
前提:1. 必须使用ES6模块化 2. 开启production环境; 作用: 减少代码体积
在package.json中配置 "sideEffects": false 所有代码都没有副作用(都可以进行tree shaking) 问题:可能会把css / @babel/polyfill (副作用)文件干掉,所以要是使用下面这种情况。 "sideEffects": [".css", ".less"] 意思是css文件和less文件不进行树摇。
module.exports = {mode: 'production'}
4.6 code split 代码分割
(1) 多入口
entry: {
// 多入口: 有一个入口, 最终输出就有一个 bundle
index: './src/js/index.js',
test: './src/js/test.js'
},
(2) optimization 将 node_modules 中代码单独打包一个 chunk 最终输出
module.exports = {
/*
1. 可以将node_modules中代码单独打包一个chunk最终输出
2. 自动分析多入口chunk中,有没有公共的文件。如果有会打包成单独一个chunk
*/
optimization: {
splitChunks: {
chunks: 'all'
}
},
mode: 'production'
};
(3) js文件中使用import()方法引入模块,打包形成单独文件。
4.7 懒加载预加载
在我们的js文件中使用import()方法引入模块,使用时才加载。预加载:在懒加载的前提下加上webpackPrefetch: true
console.log('index.js文件被加载了~');
// import { mul } from './test';
document.getElementById('btn').onclick = function() {
// 懒加载~:当文件需要使用时才加载~
// 预加载 prefetch:会在使用之前,提前加载js文件
// 正常加载可以认为是并行加载(同一时间加载多个文件)
// 预加载 prefetch:等其他资源加载完毕,浏览器空闲了,再偷偷加载资源
import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({ mul }) => {
console.log(mul(4, 5));
});
};
4.8 pwa
渐进式网络开发应用程序(离线可访问)
4.9 多线程打包
4.10 externals 拒绝打包
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
mode: 'production',
externals: {
// 拒绝jQuery被打包进来
jquery: 'jQuery'
}
};