webpack性能优化
主要包括以下几点
* 开发环境性能优化
* 生产环境性能优化
* 优化打包构建速度
* 优化代码调试
* 优化打包构建速度
* 优化代码运行的性能
开发环境性能优化(HMR)
将webpack第二篇文章中构建好的开发环境配置工程完整复制一份,并重命名。 在终端输入npx webpack serve 启动热更新服务。然后我们简单修改index.less内的样式,会发现如下图所示发生,即只单独更新了样式模块。 此时在简单修改index.js和index.html文件,则不会仅仅更新js或者html模块,而是整个工程进行更新。在config.js内代码中有注释说明。
/**
* 启动devServer命令:
* npx webpack serve
* 开发环境的性能优化(HMR):hot module replacement热模块替换/模块热替换
作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块)极大提升构建速度.
样式文件:可以使用HMR功能:因为style-loader内部实现了~
js文件:默认不能使用HMR功能,我们可以在入口文件中引入其他js文件,实现修改其他js文件,达成HMR功能
html文件:默认不能使用HMR功能.同时会导致问题: html文件不能热更新了~,在入口entry属性中配置上html文件即可解决
*/
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: ['./src/index.js', './src/index.html'],
output: {
filename: '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,
// 关闭ES6模块化
esMoudle: false
},
type: 'javascript/auto'
},
{
//处理html中img资源
test: /\.html$/,
loader: 'html-loader',
options: {
esModule: false,
}
},
{
//处理其他资源
exclude: /\.(html|js|css|less|jpg|png|gif)$/,
loader: 'file-loader',
options: {
esModule: false,
},
type: 'javascript/auto'
}
]
},
plugins: [
// plugins的配置
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
devServer: {
compress: true,
port: 3000,
open: true,
// 开启hmr功能,webpack5默认开启了
// hot: true
},
mode: 'development'
}
入口文件index.js不能使用HMR功能,那么在入口文件中,引入其他的js则可以使用,不过要修改代码。如下是index.js
import '../src/iconfont';
import '../src/index.less';
import print from './print';
console.log('index.js文件被加载了');
function add(x, y) {
return x + y;
}
console.log(add(1, 8));
if (module.hot) {
// 一旦 module.hot为true,说明开启了HMR功能。--> 让HMR功能代码生效
module.hot.accept('../src/print.js', function () {
//方法会监听 print.js文件的变化,一旦发生变化,其他模块不会重新打包构建。
//会执行后面的回调函数
print();
})
}
我们新建的另一个print.js
function print() {
const content = 'print.js被加载了';
console.log(content);
}
export default print;
此时修改print.js文件,则会发生HMR热更新模块功能,但是,注意,入口文件index.js一修改是只能整个工程全部更新。
source_map
source-map:一种提供源代码到构建后(打包)代码映射技术(如果构建后(打包)代码出错了,通过映射可以追踪源代码错误)。 将上一小节工程文件复制,并重命名。修改config.js代码
/**
* 启动devServer命令:
* npx webpack serve
* 开发环境的性能优化(HMR):hot module replacement热模块替换/模块热替换
作用:一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块)极大提升构建速度.
样式文件:可以使用HMR功能:因为style-loader内部实现了~
js文件:默认不能使用HMR功能
html文件:默认不能使用HMR功能.同时会导致问题: html文件不能热更新了~,在入口entry属性中配置上html文件即可解决
*/
const { resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: ['./src/index.js', './src/index.html'],
output: {
filename: '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,
// 关闭ES6模块化
esMoudle: false
},
type: 'javascript/auto'
},
{
//处理html中img资源
test: /\.html$/,
loader: 'html-loader',
options: {
esModule: false,
}
},
{
//处理其他资源
exclude: /\.(html|js|css|less|jpg|png|gif)$/,
loader: 'file-loader',
options: {
esModule: false,
},
type: 'javascript/auto'
}
]
},
plugins: [
// plugins的配置
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
devServer: {
compress: true,
port: 3000,
open: true,
// 开启hmr功能,webpack5默认开启了
// hot: true
},
// source-map:一种提供源代码到构建后(打包)代码映射技术(如果构建后(打包)代码出错了,
//通过映射可以追踪源代码错误)。
// 开启source-map,打包后会生成一个.js.map文件
devtool: 'source-map',
// 测试不同种类的source-map
// devtool: 'eval-source-map',
mode: 'development'
}
/*
有如下几个组合source-map
[inline-|hidden-|eval-] [nosources-] [cheap-[ module-]] source-map
inline-source-map:把原本单独生成的built.js.map文件,整体内嵌到了built.js中尾部去了
hidden-source-map:单独生成一个built.js.map文件,
eval-source-map:内嵌到built.js中,但是是以分段的形式,从头至尾,遍布于整个.js文件中
nosources-source-map:单独生成一个built.js.map文件
cheap-source-map:单独生成一个built.js.map文件
cheap-module-source-map:单独生成一个built.js.map文件。
*/
输入npm run dev 进行打包。
然后输入npx webpack serve 开启热更新。效果如下 那么我们为了验证代码出错时是否有映射关系。把print.js中代码人为制造一个错误。
function print() {
// 人为制造一个log的错误
const content = 'print.js被加载了',,
console.log(content)
}
export default print;
自动热更新后,网页报错的提示。 小结,不同的sourece-map对比
oneOF
将webpack第四篇中的生产环境配置工程复制,并重命名oneOf。oneOf功能是,使每个类型的文件,只匹配到一个适用的loader即可,不会把代码中写的所有loader挨个匹配一遍,优化了构建速度。在config.js代码如下,把相关的loader放入oneOf选项中。
const { resolve } = require('path');
const minicssextractplugin = require('mini-css-extract-plugin');
// css浏览器兼容性处理,默认是运行在生产环境下,若要运行于开发环境,还需做以下代码
process.env.NODE_ENV = 'development'
const cssminimizerwebpackplugin = require('css-minimizer-webpack-plugin');
const Htmlwebpackplugin = require('html-webpack-plugin');
const Eslintwebpackplugin = require('eslint-webpack-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [{
// oneOf是每一个文件,只匹配到一个loader即可,
//不像之前,每个文件都要把下列loader全部匹配一遍
// 注意不能有两个loader处理同一个类型文件
oneOf: [
{
// 检测css文件,并打包
test: /\.css$/,
use: [
minicssextractplugin.loader,
'css-loader',
{
// 对css做兼容性处理
// 还需要在package.json中定义browserslist
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [require('postcss-preset-env')()]
}
}
}
]
},
{
// 检测less文件,并打包
test: /\.less$/,
// use内代码,是从下往上的顺序执行的
use: [
minicssextractplugin.loader,
'css-loader',
{
// 还需要在package.json中定义browserslist
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [require('postcss-preset-env')()]
}
}
},
'less-loader'
]
},
// 正常来讲,一个文件只能被一个loader处理,比如一个index.js文件。
//当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序
// {
// //在package.json中定义eslintConfig --> airbnb
// // js语法检查,webpack4写法
// test: /\.js$/,
// exclude: /node_modules/,
// // 优先执行eslint-loader,对js文件进行处理
// enforce: 'pre',
// loader: 'eslint-loader',
// options: {
// // 自动修复语法检查的报错
// fix: true
// }
// },
{
// js兼容性处理
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,
outputPath: 'imgs',
esModule: false
},
type: 'javascript/auto'
},
{
// 处理html中的图片文件
test: /\.html$/,
loader: 'html-loader',
options: {
esModule: false,
}
},
{
// 处理其他文件,如字体图标等
exclude: /\.(js|css|less|html|jpg|png|gif)$/,
loader: 'file-loader',
options: {
outputPath: 'media',
esModule: false,
},
type: 'javascript/auto'
}
]
}
]
},
plugins: [
new minicssextractplugin({
// 打包提取成单独文件
filename: 'css/built.css'
}),
new cssminimizerwebpackplugin(
// 压缩css文件
),
new Htmlwebpackplugin({
template: './src/index.html',
// 压缩html
minify: {
collapseWhitespace: true,
removeComments: true
}
}),
new Eslintwebpackplugin({
// 使用默认配置
fix: true
})
],
// 生产环境下,js自动压缩
mode: 'production'
}
生产环境的缓存配置
将上一个oneOf工程复制,并重命名。首先是对babel处理的js兼容性等文件进行缓存,比如处理后,缓存了100个js文件,那么以后有1个js文件发生了更改,那么就继续缓存1个js文件就可以,不必再重新全部缓存100个js文件。 修改config.js代码,开启babel缓存。
const { resolve } = require('path');
const minicssextractplugin = require('mini-css-extract-plugin');
// css浏览器兼容性处理,默认是运行在生产环境下,若要运行于开发环境,还需做以下代码
process.env.NODE_ENV = 'development'
const cssminimizerwebpackplugin = require('css-minimizer-webpack-plugin');
const Htmlwebpackplugin = require('html-webpack-plugin');
const Eslintwebpackplugin = require('eslint-webpack-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'js/built.js',
path: resolve(__dirname, 'build')
},
module: {
rules: [{
// oneOf是每一个文件,只匹配到一个loader即可,
//不像之前,每个文件都要把下列loader全部匹配一遍
// 注意不能有两个loader处理同一个类型文件
oneOf: [
{
// 检测css文件,并打包
test: /\.css$/,
use: [
minicssextractplugin.loader,
'css-loader',
{
// 对css做兼容性处理
// 还需要在package.json中定义browserslist
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [require('postcss-preset-env')()]
}
}
}
]
},
{
// 检测less文件,并打包
test: /\.less$/,
// use内代码,是从下往上的顺序执行的
use: [
minicssextractplugin.loader,
'css-loader',
{
// 还需要在package.json中定义browserslist
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [require('postcss-preset-env')()]
}
}
},
'less-loader'
]
},
// 正常来讲,一个文件只能被一个loader处理,比如一个index.js文件。
//当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序
// {
// //在package.json中定义eslintConfig --> airbnb
// // js语法检查,webpack4写法
// test: /\.js$/,
// exclude: /node_modules/,
// // 优先执行eslint-loader,对js文件进行处理
// enforce: 'pre',
// loader: 'eslint-loader',
// options: {
// // 自动修复语法检查的报错
// fix: true
// }
// },
{
// js兼容性处理
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env', {
useBuiltIns: 'usage',
corejs:
{
version: 3
},
targets: {
// 浏览器兼容的版本
chrome: '60',
firefox: '50'
}
}
]
],
// 开启babel缓存
cacheDirectory: true,
}
},
{
//对图片进行打包处理
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
outputPath: 'imgs',
esModule: false
},
type: 'javascript/auto'
},
{
// 处理html中的图片文件
test: /\.html$/,
loader: 'html-loader',
options: {
esModule: false,
}
},
{
// 处理其他文件,如字体图标等
exclude: /\.(js|css|less|html|jpg|png|gif)$/,
loader: 'file-loader',
options: {
outputPath: 'media',
esModule: false,
},
type: 'javascript/auto'
}
]
}
]
},
plugins: [
new minicssextractplugin({
// 打包提取成单独文件
filename: 'css/built.css'
}),
new cssminimizerwebpackplugin(
// 压缩css文件
),
new Htmlwebpackplugin({
template: './src/index.html',
// 压缩html
minify: {
collapseWhitespace: true,
removeComments: true
}
}),
new Eslintwebpackplugin({
// 使用默认配置
fix: true
})
],
// 生产环境下,js自动压缩
mode: 'production'
}
与config.js同目录下,新建server.js搭建一个服务器。代码如下
/*
服务器代码
安装nodemon包:npm i nodemon -g
启动服务器指令:nodemon server.js或者node server.js
*/
const express = require('express');
const app = express();
app.use(express.static('build', {
maxAge: 1000 * 3600
}));
app.listen(3000);
输入npm i express -D 下载express,然后输入node server.js 运行该服务器。 打开网址http://localhost:3000/ ,按F12查看网页后台响应。可以看到开启了缓存,缓存时间3600秒。 此时点击刷新网页,发现在加载这些内容时,时间是0毫秒,直接从缓存中读取。 但是,此时我们修改了index.js内容,然后重新打包一次,服务器一直不关闭,点刷新网页,发现,网页的内容不变。这说明强制缓存期间,点刷新不会重新访问新资源。所以一个方法就是,增加一个哈希值,每次更改文件后,文件名字就会变,说明我们的文件变更了。config.js内修改代码,给某些文件名增加哈希值。
const { resolve } = require('path');
const minicssextractplugin = require('mini-css-extract-plugin');
// css浏览器兼容性处理,默认是运行在生产环境下,若要运行于开发环境,还需做以下代码
process.env.NODE_ENV = 'development'
const cssminimizerwebpackplugin = require('css-minimizer-webpack-plugin');
const Htmlwebpackplugin = require('html-webpack-plugin');
const Eslintwebpackplugin = require('eslint-webpack-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
// 给每次打包生成的文件名加上哈希值,表明唯一性,
// 开启babel缓存后,服务器能及时识别文件有更改
// hash值取10位
filename: 'js/built.[hash:10].js',
path: resolve(__dirname, 'build')
},
module: {
rules: [{
// oneOf是每一个文件,只匹配到一个loader即可,
//不像之前,每个文件都要把下列loader全部匹配一遍
// 注意不能有两个loader处理同一个类型文件
oneOf: [
{
// 检测css文件,并打包
test: /\.css$/,
use: [
minicssextractplugin.loader,
'css-loader',
{
// 对css做兼容性处理
// 还需要在package.json中定义browserslist
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [require('postcss-preset-env')()]
}
}
}
]
},
{
// 检测less文件,并打包
test: /\.less$/,
// use内代码,是从下往上的顺序执行的
use: [
minicssextractplugin.loader,
'css-loader',
{
// 还需要在package.json中定义browserslist
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [require('postcss-preset-env')()]
}
}
},
'less-loader'
]
},
// 正常来讲,一个文件只能被一个loader处理,比如一个index.js文件。
//当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序
// {
// //在package.json中定义eslintConfig --> airbnb
// // js语法检查,webpack4写法
// test: /\.js$/,
// exclude: /node_modules/,
// // 优先执行eslint-loader,对js文件进行处理
// enforce: 'pre',
// loader: 'eslint-loader',
// options: {
// // 自动修复语法检查的报错
// fix: true
// }
// },
{
// js兼容性处理
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env', {
useBuiltIns: 'usage',
corejs:
{
version: 3
},
targets: {
// 浏览器兼容的版本
chrome: '60',
firefox: '50'
}
}
]
],
// 开启babel缓存
cacheDirectory: true,
}
},
{
//对图片进行打包处理
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
outputPath: 'imgs',
esModule: false
},
type: 'javascript/auto'
},
{
// 处理html中的图片文件
test: /\.html$/,
loader: 'html-loader',
options: {
esModule: false,
}
},
{
// 处理其他文件,如字体图标等
exclude: /\.(js|css|less|html|jpg|png|gif)$/,
loader: 'file-loader',
options: {
outputPath: 'media',
esModule: false,
},
type: 'javascript/auto'
}
]
}
]
},
plugins: [
new minicssextractplugin({
// 打包提取成单独文件
filename: 'css/built.[hash:10].css'
}),
new cssminimizerwebpackplugin(
// 压缩css文件
),
new Htmlwebpackplugin({
template: './src/index.html',
// 压缩html
minify: {
collapseWhitespace: true,
removeComments: true
}
}),
new Eslintwebpackplugin({
// 使用默认配置
fix: true
})
],
// 生产环境下,js自动压缩
mode: 'production'
}
此时重新打包文件,然后node server.js一直不关闭,此时点击浏览器刷新页面,发现加载的文件变化了。这就实现了开启缓存后,还能实现文件改动后发生更新。 注意:
- hash:每次wepack构建时会生成一个唯一的hash值。
- 问题:因为js和css同时使用一个hash值。
- 如果重新打包,会导致所有缓存失效。(可能我只改动了一个css文件,js文件名字也会发生变化,因为哈希值每次打包都变化)
- chunkhash:根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样。
- 问题:js和css的hash值还是一样的,因为css是在js中被引入的,所以同属于一个chunk
在config.js中将hash值更改为chunkhash。
const { resolve } = require('path');
const minicssextractplugin = require('mini-css-extract-plugin');
// css浏览器兼容性处理,默认是运行在生产环境下,若要运行于开发环境,还需做以下代码
process.env.NODE_ENV = 'development'
const cssminimizerwebpackplugin = require('css-minimizer-webpack-plugin');
const Htmlwebpackplugin = require('html-webpack-plugin');
const Eslintwebpackplugin = require('eslint-webpack-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
// 给每次打包生成的文件名加上哈希值,表明唯一性,
// 开启babel缓存后,服务器能及时识别文件有更改
// hash值取10位
filename: 'js/built.[chunkhash:10].js',
path: resolve(__dirname, 'build')
},
module: {
rules: [{
// oneOf是每一个文件,只匹配到一个loader即可,
//不像之前,每个文件都要把下列loader全部匹配一遍
// 注意不能有两个loader处理同一个类型文件
oneOf: [
{
// 检测css文件,并打包
test: /\.css$/,
use: [
minicssextractplugin.loader,
'css-loader',
{
// 对css做兼容性处理
// 还需要在package.json中定义browserslist
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [require('postcss-preset-env')()]
}
}
}
]
},
{
// 检测less文件,并打包
test: /\.less$/,
// use内代码,是从下往上的顺序执行的
use: [
minicssextractplugin.loader,
'css-loader',
{
// 还需要在package.json中定义browserslist
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [require('postcss-preset-env')()]
}
}
},
'less-loader'
]
},
// 正常来讲,一个文件只能被一个loader处理,比如一个index.js文件。
//当一个文件要被多个loader处理,那么一定要指定loader执行的先后顺序
// {
// //在package.json中定义eslintConfig --> airbnb
// // js语法检查,webpack4写法
// test: /\.js$/,
// exclude: /node_modules/,
// // 优先执行eslint-loader,对js文件进行处理
// enforce: 'pre',
// loader: 'eslint-loader',
// options: {
// // 自动修复语法检查的报错
// fix: true
// }
// },
{
// js兼容性处理
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env', {
useBuiltIns: 'usage',
corejs:
{
version: 3
},
targets: {
// 浏览器兼容的版本
chrome: '60',
firefox: '50'
}
}
]
],
// 开启babel缓存
cacheDirectory: true,
}
},
{
//对图片进行打包处理
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024,
outputPath: 'imgs',
esModule: false
},
type: 'javascript/auto'
},
{
// 处理html中的图片文件
test: /\.html$/,
loader: 'html-loader',
options: {
esModule: false,
}
},
{
// 处理其他文件,如字体图标等
exclude: /\.(js|css|less|html|jpg|png|gif)$/,
loader: 'file-loader',
options: {
outputPath: 'media',
esModule: false,
},
type: 'javascript/auto'
}
]
}
]
},
plugins: [
new minicssextractplugin({
// 打包提取成单独文件
filename: 'css/built.[chunkhash:10].css'
}),
new cssminimizerwebpackplugin(
// 压缩css文件
),
new Htmlwebpackplugin({
template: './src/index.html',
// 压缩html
minify: {
collapseWhitespace: true,
removeComments: true
}
}),
new Eslintwebpackplugin({
// 使用默认配置
fix: true
})
],
// 生产环境下,js自动压缩
mode: 'production'
}
输入npm run build 先打包一次,然后我们改动index.js中一点内容,在打包一次,观看两次生成文件的变化。发现每次生成的css和js文件哈希值一样,是成对出现的。 所以最后使用contenthash:根据文件的内容生成hash值。不同文件的hash值一定不一样。把config.js内的chunkhash改为contenthash。然后重新打包生成一次。
此时修改index.js代码,在打包一次。发现因为改动了index.js文件,所以重新打包了一个js,而css没变 那么此时先刷新一次网页,加载一下,然后在修改下index.js内容,然后打包,最后再点击刷新,可以看到网页中,前后对比发现.css文件是来自于内存缓存,.js文件则是重新加载了一次。
|