Webpack5 高级篇(一)

介绍

主要是介绍Webpack 高级配置

所谓高级配置其实就是进行Webpack 优化, 让我们代码在编译/运行时性能更好~

我们会以下角度来进行优化:

  1. 提升开发体验

  2. 提升打包构建速度

  3. 减少代码体积

  4. 减少代码运行性能

一. 提升开发体验 SourceMap(映射文件)

1.为什么?

开发时我们运行的代码是经过webpack 编译后的,例如下面这个样子

复制代码
/*
 * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
 * This devtool is neither made for production nor for readable output files.
 * It uses "eval()" calls to create a separate source file in the browser devtools.
 * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
 * or disable the default devtool with "devtool: false".
 * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
 */
/******/ (() => { // webpackBootstrap
/******/ 	"use strict";
/******/ 	var __webpack_modules__ = ({

/***/ "./node_modules/css-loader/dist/cjs.js!./node_modules/less-loader/dist/cjs.js!./src/less/index.less":
/*!**********************************************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js!./node_modules/less-loader/dist/cjs.js!./src/less/index.less ***!
  \**********************************************************************************************************/
/***/ ((module, __webpack_exports__, __webpack_require__) => {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../node_modules/css-loader/dist/runtime/noSourceMaps.js */ \"./node_modules/css-loader/dist/runtime/noSourceMaps.js\");\n/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);\n// Imports\n\n\nvar ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()));\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \".box2 {\\n  width: 100px;\\n  height: 100px;\\n  background-color: deeppink;\\n}\\n\", \"\"]);\n// Exports\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);\n\n\n//# sourceURL=webpack://webpack5/./src/less/index.less?./node_modules/css-loader/dist/cjs.js!./node_modules/less-loader/dist/cjs.js");

/***/ }),

所有css 和 js 合并成了一个文件,并且多了其他代码,此时如果代码运行出错那么提示代码错误位置我们是看不懂的,一单将来开发代码文件很多,那么很难去发现错误出现在哪里

所以我们需要更加准确的错误提示,来帮助我们更好的开发代码

2.sourceMap

SourceMap(源代码映射) 是一个用来生成源代码与构建后代码一一映射的文件的文案

它会生成一个xxx.map 文件,里面包含源代码和构建后代码每一行,每一列的映射关系. 当构建后代码出错了,会通过xxx.map文件,从构建后代码出错位置找到映射后源代码出错位置,从而让浏览器提示源代码文件出错位置,帮助我们更快的找到错误根源

3. 怎么用

通过查看Devtool | webpack 中文文档 文档可知,SourceMap的值有很多中情况

但实际开发时我们只需要关注两种情况即可:

开发模式: cheap-modules-source-map
  • 优点: 打包编译速度快,只包含行映射

  • 缺点: 没有列映射

module.exports = {

// 其他省略

mode: "development",

devtool: "cheap-module-source-map",

};

生产模式:source-map
  • 优点: 包含行 / 列映射

  • 缺点: 打包编译速度更慢

module.exports = {

// 其他省略

mode: "production",

devtool: "source-map",

};

4.示例 生产模式

二. 提升打包构建速度 HotModuleReplacement (热模块替换)

1.为什么?

开发时我们修改了其中一个模块代码,Webpack默认会将所有模块全部重新打包编译,速度很慢,所以我们需要做到修改某个模块代码,就只有这个模块代码需要重新打包编译,其他模块不变,这样打包速度就能很快

2. HotModuleReplacement (热模块替换)?

HotModuleReplacement(HMR/热模块替换) : 在程序运行中,替换,添加或删除模块,而无需重新加载整个页面

3. 怎么用

  1. 基本配置

module.exports = {

// 其他省略

devServer: {

host: "localhost", // 启动服务器域名

port: "3000", // 启动服务器端口号

open: true, // 是否自动打开浏览器

hot: true, // 开启HMR功能(只能用于开发环境,生产环境不需要了)

},

};

此时css样式经过style-loader处理,已经具备HMR功能了. 但是js还不行

2. js配置 main.js

// main.js

import count from "./js/count";

import sum from "./js/sum";

// 引入资源,Webpack才会对其打包

import "./css/iconfont.css";

import "./css/index.css";

import "./less/index.less";

import "./sass/index.sass";

import "./sass/index.scss";

import "./styl/index.styl";

const result1 = count(2, 1);

console.log(result1);

const result2 = sum(1, 2, 3, 4);

console.log(result2);

// 判断是否支持HMR功能

if (module.hot) {

module.hot.accept("./js/count.js", function (count) {

const result1 = count(2, 1);

console.log(result1);

});

module.hot.accept("./js/sum.js", function (sum) {

const result2 = sum(1, 2, 3, 4);

console.log(result2);

});

}

上面这样写会很麻烦,所以实际开发我们会是会用其他loader来解决

比如:Vue GitHub - vuejs/vue-loader: 📦 Webpack loader for Vue.js components,

ReactGitHub - gaearon/react-hot-loader: Tweak React components in real time. (Deprecated: use Fast Refresh instead.)

三. OneOf

打包时每个文件都会经过所有loader处理,虽然因为test正则原因没有处理上,但是都有过一遍,比较慢

OneOf 就是只能匹配上一个Loader,剩下的就不匹配了

用法

javascript 复制代码
module.exports={

  modules:{
    
     rules:[

       oneOf:[
       
        //各种Loader
           
          {
            // 用来匹配 .css 结尾的文件
            test: /\.css$/,
            // use 数组里面 Loader 执行顺序是从右到左
            use: ["style-loader", "css-loader"],
          },
       ....//省略其他loader 
      
       ] 
    ]
 
  }


}

四. include/Exclude

开发时我们需要使用第三方的库或插件,所有文件都下载到node_modules中了,而这些文件是不需要编译时可以直接使用的

所以我们在对js文件处理时,要排除node_modules下面的文件

1. 是什么

include

包含, 只处理xxx文件

exclude

排除,除了xxx文件以外其他文件都处理

2. 使用

javascript 复制代码
  module: {
    rules: [
      {
        //每个文件只能被其中一个loader处理
        oneOf: [
          {
            test: /\.js$/,
            // exclude: /node_modules/, // 排除node_modules代码不编译
            include: path.resolve(__dirname, '../src'), // 也可以用包含
            loader: 'babel-loader'
          }
        ]
      }
    ]
  },
//插件
  plugins: [
    new ESLintWebpackPlugin({
      // 指定检查文件的根目录
      context: path.resolve(__dirname, '../src'),
      exclude: 'node_modules' // 默认值
    }),
    new HtmlWebpackPlugin({
      // 以 public/index.html 为模板创建文件
      // 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
      template: path.resolve(__dirname, '../public/index.html')
    })
  ],

五 . Cache(缓存)

1.每次打包时js文件都要经过Eslint检查和Babel编译,速度比较慢

我们可以缓存之前的Eslint检查和Babel编译结果,这样第二次打包时速度就会更快了

2. Cache

对Eslint检查和Babel编译结果进行缓存

3. 使用

javascript 复制代码
  module: {
    rules: [
      {
        //每个文件只能被其中一个loader处理
        oneOf: [
           {
            test: /\.js$/,
            // exclude: /node_modules/, // 排除node_modules代码不编译
            include: path.resolve(__dirname, "../src"), // 也可以用包含
            loader: "babel-loader",
            options: {
              cacheDirectory: true, // 开启babel编译缓存
              cacheCompression: false, // 缓存文件不要压缩
            },
          },
        ]
      }
    ]
  },
//插件
  plugins: [
      new ESLintWebpackPlugin({
      // 指定检查文件的根目录
      context: path.resolve(__dirname, "../src"),
      exclude: "node_modules", // 默认值
      cache: true, // 开启缓存
      // 缓存目录
      cacheLocation: path.resolve(
        __dirname,
        "../node_modules/.cache/.eslintcache"
      ),
    }),
    new HtmlWebpackPlugin({
      // 以 public/index.html 为模板创建文件
      // 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
      template: path.resolve(__dirname, '../public/index.html')
    })
  ],

六. 多进程 Thead

当项目越来越庞大时,打包速度越来越慢,甚至于需要一个下午才能打包出来代码,这个速度是比较慢的

我们需要继续提升打包速度,其实就是要提升js的打包速度,因为其他文件都比较少

而对js文件处理主要就是eslint,babel,Terser三个工具,所以我们要提升它们的运行速度.

我们可以开启多进程同时处理js文件,这样速度就比之前的单进程打包更快了

1.Thead ?

多进程打包: 开启电脑的多个进程同时干一件事,速度更快

需要注意: 请仅在特别好使的操作中使用,因为每个进程启动就有大约为600ms左右开销

2.使用

npm i thread-loader -D

javascript 复制代码
const os = require('os')

//代码压缩
const TerserPlugin = require('terser-webpack-plugin')

// cpu核数
const threads = os.cpus().length

// 获取处理样式的Loaders
const getStyleLoaders = (preProcessor) => {
  return [
    MiniCssExtractPlugin.loader,
    'css-loader',
    {
      loader: 'postcss-loader',
      options: {
        postcssOptions: {
          plugins: [
            'postcss-preset-env' // 能解决大多数样式兼容性问题
          ]
        }
      }
    },
    preProcessor
  ].filter(Boolean)
}

module.exports = {
  entry: './src/main.js',
  output: {
    path: path.resolve(__dirname, '../dist'), // 生产模式需要输出
    filename: 'static/js/main.js', // 将 js 文件输出到 static/js 目录中
    clean: true
  },
  module: {
    rules: [
      {
        oneOf: [
          {
            test: /\.js$/,
            // exclude: /node_modules/, // 排除node_modules代码不编译
            include: path.resolve(__dirname, '../src'), // 也可以用包含
            use: [
              {
                loader: 'thread-loader', // 开启多进程
                options: {
                  workers: threads // 数量
                }
              },
              {
                loader: 'babel-loader',
                options: {
                  cacheDirectory: true // 开启babel编译缓存
                }
              }
            ]
          }
        ]
      }
    ]
  },
  plugins: [
    new ESLintWebpackPlugin({
      // 指定检查文件的根目录
      context: path.resolve(__dirname, '../src'),
      exclude: 'node_modules', // 默认值
      cache: true, // 开启缓存
      // 缓存目录
      cacheLocation: path.resolve(
        __dirname,
        '../node_modules/.cache/.eslintcache'
      ),
      threads // 开启多进程
    }),
    new HtmlWebpackPlugin({
      // 以 public/index.html 为模板创建文件
      // 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
      template: path.resolve(__dirname, '../public/index.html')
    }),
    // 提取css成单独文件
    new MiniCssExtractPlugin({
      // 定义输出文件名和目录
      filename: 'static/css/main.css'
    })
    // css压缩
    // new CssMinimizerPlugin(),
  ],
  //压缩操作
  optimization: {
    //开启代码压缩
    minimize: true,
    minimizer: [
      // css压缩也可以写到optimization.minimizer里面,效果一样的
      new CssMinimizerPlugin(),
      // 当生产模式会默认开启TerserPlugin,但是我们需要进行其他配置,就要重新写了
      new TerserPlugin({
        parallel: threads // 开启多进程
      })
    ]
  },
  mode: 'production',

  //源代码映射
  devtool: 'source-map'
}

七. 减少代码体积 Tree Shaking

开发时我们定义了一些工具函数,或者引用第三方工具函数库或组件库

如果没有特殊处理的话我们打包时会引入整个库,但是实际上可能我们只用上极小部分的功能,这样将整个库打包进来,体积就太大了

TreeShaking

是一个术语,通常用于描述移除js中的没有使用上的代码

注意; 它依赖ES Module

使用

Webpack 已经默认开启了这个功能,无需其他配置

八. 减少Babel生成文件的体积

Babel 为编译的每个文件都插入了辅助代码,使代码体积过大!

Babel 为一些公共方法使用了非常小的辅助代码,比如_extend,默认情况下会被添加到每一个需要它的文件中, 你可以将这些辅助代码作为一个独立模块,来避免重复引入

@babel/plugin-transform-runtime: 禁用了Babel自动对每个文件的runtime注入,而是引入@babel/plugin-transform-runtime 并且使所有辅助代码从这里引用

使用

  1. 下载

npm i @babel/plugin-transform-runtime -D

  1. 配置

// 完整配置示例

module.exports = {

// 其他webpack配置...

module: {

rules: [

{

test: /\.js$/, // 匹配.js文件

// exclude: /node_modules/, // 排除node_modules代码不编译

include: path.resolve(__dirname, '../src'), // 指定只编译src目录

use: [

{

loader: 'thread-loader', // 开启多进程

options: {

workers: threads // 工作进程数量

}

},

{

loader: 'babel-loader',

options: {

cacheDirectory: true, // 开启babel编译缓存

cacheCompression: false, // 缓存文件不压缩

plugins: ['@babel/plugin-transform-runtime'] // 减少代码体积

}

}

]

}

]

}

}

九. image Minimizer (压缩图片)

开发项目中引用了较多图片,那么图片体积会比较大,将来请求速度比较慢

我们可以对图片进行压缩,减少图片体积

注意: 如果项目中图片都是在线链接,那么就不需要了. 本地项目静态图片才需要进行压缩

image-minimizer-webpack-plugin: 用来压缩图片的插件

1. 使用

npm i image-minimizer-webpack-plugin imagemin -D

还有剩下包需要下载, 有两种模式:

  1. 无损压缩

npm install imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D

2.有损压缩

npm install imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo -D

2.配置

我们以无损压缩配置为例:

javascript 复制代码
  //插件
  plugins: [
    new ESLintWebpackPlugin({
      // 指定检查文件的根目录
      context: path.resolve(__dirname, '../src'),
      exclude: 'node_modules', // 默认值
      cache: true, // 开启缓存
      // 缓存目录
      cacheLocation: path.resolve(
        __dirname,
        '../node_modules/.cache/.eslintcache'
      ),
      threads // 开启多进程
    }),
    new HtmlWebpackPlugin({
      // 以 public/index.html 为模板创建文件
      // 新的html文件有两个特点:1. 内容和源文件一致 2. 自动引入打包生成的js等资源
      template: path.resolve(__dirname, '../public/index.html')
    }),
    // 提取css成单独文件
    new MiniCssExtractPlugin({
      // 定义输出文件名和目录
      filename: 'static/css/main.css'
    })
    // css压缩
    // new CssMinimizerPlugin(),
  ],
  //压缩操作
  optimization: {
    //开启代码压缩
    minimize: true,
    minimizer: [
      // css压缩也可以写到optimization.minimizer里面,效果一样的
      new CssMinimizerPlugin(),
      // 当生产模式会默认开启TerserPlugin,但是我们需要进行其他配置,就要重新写了
      new TerserPlugin({
        parallel: threads // 开启多进程
      }),
      //压缩图片
      // 压缩图片
      new ImageMinimizerPlugin({
        minimizer: {
          implementation: ImageMinimizerPlugin.imageminGenerate,
          options: {
            plugins: [
              ['gifsicle', { interlaced: true }],
              ['jpegtran', { progressive: true }],
              ['optipng', { optimizationLevel: 5 }],
              [
                'svgo',
                {
                  plugins: [
                    'preset-default',
                    'prefixIds',
                    {
                      name: 'sortAttrs',
                      params: {
                        xmlnsOrder: 'alphabetical'
                      }
                    }
                  ]
                }
              ]
            ]
          }
        }
      })
    ]
  },
  // devServer: {
  //   host: "localhost", // 启动服务器域名
  //   port: "3000", // 启动服务器端口号
  //   open: true, // 是否自动打开浏览器
  // },
  mode: 'production',
  //源代码映射
  devtool: 'source-map'

3.打包时有可能会出现报错:

Error: Error with 'src\images\1.jpeg': '"C:\Users\86176\Desktop\webpack\webpack_code\node_modules\jpegtran-bin\vendor\jpegtran.exe"'

Error with 'src\images\3.gif': spawn C:\Users\86176\Desktop\webpack\webpack_code\node_modules\optipng-bin\vendor\optipng.exe ENOENT

我们需要安装两个文件到node------modules中才解决

  • jpegtran.exe

需要复制到 node_modules\jpegtran-bin\vendor 下面

jpegtran 官网地址

  • optipng.exe

需要复制到 node_modules\optipng-bin\vendor 下面

OptiPNG 官网地址

相关推荐
疯狂踩坑人15 小时前
【React 19 尝鲜】第一篇:use和useActionState
前端·react.js
毕设源码-邱学长15 小时前
【开题答辩全过程】以 基于VUE的打车系统的设计与实现为例,包含答辩的问题和答案
前端·javascript·vue.js
用户390513321928815 小时前
JS判断空值只知道“||”?不如来试试这个操作符
前端·javascript
海云前端115 小时前
前端面试必问 asyncawait 到底要不要加 trycatch 90% 人踩坑 求职加分技巧揭秘
前端
wuk99816 小时前
梁非线性动力学方程MATLAB编程实现
前端·javascript·matlab
XiaoYu200216 小时前
第11章 LangChain
前端·javascript·langchain
霉运全滚蛋好运围着转17 小时前
启动 Taro 4 项目报错:Error: The specified module could not be found.
前端
cxxcode17 小时前
前端模块化发展
前端
不务正业的前端学徒17 小时前
docker+nginx部署
前端