1. webpack.config.js 基本配置
webpack.config.js 都是使用 commentJS
const path = require('path');
const { resolve, join } = path
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/index.js', // 入口文件
/*
bulid 输出的位置
*/
output: {
publicPath: './', // 输出根路径
path: resolve(__dirname, 'dist'), // 输出的文件夹(当前路径下的 dist 目录)
filename: 'built.js' // 打包后的文件名
},
module: {
rules: [
/*
css 文件使用 css-loader 将 css 文件编译进 js 中,再使用 style-loader 将样式使用 style 包起来,再打包进html 中
*/
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
{ test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },
/*
只会解析 css 中的 url
*/
{ test: /\.(jpg|png|jpeg|gif)$/, loader: 'url-loader', options: {
limit: 1*1024,
name: '[hash:10].[ext]',
outputPath: 'imgs', // build 生产输出位置(文件夹)
esModule: false, // 是否启用 es6 module 编译
}},
/*
exclude: 若文件不是配置中的文件之一,就会走此 loader
*/
{ exclude: /\.(jpg|png|jpeg|gif|css|less|html|js)$/, loader: 'file-loader', options: {
outputPath: 'public'
} },
]
},
/*
插件
*/
plugins: [
/*
引入此插件会在打包时自动生成 html 模板文件,并引入 js 文件
*/
new HtmlWebpackPlugin({
template: './src/index.html'
}),
],
// devServer 开发自动化构建
// 下载 webpack-dev-server 后需要, npx webpack serve / npx webpack-dev-server 才可以运行项目
devServer: {
contentBase: resolve(__dirname, 'dist'), // 项目构建后的路径
port: 3000,
compress: true, // 启动 gzip 压缩
open: true, // 是否自动打开浏览器
},
mode: 'development', // 开发模式, 开发模式 development 生产模式 production 模式(打包方式有所不同, 生产模式会启用更多生产插件)
}
2. CSS 文件处理
- 提取 css 为单独文件,目前的配置会直接使用 style-loader 将样式插入到 html 中
- css 兼容性处理
- 压缩 css 文件
/*
· 提取 css 为单独文件
1. npm i mini-css-extract-plugin -D
2. 引入 const MiniCssExtractPlugin = require(mini-css-extract-plugin)
3. use 中配置(配置成功后会将 css 输出至默认 main.js 的文件中)
4. plugin 中修改文件路径\名
*/
// 注意: 需要配置 publicPath
const MiniCssExtractPlugin = require(mini-css-extract-plugin)
module.exports = {
module: {
rules: {
{
test: /\.css$/,
use: [ MiniCssExtractPlugin.loader, 'css-loader' ]
// 将style-loader 替换成 MiniCssExtractPlugin.loader
// 代表将 css代码 提取至 main.css 文件中
}
}
}
plugins: [
new MiniCssExtractPlugin({
filename: 'css/build.css' // 默认路径\文件名
})
]
}
/*
· css 兼容性处理
1. npm i postcss-loader postcss-preset-env -D
2. use 中配置 postcss
3. pakeage.json 文件中配置 browserslist
4. 设置开发环境变量,(因为 node 默认是 production 环境,开发时需要手动设置环境变量为 dev 环境)
*/
// webpack.config.js
process.env.NODE_ENV = 'development' // 设置环境变量为 开发环境
module.exports = {
module: {
rules: {
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
/*
使用 loader 默认配置
postcss-loader
*/
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
postcssOptions: {
plugins: () => [ require('postcss-preset-env') ]
}
}
}
]
}
}
}
plugins: [
new MiniCssExtractPlugin({
filename: 'css/build.css' // 默认路径\文件名
})
]
}
// package.json
browserslist: {
devlopment: [
// 兼容最近的一个浏览器版本
'last 1 firefox version',
'last 1 chrome version',
'last 1 safari version'
],
production: [
'>0.2%', // 大于 99.8% 的浏览器
'not dead', // 不要已经不用的浏览器
]
}
/*
· css 文件压缩
注意: webpack5 使用 css-minimizer-webpack-plugin 压缩样式文件
1. npm i optimize-css-assets-webpack-plugin -D
2. 引入: const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
3. plugins 中配置 new OptimizeCssAssetsWebpackPlugin
*/
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = {
plugins: [
new OptimizeCssAssetsWebpackPlugin() // 启用压缩
]
}
3. 文件压缩
生产环境下,会默认加载UglifyJsPlugin 插件,会自动压缩 js 文件
配置 html 文件压缩,配置 minify
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
minify: { // 压缩配置
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComments: true
}
})
]
4. 性能优化
1. HMR(热模块替换) - - 加载优化
开启 HMR (热模块替换)
配置 HMR 后会按需打包,修改单一模块时,只会重新打包这个模块。不会再重复打包所有模块
devServer: {
contentBase: resove(__dirname ,'bulid'),
compress: true,
port: 3000,
open: true,
// 开启 HMR
hot: true,
}
注意: js, html 文件默认不能使用 HMR, 并且开启 HMR 后会导致 html 不能热更新(不过html 文件不需要热更新,因为 html 只有一个)
解决方法: 指定入口文件以及配置 js HMR 代码
// webpack.config.js
// 引入 html 文件,解决开启 HMR 后 html 不能更新得问题
entry: ['./index.js', './index.html']
// index.js (入口 js 文件)
if (module.hot) {
// module.hot 为 true 时,说明开启了 HMR 功能
module.hot.accept('./print.js', function() {
// 方法会监听 print.js 文件得变化,一旦发生变化,其他得模块不会打包构建
// 并且会执行后面得回调
print()
})
}
// 若不增加此函数,当改变文件时,则所有模块都会打包
// 注意:可以将所有得操作放在非入口文件后,再将此文件引入入口文件,修改时直接修改操作文件即可。
// 直接修改入口文件,必定会使所有文件重新打包。因为入口文件引入了所有模块,牵一发动全身。
2. souce-map - - 开发调试优化
souce-map 提供构建后代码得源代码映射
通俗的说,souce-map 可以通过映射技术,将构建后代码的抛错,映射到源代码中,增加报错定位的效率
module.exports = {
devtool: 'source-map' // 开启 source-map
/**
* source-map 类型 :
*
* 内联:1. 不会在外部生成 source-map 文件 2. 构建速度更快
* ( inline-source-map | eval-source-map )
*
* 外联:1. 在外部生成了 source-map 文件 2. 相对更慢
* ( source-map | hidden-source-map | cheap-source-map )
*/
}
注意: 生产环境不要使用 source-map
, 如果需要使用,需要使用外联的方式,因为内联的方式会让代码体积变得非常大
3. one-of - - 加载优化
webpack
的rules
中会出现一个文件被多个 rule 匹配的情况
有些文件如果只需要被一个 loader 加载时,可以使用one-of
优化,使用后,当文件被 loader 所处理时,就不会再进行下面的 rule 匹配。
const cssHandleLoader = [ 'style-loader', 'css-loader' ]
module.exports = {
module: {
rules: {
{
// 注意: 以下 loader 只匹配一个,一个loader 被匹配后,则不会往下再匹配
oneOf: [
{ test: '/\.css$/', use: [...cssHandleLoader] },
{ test: '/\.less$/', use: [...cssHandleLoader, 'less-loader'] }
]
},
// 外部的其他 loader 不会受到 oneOf 配置影响
// 被 oneOf 配置处理的文件也会匹配下面的配置
{ test: '/\.js$/', use [......] }
}
}
}
4. 缓存 - - 加载优化
- 开启 babel 缓存,babel 会对文件进行大量的处理,开启缓存可以避免重复的处理同一个文件,只有当文件变化时,才会重新处理
- 浏览器文件资源缓存,浏览器每次会根据文件名称重新载入文件,每次打包后,文件hash地址就会变化,使得所有缓存失效,导致重新加载资源。
这时就需要使用到contentHash
来命名文件即可。contentHash
会根据文件内容生产 hash 值,如果内容不变,则 hash 值也不变,所以浏览器不会重载文件
1. 开启 babel 缓存
{
test: /\.js$/,
exclude: /node_module/,
loader: 'babel-loader',
options: {
// ...
cacheDirectory: true
// 开启缓存
}
}
2. 使用 contentHash
output: {
filename: 'js/built.[contenthash:10].js'
}
plugins: [
new MiniCssExtractPlugin({
filename: 'css/built.[contenthash:10].css'
})
]
3. 注意: contenthash 已经被废弃,可以使用下面的方法
output: {
filename: 'js/built.[md5:contentHash:hex:8].js'
}
plugins: [
new MiniCssExtractPlugin({
filename: 'css/built.[md5:contentHash:hex:8].css'
})
]
5. tree shaking - - 体积以及加载优化
tree shaking
可以去除无用代码
注意 : tree shaking 必须使用 es6 模块化 以及 开启 production 环境
// 在 package.json 中配置
"sideEffects": ["*.css"] // 配置不要处理的文件
6. split - - 体积优化
在打包时,页面中若是导入了 node_modules 中的包,会直接打包进入口文件中。
如果项目为多页应用,则需要开启split
优化
开启后回将导入的包提取出来,生产单独文件,这样多文件引入时就不会造成重复打包
开启 split 会自动判断包大小, 太小则不会打包
optimization: {
splitChunks: {
chunks: 'all' // 开启 split
}
}
7. 懒加载 预加载
懒加载: 需要时再加载
预加载: 等所有模块加载完之后再加载
// ES10 模块懒加载
// entry.js
document.getElementById('btn').onclick = () => {
// 懒加载
import('./mode.js').then((obj) => {
console.log('模块中导出的对象', obj)
})
}
// 可以通过注释的方式指定懒加载的 chunk 名称
import(/* webpackChunkName: 'mode' */'./mode.js')
// 开启预加载,同上(兼容性差)
import(/* webpackChunkName: 'mode', webpackPrefetch: true */'./mode.js')
8. 多进程打包
因为开启进程也需要时间,所以只有大型项目才会用到多进程打包
进程启动需要约 600ms 时间
/**
* 1. 下载 thread-loader 包
* 2. webpack 配置
*/
{
test: /\.js$/,
exclude: /node_module/,
use: [
// 开启多进程打包
'thread-loader',
// ......
]
}
// 详细配置
use: [
{
loader: 'thread-loader',
options: {
workers: 2 // 进程数量配置
}
}
]
9. externals - - 模块编译优化
配置
externals
可以使包不被 build,但是需要手动在 html 中引入 cdn 包
externals: {
// 拒绝 JQ 被 build
// key 为 npm包名, value 为 引入时的变量名
jquery: 'jQuery'
}
// 注意: 需要在 html 文件中使用 cdn 链接