二、Webpack5 入门到原理(高级优化)

系列文章

一、Webpack5 入门到原理(基础)

1、提升开发体验

1、sourceMap

1、为什么?

为什么要有sourceMap

因为我们在开发环境用webpack编译生成的代码通常情况下是这个样子的,如果在开发环境下面报错了,我们在定位问题的时候是非常困难的,所以我们就需要一个文件来映射源代码与编译后代码之间的关系。

2、是什么?

sourceMap顾名思义,就是对源代码的一个映射,里面包含了源代码和构建后代码的每一行、每一列的映射关系。当构建后的代码运行报错的时候,就可以通过.map文件,从构建后代码出错的位置找到映射后源代码出错的位置,这样就可以让浏览器提示出源代码出错的位置,帮助我们更快的找到错误的位置。

3、怎么用?

通过查看Webpack DevTool 文档open in new window可知,SourceMap 的值有很多种情况.

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

  • 开发模式:cheap-module-source-map

    • 优点:打包编译速度快,只包含行映射
    • 缺点:没有列映射
js 复制代码
module.exports = {
  mode: "development",
  devtool: "cheap-module-source-map",
};
  • 生产模式:source-map

    • 优点:包含行/列映射
    • 缺点:打包编译速度更慢

生产模式也有可能不开启sourceMap,会部生成专门的sourceMap文件

js 复制代码
module.exports = {
  // 其他省略
  mode: "production",
  devtool: "source-map",
};

2、提升打包构建速度

1、HotModuleReplacement

1、为什么?

在没有使用热模块替换功能的时候,当我们修改了项目中一个模块的代码,webpack就会将所有模块重新编译打包,这样速度回很,还会刷新整个页面。

所以我们就需要一个机制,就是当我们修改了某一模块,只需要重新打包编译当前模块,其他模块不变,这样就能大大提升开发环境编译的速度。

2、是什么?

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

3、怎么用?

css 已经经过 style-loader 处理过了,已经有HMR的功能了,此时 js 还不行。

基本配置

js 复制代码
module.exports = {
  // 其他省略
  devServer: {
    host: "localhost", // 启动服务器域名
    port: "3000", // 启动服务器端口号
    open: true, // 是否自动打开浏览器
    hot: true, // 开启HMR功能(只能用于开发环境,生产环境不需要了)
  },
};

js 配置

js 复制代码
// 判断是否支持 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);
  });
}

2、oneOf

1、为什么?

webpack打包时,每个文件都会经过所有的loader处理,就算有test进行匹配,没有进行实际上的处理,但是都需要过一遍,比较慢。

2、是什么?

就是只能匹配上一个loader,剩下的就不进行匹配了。

3、怎么用?

js 复制代码
  // 加载器
  module: {
    rules: [
      {
        oneOf: [
          ... 原有的 rule 规则
        ],
      },
    ],
  },

生产环境也这样配置

3、include/exclude

1、为什么?

开发的时候我们安装的第三方库或者插件,都下载到了node_modules中了,这些依赖包在内部已经生成好了编译后的代码了,所以不在需要webpack对其进行编译。所以我们在对 js 文件进行处理的时候,就需要排除这些node_modules下面的依赖包。

2、是什么?

  • include 包含,只处理 包含的文件
  • exclude 排除,处理 排除文件之外的文件

3、怎么用?

开发环境配置

js 复制代码
  {
    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", // 默认值
    })
  ],

生产环境也是同样的配置

3、Cache

1、为什么?

每次打包的时候 js 文件都要经过 eslint 检查和 babel 编译,比较慢。

我们可以缓存之前 eslint 检查和 babel 编译的结果,这样在第二次构建的时候速度就会更快了。

2、是什么?

eslint 检查和 babel 编译结果进行缓存。

3、怎么用?

js 复制代码

可以观察一下第一次耗时与开启后的耗时

第一次

第二次

还有更多配置参见 babel-loader 文档

4、Thread

1、为什么?

当我们的项目越来越大的时候,打包构建的速度越来越慢的时候,这个时候我们就要想办法提高打包的效率,对于我们来说其实就是提高 js 打包的效率,因为其他的文件数量相对较少。

而处理 js 文件的主要就是 eslint babel Terser 这三个工具,因此我们要提高他们的运行速度。

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

2、是什么?

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

注意:仅适合在特别耗时的操作中使用,因为每个进程启动就有大约为 600ms 左右的开销。

3、怎么用?

启动的进程数就是我们电脑 cpu 的核心数

js 复制代码
const os = require("os");

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

安装 thread-loader

shell 复制代码
yarn add thread-loader

webpack 配置

