这是我参与「第四届青训营 」笔记创作活动的的第8天
webpack 中为我们提供了很多很好用的工具,这些工具能带给我们更好的开发体验和更高代码质量,webpack 的工具非常之多,下面的内容也仅仅只是冰山一角。
HMR
MHR:hot module replacement 热模板替换 / 热模板替换
开发的时候,当我们修改了其中一个模块代码,webpack 默认会将所有模块全部重新打包编译,速度很慢。
HMR 能够在程序运行中,当一个模块发生变化,能智慧重新打包这一个模块(而不是打包所有模块),能够极大的提升构建速度
devServer:{
host:'localhost',
port:3000,
open:true,
//添加这一行代码即可。
hot: true,
}
此时 css 文件可以使用HMR功能:因为style-loader内部实现了。
但是,js 文件和 html 文件没有使用 HMR
js HMR实现
//在index.js入口js文件中最后添加下面的代码
if (module.hot) {
// 之后有了其他的 js 文件,往后面加就可以了
module.hot.accept("./index.js");
module.hot.accept("./add.js");
}
那么,如何实现 html 的热重载呢?
SourceMap
当我们使用 webpack 打包代码后,所有的 css文件和 js文件合并成了一个文件,并且多了很多的代码。此时代码提示的错误位置是打包之后的文件,一旦代码量的增大,我们很难定位到源码的错误位置,这时,我们就需要 SourceMap 为我们提供更加准确的代码提示。
SourceMap 是一个用来生成源代码与构建后代码一一映射的文件方案
它会生成一个 xxx.map 文件,里面包含源代码和构建后代码的每一行,每一列的映射关系,当构建后代码出错了,会通过 xxx.map 文件,从构建后代码出错位置找到映射后源代码出错的位置,从而让浏览器提示源代码出错位置,帮助我们更快的找到出错根源。
使用
SourceMap 的种类有很多,详情请见官方文档。webpack.js.org/configurati…
实际开发中,我们常用两种形式即可:
-
开发模式:
cheap-module-source-map- 优点:打包编译速度快,值包含行映射
- 缺点:没有列映射
module.exports = { mode:'development', devtool:'cheap-module-source-map' } -
生产模式:
source-map- 优点:包含行/列映射
- 缺点:打包编译速度更慢
module.exports = { mode:'development', devtool:'source-map' }
oneOf
在rules中,一种文件,往往只需要匹配一个loader,但是,浏览器会将该文件经过所有loader
使用oneOf就可以解决这样的问题
module: {
rules: [
{
oneOf: [
{
test: /.css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [["postcss-preset-env"]],
},
},
},
],
},
{
test: /.less$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [["postcss-preset-env"]],
},
},
},
"less-loader",
],
},
{
test: /.s[ac]ss$/i,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [["postcss-preset-env"]],
},
},
},
"sass-loader",
],
},
{
test: /.(png|jpe?g|gif|webp|svg)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024,
},
},
generator: {
filename: "image/[hash][ext][query]",
},
},
{
test: /.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
},
},
},
],
},
],
},
Cache缓存
每一次打包时 js 文件都要经过 Eslint 检查 和 Babel 编译,速度比较慢。
我们可以缓存之前的 Eslint 检查 和 Babel 编译结果,这样第二次打包的速度就会更快了
下面以缓存 Babel 为例
{
test: /.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
cacheDirectory:true,
cacheCompression:false,
},
},
},
Tree-Shaking树摇(去除无用代码)
开发时我们定义了一些工具函数库,或者应用了第三方工具库或组件库。
如果没有特殊处理的话,我们打包时会引入整个库,但是实际上可能我们只用上了其中极小部分的功能。
这样将整个库都打包进来,体积就太大了。这时,Tree-Shaking 就会帮我们去除不需要的代码。
前提:1.开启production环境 2.在引入时只能用import而不能用require
压缩图片
开发项目中引用了较多图片,那么图片体积会比较大,将来请求的速度比较慢。
我们可以对图片进行压缩,减少图片的体积。
注意:如果项目中的图片时在线连接,那么就不需要了。本地项目静态图片才需要进行压缩。
安装
npm install image-minimizer-webpack-plugin imagemin --save-dev
无损压缩
npm install imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo --save-dev
有损压缩
npm install imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo --save-dev
如果下载不下来,可以使用 cnmp 试一下
使用
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const { extendDefaultPlugins } = require("svgo");
module.exports = {
module: {
rules: [
{
test: /.(jpe?g|png|gif|svg)$/i,
type: "asset",
},
],
},
optimization: {
minimizer: [
"...",
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.imageminMinify,
options: {
// Lossless optimization with custom option
// Feel free to experiment with options for better result for you
plugins: [
["gifsicle", { interlaced: true }],
["jpegtran", { progressive: true }],
["optipng", { optimizationLevel: 5 }],
// Svgo configuration here https://github.com/svg/svgo#configuration
[
"svgo",
{
plugins: extendDefaultPlugins([
{
name: "removeViewBox",
active: false,
},
{
name: "addAttributesToSVGElement",
params: {
attributes: [{ xmlns: "http://www.w3.org/2000/svg" }],
},
},
]),
},
],
],
},
},
}),
],
},
};
code split代码分离
多入口起点配置
entry:{
main:'./src/main.js',
app:'./src/app.html'
},
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].js",
},
多入口提取公共模块
如果上面的 main.js 和 app.js 都引入并且使用了 math.js 中的 某些函数(比如一个最简单的sum函数),那么,打包的时候,这些函数会被打包两份,如果代码量庞大且引用过多的话,会极大的降低打包的速度。
这时候我们就需要配置提取这些公共模块的内容
配置
optimization: {
splitChunks: {
chunks: "all", // 对所有模块都进行分隔
// 以下是默认值
// minSize: 20000,// 分割代码最小的大小
// minRemainingSize: 0,// 类似于 minSize ,最后确保提取的文件大小不能为0
// minChunks: 1,// 至少被应用的次数,瞒住条件待会代码分割
// maxAsyncRequests: 30,// 按需加载时并行加载的文件的最大数量
// maxInitialRequests: 30,// 入口js文件最大并行请求数量
// enforceSizeThreshold: 50000,// 超过50kb一定会单独打包(此时会忽略minRemainingSize,maxAsyncRequest,maxInitialRequests)
// cacheGroups: {// 组,哪些模块要打包到一个组
// defaultVendors: {//组名
// test: /[\/]node_modules[\/]/,// 需要打包到一起的模块
// priority: -10,// 权重(越大越高)
// reuseExistingChunk: true,// 如果当前 chunk 包含已从 bundle 中拆分出的模块,则它将被重用,而不是生成新的模块
// },
// default: {// 其他没有写的配置会使用上面的默认值
// minChunks: 2,// 这里的minChunks权重更大
// priority: -20,
// reuseExistingChunk: true,
// },
// },
// 修改配置
cacheGroups: {
default: {
minSize: 0,
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
多入口按需加载
如果有一个功能,用户很可能用不到,那我们可以采取按需加载,当用户触发某个事件的时候,在获取特定功能的文件。
比如,我创建了一个 count.js ,里面有一些函数,当我们点击页面上面的某个按钮的时候,就加载这个js文件,反之,不加载。
document.getElementById("btn").onclick = function(){
// import 动态导入,会将动态导入的文件代码分割(拆分成为单独模块),在需要使用的时候自动加载
import('./count').then((res)=>{
console,log('模块加载成功',res);
}).catch((err)=>{
console.log('模块加载失败',err)
})
}
在未点击按钮的时候
点击按钮后(新增了一个293.js)
单入口(SPA)
如果是单入口文件,那么我们只需要:
optimization: {
splitChunks: {
chunks: "all", // 对所有模块都进行分隔
}
},
给模块命名
在 webpack.config.js 文件中 新增 chunkFilename 节点
output: {
path: path.resolve(__dirname, "dist"),
filename: "js/[name].js",
chunkFilename:'js/[name].chunk.js',
clean: true,
},
在引入的时候
document.getElementById("btn").onclick = function(){
// import 动态导入,会将动态导入的文件代码分割(拆分成为单独模块),在需要使用的时候自动加载
import(/* webpackChunkName:"count" */ './count').then((res)=>{
console,log('模块加载成功',res);
}).catch((err)=>{
console.log('模块加载失败',err)
})
}
PWA渐进式网络开发应用程序(离线可访问)
PWA 主要目的是 在离线的状态下能够继续运行功能
安装
npm install workbox-webpack-plugin --save-dev
使用
const WorkboxPlugin = require('workbox-webpack-plugin');
plugins: [
new HtmlWebpackPlugin({
title: 'Output Management',
title: 'Progressive Web Application',
}),
new WorkboxPlugin.GenerateSW({
clientsClaim: true,
skipWaiting: true,
}),
],
在index.js入口文件中注册servicworker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/service-worker.js').then(registration => {
console.log('SW registered: ', registration);
}).catch(registrationError => {
console.log('SW registration failed: ', registrationError);
});
});
}
代码必须运行在服务器上
可以使用nodejs,也可以使用其他的,这里以serve包为例
//安装
npm i serve -g
//运行
serve dist
结语
文章如果有不正确的地方,欢迎指正,共同学习,共同进步。
若有侵权,请联系作者删除。