打包优化
HMR: hot module replacement 热模块替换 / 模块热替换
作用: 一个模块发生变化,只会重新打包这一个模块(而不是打包所有)
极大提升构建速度
样式文件:可以使用HMR功能,因为style-loader 内部实现了
js文件:默认不使用HMR功能 --> 需要修改 js 代码,添加支持HMR功能的代码
注意:HMR功能对js的处理,只能处理非入口js文件的其他文件
html文件:默认不使用HMR功能,同时会导致html文件不会热更新了 (不用做HMR功能呢)
解决: 修改entry入口,将html文件引入 例: entry: ['./src/js/index.js', './src/index.html']
代码调试
代码调试 devtool: 'source-map'
source-map: 一种提供源代码到构建代码映射技术(如果构建后代码出错,通过映射关系可以追踪源代码的错误)
[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
source-map: 外部
错误代码的准确信息 和 源代码的错误位置
inline-source-map: 内联
1. 只生成一个内联 source-map
错误代码的准确信息 和 源代码的错误位置
hidden-source-map: 外部
错误代码错误原因,但是没有错误位置
不能追踪到源代码的错误,只能提示到构建后代码的错误位置
eval-source-map: 内联
1.每一个文件都生成对应的source-map, 都在eval
错误代码的准确信息 和 源代码的错误位置
nosources-source-map: 外部
错误代码的准确信息,但是没有任何源代码信息(隐藏源代码)
cheap-source-map: 外部
错误代码的准确信息 和 源代码的错误位置
只能精确到行
cheap-module-source-map: 外部
错误代码的准确信息 和 源代码的错误位置
module 会将loader和source map加入
内联 和 外部的区别:1. 外部生成了文件,内联没有 2.内联构建速度更快
开发环境:速度快,调试友好
速度快(eval>inline>cheap>...)
eval-cheap-source-map
eval-source-map
调试友好
source-map
cheap-module-source-map
cheap-source-map
--> eval-source-map 更友好(脚手架默认的) ,其次 eval-cheap-module-source-map
生产环境:源代码要不要隐藏? 调试要不要更友好
内联会让体积变大,所以生产环境用外部
nosources-source-map 全部隐藏
hidden-source-map 只隐藏源代码,会提示构建后代码错误信息
--> source-map 调试友好 / cheap-module-source-map
oneOf 以下loader只会匹配一个
注意: 不能有两个配置处理同一种类型文件
提升构建速度
缓存
babel缓存
cacheDirectory: true
--> 让第二次打包更快
文件资源缓存
hash:每次webpack构建打包会生成一个唯一的hash值
问题: 因为js和css同时使用一个hash值
如果重新打包,会导致所有缓存失效(可能只改动一个文件)
chunkhash: 根据chunk生成的hash值。如果打包来源于同一个chunk,那么hash值就一样
问题:js和css的hash值还是一样的,因为css是在js中被引入的,所以同属于一个chunk
contenthash: 根据文件的内容生成hash值。不同的文件hash值一定不一样,并且是单独管理的
--> 让代码上线运行缓存更好使用
tree shaking: 去除无用的代码
前提:1.必须使用es6模块化 2. 开启production环境
作用: 减少代码体积
在package.json中配置
"sideEffects": false; 所有代码都没有副作用(都可以进行 tree shaking)
webpack版本原因造成的问题: 可能把css/@babel/polyfill (副作用)文件干掉
"sideEffects": [".css", ".less"]
code split
demo1:
多入口
entry: {
// 多入口:有一个入口,最终输出就有一个bundle
index: './src/js/index.js',
test: './src/js/test.js'
},
demo2:
optimization: {
splitChunks: {
chunks: 'all'
}
},
demo3:
通过js代码,让某个文件被单独打包成一个chunk
import 动态导入语法:能将某个文件单独打包.
import(/*webpackChunkName:'test' */'./test')
.then(() => {
// eslint-disable-next-line
console.log('加载成功')
})
.catch(() => {
// eslint-disable-next-line
console.log('加载失败')
})
懒加载 lazy loading
懒加载~: 当文件需要使用的时候在加载
预加载 prefetch: 会在使用之前,提前加载js文件
// 例如 点击事件之后加载test.js
document.getElementById('btn').onclick = function(){
import(/*webpackChunkName:'test',webpackPrefetch: true */'./test')
.then(() => {
// eslint-disable-next-line
console.log('加载成功')
})
.catch(() => {
// eslint-disable-next-line
console.log('加载失败')
})
}
PWA 渐进式网络开发应用程序(在入口文件里配置)
/*
1.eslint 不认识window navigator全局变量
解决:需要修改package.json 中的 eslintConfig 配置
"env": {
"browser": true // 支持浏览器变量
}
2. sw代码必须运行在服务器上
--> nodejs
--> npm i serve -g
serve -s -build 启动服务器,将 build 目录下所有资源作为静态资源暴露出去
*/
// 注册 serviceworker
// 处理兼容性问题
if ('serviceworker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js')
.then(() => {
// eslint-disable-next-line
console.log('sw注册成功~')
})
.catch(() => {
// eslint-disable-next-line
console.log('sw注册失败~')
});
});
}
webpack 开发环境配置文件
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 提取css成单独文件的插件
// mini-css-extract-plugin
const miniCssExtractPlugin = require('mini-css-extract-plugin')
// 压缩css插件:optimize-css-assets-webpack-plugin
const optimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
// 设置 nodeJs 环境变量,方便开发者调试
process.env_NODE_ENV = 'development'
module.exports = {
entry: ['./src/js/index.js', './src/index.html'],
output: {
filename: 'js/built.[contenthash:10].js',
path: resolve(__dirname, 'build')
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_moudles/, // 排除,防止eslint检测
loader: 'eslint-loader',
// 优先执行
enforce: 'pre',
options: {
// 自动修复
fix: true
}
},
{
// 以下loader只会匹配一个
// 注意: 不能有两个配置处理同一种类型文件
// 提升构建速度
oneOf: [
{
// 处理css资源
test: /\.css$/,
use: [
miniCssExtractPlugin.loader,
// 将css整合到js中
'css-loader',
{
loader: 'postcss-loader'
}
]
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
// 预设: 指示 babel 做怎样的兼容处理
// presets: ['@babel/preset-env']
presets: [
[
'@babel/preset-env',
{
// 按需加载
useBuiltIns: 'usage',
// 指定 core-js 版本
corejs: {
version: 3
},
// 指定兼容性做到哪个版本的浏览器
targets: {
chrome: '60',
firefox: '60',
ie: '9',
safari: '10',
edge: '17'
}
}
]
]
}
},
{
// 处理图片资源
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 5 * 1024,
name: '[hash:10].[ext]',
// 关闭es6模块
esMoudle: false,
outputPath: 'imgs'
}
},
{
// 处理html的img资源
test: /\.html$/,
loader: 'html-loader',
},
{
// 处理其他资源
exclude: /\.(css|less|js|html|jpg|png|gif)/,
loader: 'file-loader',
options: {
name: '[contenthash:10].[ext]',
outputPath: 'media'
}
}
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
// 压缩html
minify: {
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComments: true
}
}),
new miniCssExtractPlugin({
//对输出的css文件进行重命名
filename: 'css/built.[contenthash:10].css'
}),
// 压缩css
new optimizeCssAssetsWebpackPlugin()
],
mode: 'development', // 开发模式
devServer: {
contentBase: resolve(__dirname, 'build'),
compress: true,
port: 3000,
open: true,
// 开启 HMR 功能
// 当修改了webpack配置,新配置想生效,重启webpack服务
hot: true,
},
devtool: 'eval-source-map'
}