js 复制代码
const TerserPlugin = require("terser-webpack-plugin");

  // babel-loader
  {
    test: /\.js$/,
    exclude: /node_modules/, // 排除 node_modules 下面的代码不进行编译
    use: [
      {
        loader: "thread-loader",
        options: {
          workers: threads,
        },
      },
      {
        loader: "babel-loader",
        options: {
          cacheDirectory: true,
          cacheCompression: false,
        },
      },
    ],
  },
  
  // eslint
  new ESlintWebpackPlugin({
    ...
    threads,
  }),
  
  // 压缩
  optimization: {
    minimize: true,
    minimizer: [
      // css压缩也可以写到optimization.minimizer里面,效果一样的
      new CssMinimizerPlugin(),
      // 当生产模式会默认开启TerserPlugin,如果我们有其他的配置就要在这里进行重写
      new TerserPlugin({
        parallel: threads, // 开启多进程
      }),
    ],
  },

更多信息参见 优化(Optimization)

因为当前我们的代码内容很少,又因为开启了进程的原因,所以实际上我们的打包时间会变长。所以大项目才使用多进程打包。

开启多进程以后的

可以与之前的进行对比

3、减少代码体积

1、tree shaking

1、为什么?

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

如果没有特殊处理的话我们打包时会引入整个库,但是实际上可能我们可能只用上极小部分的功能。

这样将整个库都打包进来,体积就太大了。

2、是什么?

tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块语法的 静态结构 特性,例如 importexport。这个术语和概念实际上是由 ES2015 模块打包工具 rollup 普及起来的。

注意:它依赖 Es Module

3、怎么用?

我们新建src/js/math.js

js 复制代码
export function square(x) {
  return x * x;
}

export function cube(x) {
  return x * x * x;
}

在 main.js 中使用

js 复制代码
import { cube } from "./js/math";

console.log(cube(5));

然后我们先给webpack.prod.js 配置文件中的 mode 改成 development 然后进行打包

再改回 production再看打包结果,是不是很清晰,只会把用到的代码打包进结果里面。

更多用法请参见 Tree Shaking 文档

webpack production 模式已经默认开启了这个功能。

2、babel

1、为什么?

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

Babel 对一些公共方法使用了非常小的辅助代码,比如 _extend。默认情况下会被添加到每一个需要它的文件中。

你可以引入 Babel runtime 作为一个独立模块,来避免重复引入。

注意 :你必须执行 npm install -D @babel/plugin-transform-runtime 来把它包含到你的项目中,然后使用 npm install @babel/runtime@babel/runtime 安装为一个依赖。

2、是什么?

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

更多信息请查看 文档

3、怎么用?

下载包

js 复制代码
yarn add @babel/plugin-transform-runtime -D

配置

js 复制代码
module: {
  rules: [
    {
        test: /\.js$/,
        exclude: /node_modules/, // 排除 node_modules 下面的代码不进行编译
        use: [
         ...
          {
            loader: "babel-loader",
            options: {
              cacheDirectory: true,
              cacheCompression: false,
              presets: ["@babel/preset-env"],
              plugins: ["@babel/plugin-transform-runtime"],
            },
          },
        ],
      },
  ];
}

更多信息请参见文档 babel-loader

这里说的 babel 产生的辅助代码,目前没有测试出来,什么情况下会产生辅助代码,有了解的小伙伴可以评论一波哦!!!

4、优化代码运行性能

1、code split

1、为什么?

打包代码时会将所有 js 文件打包到一个文件中,体积太大了。我们如果只要渲染首页,就应该只加载首页的 js 文件,其他文件不应该加载。

所以我们需要将打包生成的文件进行代码分割,生成多个 js 文件,渲染哪个页面就只加载某个 js 文件,这样加载的资源就少,速度就更快。

2、是什么?

代码分割(Code Split)主要做了两件事:

  1. 分割文件:将打包生成的文件进行分割,生成多个 js 文件。
  2. 按需加载:需要哪个文件就加载哪个文件

3、怎么用?

代码分割实现方式有不同的方式,为了更加方便体现它们之间的差异,我们会分别创建新的文件来演示

  • 多入口配置

文件目录

arduino 复制代码
├── public
├── src
|   ├── app.js
|   └── main.js
├── package.json
└── webpack.config.js

app.js

js 复制代码
console.log("this is app");

main.js

js 复制代码
console.log("this is main");

运行打包命令

shell 复制代码
yarn build
  • 提取重复代码

如果多入口文件中都引用了同一份代码,我们不希望这份代码被打包到两个文件中,导致代码重复,体积更大。

我们需要提取多入口的重复代码,只打包生成一个 js 文件,其他文件引用它就好。

代码如下,两个入口文件都引用了 math.js

新手可以跟着 视频课程 学习,有基础的直接跟着文章敲一遍感受一下。

相关推荐
浮华似水20 分钟前
简洁之道 - React Hook Form
前端
正小安2 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch4 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光4 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   4 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   4 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web4 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常4 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇5 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr5 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui