webpack优化
HMR 热模块替换/模块热替换
作用:一个模块发生变化,只会更新这个模块(而不是打包所有)提高速度
样式文件:可以使用HRM功能,因为style-loader内部实现了
js文件:默认没有HRM
html文件:默认没有HRM,同时html文件不能热更新(本地代码不能重新编译)了<就一个文件,不需要做HMR功能>
- 解决 :修改entry入口,讲html文件引入
const {resolve}=require('path');
const HtmlWebpackPlugin=require('html-webpack-plugin');
module.export={
entry:['./src/index.js','./src/index.html'],
output:{
filename:'built.js',
path:resolve(_dirname,'build')
},
module:{
rules:[
module:{
rules:[
//详细loader配置
{
//处理css资源
test:/\.css$/,
use:[
'style-loader',//(是一个包)
'css-loader',//(是一个包)
]
},
{
//处理less资源
test:/\.less$/,
use:[
'style-loader',//(是一个包)
'css-loader',//(是一个包)
'less-loader'
]
},
{
//处理图片资源
test:/\.(jpg|png|gif)$/,
loader:'url-loader'
options:[
limit:8*1024
name:'[hash:10].[ext]',
//关闭es6模块化
esModule:false,
outputPath:'imgs'//改变输出路径
]
},
{
test:/\.html$/,
//处理html文件的img图片
loader:'html-loader'
name:'[hash:10].[ext]'
},
{
//打包其他资源
exclude:/\.(css|js|html|less|jpg|png|gif)$/,
loader:'file-loader',
//修改名字
options:{
name:'[hash:10],[ext]'
},
]
},
//plugin的配置
plugins:[
new HtmlWebpackPlugin({
template:'./src/index.html'
})
],
//模式
mode:'development'
devServer:{
//项目构建后的路径
contentBase:resolve(_dirname,'build'),
//启动gzip压缩
compress:true,
//端口号
port:3000,
//自动打开浏览器
open:true,
//开启HMR功能
//当修改了webpack配置,一定要重启webpack服务
hot:true
}
}
js的热模块
在index.js文件中写
针对非入口文件的js
import print from './print';
if(module.hot){
//一旦module.hot为true,说明开启了HMR功能 -->让HMR功能代码生效
module.hot.accept('./print.js',function(){
//方法监听print.js文件变化,一旦发生变化,其他模块不会重新打包构建,会执行后面的回调函数
print();
}
开发环境下调试代码(source-map)
一种提供源代码到构建后代码到映射技术(如果构建后代码出错了,通过映射可以追踪到源代码的目录)
[inline-|hidden-|eval-][nosources-][cheap-[module]]source-map
- source-map:外联
提示错误代码的准确信息和源代码错误位置 - inline-source-map:内联:只生成一个source-map
提示错误代码的准确信息和源代码错误位置 - hidden-source-map:外联
提示错误代码错误原因但是没有错误位置,不能追踪到源代码错误 - eval-source-map:内联:每个文件都生成对应source-map,在eval函数中
提示错误代码的准确信息和源代码错误位置 - mosources-source-map:外联
提示错误代码的准确信息,没有源代码错误位置 - cheap-source-map:外联
提示错误代码的准确信息和源代码错误位置,只能精确到行 - cheap-module-source-map:外联
提示错误代码的准确信息和源代码错误位置
1.生产环境:速度快,调试友好 (内联会让代码体积大,所以排除)source-map
2.开发环境:源代码隐藏?调式友好? eval-source-map
内联外联区别 1.外部生成了文件,内联没有2.内联构建速度更快
const {resolve}=require('path');
const HtmlWebpackPlugin=require('html-webpack-plugin');
module.export={
entry:['./src/index.js','./src/index.html'],
output:{
filename:'built.js',
path:resolve(_dirname,'build')
},
module:{
rules:[
module:{
rules:[
//详细loader配置
{
//处理css资源
test:/\.css$/,
use:[
'style-loader',//(是一个包)
'css-loader',//(是一个包)
]
},
{
//处理less资源
test:/\.less$/,
use:[
'style-loader',//(是一个包)
'css-loader',//(是一个包)
'less-loader'
]
},
{
//处理图片资源
test:/\.(jpg|png|gif)$/,
loader:'url-loader'
options:[
limit:8*1024
name:'[hash:10].[ext]',
//关闭es6模块化
esModule:false,
outputPath:'imgs'//改变输出路径
]
},
{
test:/\.html$/,
//处理html文件的img图片
loader:'html-loader'
name:'[hash:10].[ext]'
},
{
//打包其他资源
exclude:/\.(css|js|html|less|jpg|png|gif)$/,
loader:'file-loader',
//修改名字
options:{
name:'[hash:10],[ext]'
},
]
},
//plugin的配置
plugins:[
new HtmlWebpackPlugin({
template:'./src/index.html'
})
],
//模式
mode:'development'
devServer:{
//项目构建后的路径
contentBase:resolve(_dirname,'build'),
//启动gzip压缩
compress:true,
//端口号
port:3000,
//自动打开浏览器
open:true,
//开启HMR功能
//当修改了webpack配置,一定要重启webpack服务
hot:true
},
//加东西
devtool:'eval-source-map'
}
oneOf
提升构建速度
//当一个文件被多个loader处理,一定要指定loader的先后执行顺序js中(先执行eslint 再执行babel)
const {resolve}=require('path');
const HtmlWebpackPlugin=require('html-webpack-plugin');
const MiniCssExtractPlugin=require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin=require('optimize-css-assets-webpack-plugin');
//复用loader
const commonCssLoader=[
MiniCssExtractPlugin.loader,
'css-loader',
{//兼容性,在package.json中定义browserslist
loader:'postcss-loader',
//修改配置
potions:{
ident:'postcss',
plugins:()=>[
//postcss插件
require('postcss-preset-env')()
]
}
}
];
module.export={
entry:'./src/index.js',
output:{
filename:'built.js',
path:resolve(_dirname,'build')
},
module:{
rules:[
{
test:/.js$/,
exclude:/node_modules/,
loader:'eslint-loader',
//优先执行的意思
enforce:'pre'
options:{
//自动修复eslint错误
fix:true
},
oneOf:[//以下loader只会匹配一个,不能两个配置处理同一种类型文件
{
test:/\.css$/,
use:[commonCssLoader]
},
{
test:/\.less$/,
use:[...commonCssLoader, 'less-loader']
},
{//在package.json中eslintConfig ---> airbnb
test:/.js$/,
exclude:/node_modules/,
loader:'eslint-loader',
options:{
presets:[
[
'@babel/preset-env',
{
useBuiltIns:'usage',
//指定corejs版本
corejs:{
version:3
},
//兼容性做到哪个版本浏览器
targets:{
chrome:'60',
firefox:'60',
ie:'9',
safari:'10',
edge:'17'
}
}
]
]
]
}
},
{
test:/\.(jpg|png|gif)$/,
loader:'url-loader',
options:{
limit:8*1024,
name:'[hash:10].[text]',
outputath:'imgs',
esModule:false
}
},//html里面的img
{
test:/\.html$/,
loader:'html-loader'
},
{
//打包其他资源
exclude:/\.(css|js|html|less|jpg|png|gif)/,
loader:'file-loader',
options:{
outputPath:'media',
name:'[hash:10],[ext]'
}
},
},
//plugin的配置
plugins:[
new HtmlWebpackPlugin({
template:'./src/index.html',
//压缩html代码,里面压缩配置
minify:{
//折叠空格
collapseWhitespace:true,
//移除注释
removeComments:true
}
}),
new MiniCssExtractPlugin({
//若想输入到css目录下/重命名
filename:'css/built.css'
}),
//压缩css:会运行更快
new OptimizeCssAssetsWebpackPlugin(),
],
//模式 生产环境下自动压缩js代码
mode:'production'
}
缓存
- babel:CacheDirectory:true–>让第二次打包构建速度更快
- 文件资源 修改文件名 hash:每次webpack构建打包时生成唯一hash值
问题:因为js和css使用同一hash值,如果重新打包会导致所有缓存失效
chunkhash:根据chunk生成的hash值,如果打包来自于同一个trunk,hash值一样
问题:js和css的hash值还是一样的,因为css是在js中被引入的,所以属于同一个trunk
什么是trunk,所有根据入口文件引入的js、css,都会用一个trunk,是一个代码块
contenthash:根据文件的内容生成的hash,不同文件hash值一定不一样–>让代码上线运行缓存速度更快
//当一个文件被多个loader处理,一定要指定loader的先后执行顺序js中(先执行eslint 再执行babel)
const {resolve}=require('path');
const HtmlWebpackPlugin=require('html-webpack-plugin');
const MiniCssExtractPlugin=require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin=require('optimize-css-assets-webpack-plugin');
//复用loader
const commonCssLoader=[
MiniCssExtractPlugin.loader,
'css-loader',
{//兼容性,在package.json中定义browserslist
loader:'postcss-loader',
//修改配置
potions:{
ident:'postcss',
plugins:()=>[
//postcss插件
require('postcss-preset-env')()
]
}
}
];
module.export={
entry:'./src/index.js',
output:{
filename:'built.[hash:10].js', //生成的资源带hash值
path:resolve(_dirname,'build')
},
module:{
rules:[
{
test:/.js$/,
exclude:/node_modules/,
loader:'eslint-loader',
//优先执行的意思
enforce:'pre'
options:{
//自动修复eslint错误
fix:true
},
oneOf:[//以下loader只会匹配一个,不能两个配置处理同一种类型文件
{
test:/\.css$/,
use:[commonCssLoader]
},
{
test:/\.less$/,
use:[...commonCssLoader, 'less-loader']
},
{//在package.json中eslintConfig ---> airbnb
test:/.js$/,
exclude:/node_modules/,
loader:'eslint-loader',
options:{
presets:[
[
'@babel/preset-env',
{
useBuiltIns:'usage',
//指定corejs版本
corejs:{
version:3
},
//兼容性做到哪个版本浏览器
targets:{
chrome:'60',
firefox:'60',
ie:'9',
safari:'10',
edge:'17'
}
}
]
],
//开启babel缓存
//第二次构建时,会读取之前的缓存
CacheDirectory:true
]
}
},
{
test:/\.(jpg|png|gif)$/,
loader:'url-loader',
options:{
limit:8*1024,
name:'[hash:10].[text]',
outputath:'imgs',
esModule:false
}
},//html里面的img
{
test:/\.html$/,
loader:'html-loader'
},
{
//打包其他资源
exclude:/\.(css|js|html|less|jpg|png|gif)/,
loader:'file-loader',
options:{
outputPath:'media',
name:'[hash:10],[ext]'
}
},
},
//plugin的配置
plugins:[
new HtmlWebpackPlugin({
template:'./src/index.html',
//压缩html代码,里面压缩配置
minify:{
//折叠空格
collapseWhitespace:true,
//移除注释
removeComments:true
}
}),
new MiniCssExtractPlugin({
//若想输入到css目录下/重命名
filename:'css/built.css'
}),
//压缩css:会运行更快
new OptimizeCssAssetsWebpackPlugin(),
],
//模式 生产环境下自动压缩js代码
mode:'production'
}
tree shaking
去除在应用程序中没有使用的代码
前提:1.必须使用es6模块化 2.开启production环境
作用:减少代码体积
在package.json中配置
"sideEffects":false;//所有代码都没有副作用(都可以进行 tree shaking)
//问题:可以能会把css/ @babel/polyfill(副作用)文件干掉
"sideEffects":["*.css","*.less"];//解决
code split
module.export={
//单入口
// entry:'./src/index.js',
entry:{
//多入口:有一个入口,最终输出就有一个bundle
main:'./src/index.js',
test:'./src/test.js',
}
output:{
//[name]取文件名
filename:'built.[name].[hash:10].js', //生成的资源带hash值
path:resolve(_dirname,'build')
},
//plugin的配置
plugins:[
],
//可以将node_modules中代码单独打包成一个chunk最终输出
//自动分析多入口文件中有没有公共的文件,如果有会打包成单独的一个trunk
optimzation:{
splitChunks:{
chunks:'all'
}
}
//模式 生产环境下自动压缩js代码
mode:'production'
}
第3种方式:通过js代码,让某个文件被单独打包成一个trunk
在index.js页面
import动态导入语法:能将某个文件单独打包
import('./test')
.then(()=>{
//文件加载成功
})
.catch(()=>{
//文件加载失败
})
懒加载和预加载
js文件的懒加载(触发了某些条件的时候才会加载)
//懒加载 --放在函数中,函数调用的时候加载:需要加载的时候才加载
//预加载 prefetch:会提前加载js文件
//正常加载可以认为是并行加载(用一时间加载多个文件) 预加载:等其他资源加载完毕,浏览器空闲了,再偷偷下载
//'/*webpackChunkName:'test',webpackPrefetch:true*/'分割成两个文件
import('/*webpackChunkName:'test'*/'./test').then(()=>{
console.log('过一段时间再加载成功')
}
PWA(渐进式网络开发应用程序)
离线也可以访问
workbox 下载包workbox-webpack-plugin
//当一个文件被多个loader处理,一定要指定loader的先后执行顺序js中(先执行eslint 再执行babel)
const {resolve}=require('path');
const HtmlWebpackPlugin=require('html-webpack-plugin');
const MiniCssExtractPlugin=require('mini-css-extract-plugin');
const OptimizeCssAssetsWebpackPlugin=require('optimize-css-assets-webpack-plugin');
const WorkboxWebpackPlugin=require('workbox-webpack-plugin');
//复用loader
const commonCssLoader=[
MiniCssExtractPlugin.loader,
'css-loader',
{//兼容性,在package.json中定义browserslist
loader:'postcss-loader',
//修改配置
potions:{
ident:'postcss',
plugins:()=>[
//postcss插件
require('postcss-preset-env')()
]
}
}
];
module.export={
entry:'./src/index.js',
output:{
filename:'built.[hash:10].js', //生成的资源带hash值
path:resolve(_dirname,'build')
},
module:{
rules:[
{
test:/.js$/,
exclude:/node_modules/,
loader:'eslint-loader',
//优先执行的意思
enforce:'pre'
options:{
//自动修复eslint错误
fix:true
},
oneOf:[//以下loader只会匹配一个,不能两个配置处理同一种类型文件
{
test:/\.css$/,
use:[commonCssLoader]
},
{
test:/\.less$/,
use:[...commonCssLoader, 'less-loader']
},
{//在package.json中eslintConfig ---> airbnb
test:/.js$/,
exclude:/node_modules/,
loader:'babel-loader',
options:{
presets:[
[
'@babel/preset-env',
{
useBuiltIns:'usage',
//指定corejs版本
corejs:{
version:3
},
//兼容性做到哪个版本浏览器
targets:{
chrome:'60',
firefox:'60',
ie:'9',
safari:'10',
edge:'17'
}
}
]
],
//开启babel缓存
//第二次构建时,会读取之前的缓存
CacheDirectory:true
]
}
},
{
test:/\.(jpg|png|gif)$/,
loader:'url-loader',
options:{
limit:8*1024,
name:'[hash:10].[text]',
outputath:'imgs',
esModule:false
}
},//html里面的img
{
test:/\.html$/,
loader:'html-loader'
},
{
//打包其他资源
exclude:/\.(css|js|html|less|jpg|png|gif)/,
loader:'file-loader',
options:{
outputPath:'media',
name:'[hash:10],[ext]'
}
},
},
//plugin的配置
plugins:[
new HtmlWebpackPlugin({
template:'./src/index.html',
//压缩html代码,里面压缩配置
minify:{
//折叠空格
collapseWhitespace:true,
//移除注释
removeComments:true
}
}),
new MiniCssExtractPlugin({
//若想输入到css目录下/重命名
filename:'css/built.css'
}),
//压缩css:会运行更快
new OptimizeCssAssetsWebpackPlugin(),
new WorkboxWebpackPlugin.GenerateSW({
//帮助serviceworker快速启动
//删除旧的serviceworker
//生成一个serviceworker配置文件
clientsClaim:true,
skipWaitting:true
})
],
//模式 生产环境下自动压缩js代码
mode:'production'
}
注册serviceworker
在入口文件
//注意:1.eslint不认识window。navigator全局变量
解决:需要修复package.json中eslintConfig配置
2.sw代买必须运行在服务器上-->nodejs -->npm i serve -g serve -s build
启动服务器,将build目录下的所有资源作为静态资源暴露出去
"env":{
"browser":true//支持浏览器端全局变量
}//
//注册serviceworker
//处理兼容性问题
if('serviceworker' in navigator){
window.addEventListener('load',()=>{
navigator.serviceworker.register('/service-worker.js')
.then(()=>{
console.log('注册成功了')
})
.catch(()=>{
console.log('注册失败了')
})
}
多进程打包
下载thread-loader ,一般给babel-loader用
{//在package.json中eslintConfig ---> airbnb
test:/.js$/,
exclude:/node_modules/,
use:[
//开启多进程打包,进程开启有时间的,大概600ms,进程通信也有开销
//只有在工作消耗时间比较长的时候,才需要多进程打包
'thread-loader',
//修改配置
options:{
workers:2//进程2个
}
{
loader:'babel-loader',
options:{
presets:[
[
'@babel/preset-env',
{
useBuiltIns:'usage',
//指定corejs版本
corejs:{
version:3
},
//兼容性做到哪个版本浏览器
targets:{
chrome:'60',
firefox:'60',
ie:'9',
safari:'10',
edge:'17'
}
}
]
],
//开启babel缓存
//第二次构建时,会读取之前的缓存
CacheDirectory:true
]
}
}
],
},
externals和dill(动态连接库)
不同的是:dill单独的对库进行打包,有的不打包,有的拆分开打包成多个
const {resolve}=require('path');
const HtmlWebpackPlugin=require('html-webpack-plugin');
const webpack=require('webpack');
const AddAssetHtmlWebpackPlugin=require('add-asset-html-webpack-plugin');
module.export={
entry:'./src/index.js',
output:{
filename:'built.[hash:10].js', //生成的资源带hash值
path:resolve(_dirname,'build')
},
module:{
rules:[]
},
//plugin的配置
plugins:[
new HtmlWebpackPlugin({
template:'./src/index.html',
}),
//告诉webpack那些库不参与打包,同时使用时名称也得改
new webpack.DillPlugin({
mainfes:resolve(_dirname,'dill/mainfest.json'),
//将某个文件打包输出出去,并在html中引入该资源
new AddAssetHtmlWebpackPlugin({
filepath:resolve(_dirname,'dill/jquery.json'),
})
],
//模式 生产环境下自动压缩js代码
mode:'production',
externals:{//快
//拒绝包打包进来 --npm包
jquery:'jQuery'
}
}
在index.js页面引入
dill打包,创建webpack.dill.js
使用dill技术,对某些库进行单独打包(jQuery、react、vue。。。)
运行–> webpack --config webpack.dill.js
const {resolve}=require('path');
const webpack=require('webpack');
module.export={
entry:{//最终打包生成[name]-->jquery
//['jquery']-->要打包的库是jQuery
jquery:['jquery']
},
output:{
filename:'[name].js',
path:resolve(_dirname,'dill'),
libiary:'[name]_[hash]',//打包的库里面向外面暴露出去的内容的名字
},
plugins:[
new webpack.DillPlugin({
name:'name_[hash]',//映射库的暴露的内容名称
path:resolve(_dirname,'dill/mainfest.json'),//输出文件路径
],
mode:'production'
}