七、调试和迁移

本章将进一步探讨迁移和调试,提供对这些主题的广泛概述和详细检查。

迁移是指将内容和项目从早期版本的 Webpack 移动到新版本的过程。我们将特别关注从 Webpack 版本 3 到版本 4 以及从版本 4 到版本 5 的转变。我们还将介绍如何处理不推荐使用的插件,以及如何删除或更新它们。这将包括在使用 Node.js v4 和命令行界面 ( CLI )时查看迁移。

本章将讨论resolve方法以及module.loaders现在如何被module.rules方法取代。它还将涵盖加载程序的链接,包括不再需要或已删除的链接加载程序。

然后,本章将继续探讨调试。调试包括消除复杂软件系统中常见故障和错误的过程。本章将解释常见问题及其解决方案、故障排除、避免这些问题应遵循的最佳实践以及如何查找故障。

本章涵盖的主题如下:

  • 排除故障
  • 热模块更换
  • 添加实用程序
  • 移民

排除故障

调试工具是工作流的核心,尤其是在参与核心复制、编写加载器或任何其他复杂形式的编码时。本指南将带您了解在解决性能缓慢或不可原谅的回溯等问题时最有用的实用程序。这些主要实用程序如下:

  • 通过 Node.js 和 CLI 提供的stats数据
  • Chrome 开发工具通过node-nightly和最新的 Node.js 版本

In Webpack 5, as of the time of writing, there are some known problems; for example, DevTools doesn't support persistent caching and persistent cache files that include absolute paths are not yet portable.

stats数据在调试构建问题、手动筛选数据或使用工具时非常有用。它可用于查找以下内容:

  • 生成错误和警告
  • 每个模块的内容
  • 模块编译和解析统计信息
  • 模块之间的相互关系
  • 任何给定块中包含的模块

另外,官方的网络包分析工具会接受这些数据,并为您可视化。

有时,当控制台语句无法完成工作时,需要更健壮的解决方案。

正如前端开发人员社区普遍认为的那样,Chrome DevTools 在调试应用时是不可或缺的,但它并不止于此。从 Node.js v6.3.0+开始,开发人员可以使用内置的检查标志在 DevTools 中调试 Node.js 程序。

这个简短的演示将利用node-nightly包,该包提供对最新检查能力的访问。这提供了创建断点、调试内存使用问题、在控制台中公开对象等功能:

  1. 首先在全球范围内安装node-nightly包:
npm install --global node-nightly
  1. 现在必须使用命令行运行此包,以完成安装:
node-nightly
  1. 现在,使用node-nightlyinspect标志功能,我们可以开始调试任何 Webpack 项目。需要注意的是npm脚本现在无法运行;相反,完整的node_module路径需要表达为:
node-nightly --inspect ./node_modules/webpack/bin/webpack.js
  1. 输出应该在命令行实用程序窗口中显示如下内容:
Debugger listening on ws://127.0.0.1:9229/c624201a-250f-416e-a018-300bbec7be2c For help see https://nodejs.org/en/docs/inspector
  1. 现在,转到 Chrome 的检查功能(chrome://inspect),任何活动脚本现在都应该可以在Remote Target标题下查看。

单击每个脚本下的inspect链接将为会话中的节点打开专用调试器或开发工具链接,该链接将自动连接请注意 NiM 是 Chrome 的一个方便的扩展,每次你检查时,它会在一个新的选项卡中自动打开开发工具。这对于较长的项目非常有用。

使用inspect-brk标志可能也很有用,它会导致任何脚本的第一条语句中断,从而允许仔细阅读源代码、设置断点以及临时停止和启动进程。这也允许程序员继续向有问题的脚本传递参数;这对于进行并行配置变更可能是有用的。

所有这些都涉及到的一个关键特征,也是本指南前面提到的,是热模块更换 (HMR)的激动人心的主题。它是什么以及如何使用它将在下一节以及教程中介绍。

热模块更换

HMR 可能是网络包中最有用的元素。它允许需要完全刷新的模块的运行时更新。本节将探讨 HMR 的实现,并详细说明它是如何工作的以及为什么它如此有用。

非常重要的是要注意,HMR 不适合也不应该用于生产模式;它应该只用于开发模式。

It's worth noting that, according to the developers, the internal HMR API for plugins will probably change in future updates of Webpack 5.

要启用 HMR,我们首先需要做的是更新我们的webpack-dev-server配置,并使用 Webpack 的内置 HMR 插件。这个特性对生产力非常有用。

移除print.js的入口点也是一个好主意,因为它现在将被index.js模块消耗。

任何使用webpack-dev-middleware而不是webpack-dev-server的人现在都应该使用webpack-hot-middleware包来启用 HMR:

  1. 要开始使用 HMR,我们需要返回到配置文件webpack.config.js。请遵循此处的修改:
  const path = require('path');
  const HtmlWebpackPlugin = require('html-webpack-plugin');
  const CleanWebpackPlugin = require('clean-webpack-plugin');
  const webpack = require('webpack');

  module.exports = {
    entry: {
       app: './src/index.js',
       print: './src/print.js'
       app: './src/index.js'
    },
    devtool: 'inline-source-map',
    devServer: {
      contentBase: './dist',
 hot: true
    },
    plugins: [
      // new CleanWebpackPlugin(['dist/*']) for < v2 versions of 
      // CleanWebpackPlugin
      new CleanWebpackPlugin(),
      new HtmlWebpackPlugin({
        title: 'Hot Module Replacement'
      }),
      new webpack.HotModuleReplacementPlugin()
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
  };

请注意前面代码中的新增内容——将hot:选项设置为true并且添加了'Hot Module Replacement'插件——以及在 HMR 的配置中创建新的网络包插件。所有这些都应该利用插件和 HMR。

  1. 命令行可以通过以下命令修改webpack-dev-server配置:
webpack-dev-server --hot 

这将允许对捆绑的应用进行临时更改。

  1. index.js现在应该更新,这样当检测到print.js发生变化时,Webpack 可以接受更新的模块。在以下示例中,这些更改以粗体显示;我们只是用一个import表达式和函数来展示print.js文件:
  import _ from 'lodash';
 import printMe from './print.js';

  function component() {
    const element = document.createElement('div');
    const btn = document.createElement('button');

    element.innerHTML = _.join(['Hello', 'Webpack'], ' ');

    btn.innerHTML = 'Click me and check the console!';
    btn.onclick = printMe;

    element.appendChild(btn);

    return element;
  }

  document.body.appendChild(component());

 if (module.hot) {
    module.hot.accept('./print.js', function() {
      console.log('Accepting the updated printMe module');
      printMe();
    })
  }

如果您在print.js中对控制台日志进行更改,将在浏览器控制台中看到以下输出。强制性的printMe()按钮暂时丢失,但可以在以后更新:

  export default function printMe() {
    console.log('This got called from print.js!');
    console.log('Updating print.js...')
  }

查看控制台窗口会显示以下打印输出:

[HMR] Waiting for update signal from WDS...
main.js:4395 [WDS] Hot Module Replacement enabled.
  2main.js:4395 [WDS] App updated. Recompiling...
  main.js:4395 [WDS] App hot update...
  main.js:4330 [HMR] Checking for updates on the server...
  main.js:10024 Accepting the updated printMe module!
  0.4b8ee77.hot-update.js:10 Updating print.js...
  main.js:4330 [HMR] Updated modules:
  main.js:4330 [HMR]  - 20

前面的块显示 HMR 正在等待来自网络包的信号,如果 HMR 事件发生,命令行实用程序可以执行自动包修改。当保持打开时,命令行窗口也将显示这一点。Node.js 有一个可以以类似方式使用的 API。

将开发服务器与节点应用编程接口结合使用

使用 DevServer 和 Node.js API 时,不要把dev server选项放在 Webpack 配置对象上;相反,它应该始终作为辅助参数传递。

这里,DevServer 只是指在开发模式下使用 Webpack,而不是watchingproduction模式。要将开发服务器与节点. js 应用编程接口一起使用,请执行以下步骤:

  1. 该功能被放置在webpack.config.js文件中,如下所示:
new WebpackDevServer(compiler, options)

要启用 HMR,必须首先修改配置对象,以包括 HMR 入口点。webpack-dev-server包包括一个名为addDevServerEntryPoints的方法,可以用来做到这一点。

  1. 下面是一个使用dev-server.js的简短示例:
const webpackDevServer = require('webpack-dev-server');
const webpack = require('webpack');

  const config = require('./webpack.config.js');
  const options = {
    contentBase: './dist',
    hot: true,
    host: 'localhost'
};

webpackDevServer.addDevServerEntrypoints(config, options);
const compiler = webpack(config);
const server = new webpackDevServer(compiler, options);

server.listen(5000, 'localhost', () => {
  console.log('dev server listening on port 5000');
});

HMR 可能很难。为了演示这一点,在我们的示例中,单击示例网页中创建的按钮。很明显,控制台正在打印旧功能。这是因为事件处理程序绑定到了原始函数。

  1. 要解决这个问题以便与 HMR 一起使用,必须使用module.hot.accept将绑定更新到更新的函数。使用index.js见下例:
  import _ from 'lodash';
  import printMe from './print.js';

  function component() {
    const element = document.createElement('div');
    const btn = document.createElement('button');

    element.innerHTML = _.join(['Hello', 'Webpack'], ' ');

    btn.innerHTML = 'Click me and view the console!';
    btn.onclick = printMe;  // onclick event is bind to the 
                            // original printMe function

    element.appendChild(btn);

    return element;
  }

  document.body.appendChild(component());
 let element = component(); // Store the element to re-render on 
                             // print.js changes
  document.body.appendChild(element);

  if (module.hot) {
    module.hot.accept('./print.js', function() {
      console.log('Accepting the updated printMe module!');
      printMe();
      document.body.removeChild(element);
 element = component(); 
      document.body.appendChild(element);
    })
  }

作为解释,btn.onclick = printMe;是一个onclick事件,它与最初的printMe功能相关联。let element = component();将存储元素,以便在对print.js进行任何更改时重新渲染。另外,请注意element - component();语句,它将重新呈现组件并更新点击处理程序。

这只是你可能遇到的陷阱的一个例子。幸运的是,Webpack 提供了很多加载器,其中一些将在后面讨论,这使得 HMR 的问题少了很多。现在让我们看看 HMR 和样式表。

HMR 和样式表

style-loader的帮助下,将 HMR 和 CSS 一起使用会更简单一些。这个加载器使用module.hot.accept在更新 CSS 依赖关系时修补样式标签。

在下一阶段的实际示例中,我们将采取以下步骤:

  1. 首先,使用以下命令安装两台装载机:
npm install --save-dev style-loader css-loader
  1. 接下来,更新配置文件webpack.config.js,以利用加载器:
  const path = require('path');
  const HtmlWebpackPlugin = require('html-webpack-plugin');
  const CleanWebpackPlugin = require('clean-webpack-plugin');
  const webpack = require('webpack');

  module.exports = {
    entry: {
      app: './src/index.js'
    },
    devtool: 'inline-source-map',
    devServer: {
      contentBase: './dist',
      hot: true
    },
    module: {
      rules: [
        {
 test: /\.css$/,
          use: ['style-loader', 'css-loader']
        }
      ]
    },
    plugins: [
      // new CleanWebpackPlugin(['dist/*']) for < v2 versions of 
        // CleanWebpackPlugin
      new CleanWebpackPlugin(),
      new HtmlWebpackPlugin({
        title: 'Hot Module Replacement'
      }),
      new webpack.HotModuleReplacementPlugin()
    ],
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist')
    }
  };

热加载样式表就像将它们导入模块一样简单,正如您在前面的配置示例和接下来的目录结构示例中以粗体显示的文本所示。

  1. 确保按照以下结构组织项目文件和目录,如图所示:
 webpack5-demo
  | - package.json
  | - webpack.config.js
  | - /dist
    | - bundle.js
  | - /src
    | - index.js
    | - print.js
    | - styles.css
  1. 通过添加body样式来附加样式表,使与其关联的文档正文的背景成为蓝色。使用styles.css文件执行此操作:
body {
  background: blue;
}
  1. 之后,我们需要确保内容被正确加载到index.js文件,如下所示:
  import _ from 'lodash';
  import printMe from './print.js';
 import './styles.css'; 
  function component() {
    const element = document.createElement('div');
    const btn = document.createElement('button');

    element.innerHTML = _.join(['Hello', 'Webpack'], ' ');

    btn.innerHTML = 'Click me and check the console!';
    btn.onclick = printMe;  // onclick event is bind to the 
                            // original printMe function

    element.appendChild(btn);

    return element;
  }

  let element = component();
  document.body.appendChild(element);

  if (module.hot) {
    module.hot.accept('./print.js', function() {
      console.log('Accepting the updated printMe module!');
      document.body.removeChild(element);
      element = component(); // Re-render the "component" to update 
                             // the click handler
      document.body.appendChild(element);
    })
  }

现在当body标签背景类的样式变为红色时,颜色的变化要立即注意,不需要页面刷新,说明热编码的直播性质。

  1. 您现在应该使用styles.css对背景进行这些更改:
  body {
    background: blue;
    background: red;
  }

这以一种非常简单的方式演示了如何进行实时代码编辑。这只是一个简单的例子,但它是一个很好的介绍。现在,让我们前进到更棘手的事情——加载器和框架。

其他加载器和框架

我们前面提到的众多可用的加载器使得 HMR 能够更流畅地与各种框架和库交互。下面介绍一些更有用的方法:

  • 棱角分明的 HMR :对你的主NgModule文件进行简单的修改就可以完全控制 HMR 的应用接口(不需要加载器)。
  • 反应热加载器:该加载器实时调整反应组件。
  • Elm 热网络包加载器:这个加载器支持 Elm 编程语言的 HMR。
  • Vue 装载机:该装载机支持 HMR 对 Vue 零部件进行开箱。

我们已经浏览了 HMR 和相关的加载器和框架,但是我们还没有讨论的东西——但是与我们到目前为止所讨论的内容相关——是添加一个实用程序。我们将在下一节中讨论这个问题。

添加实用程序

在本文中,实用程序是指负责一组相关功能的文件或模块,旨在优化、分析、配置或维护。这与应用相反,应用倾向于执行直接针对用户的任务或任务集。因此,在这种情况下,您可能会认为实用程序是前端的一部分,但对于后台任务来说,它隐藏在后台。

首先,向示例项目添加一个实用程序文件。在src/math.js中执行此操作,以便导出两个函数:

  1. 第一步是组织项目目录:
webpack5-demo
|- package.json
|- webpack.config.js
|- /dist
  |- bundle.js
  |- index.html
|- /src
  |- index.js
  |- math.js
|- /node_modules

项目树显示了你的文件和文件夹应该是什么样子,你会注意到一些新的添加,比如math.js

  1. 现在让我们仔细看看math.js是如何编码的:
export function square(x) {
  return x * x;
}

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

您会看到它们是易于导出的函数;他们将在以后脱颖而出。

  1. 此外,确保在配置文件webpack.config.js中将网络包模式设置为development,这可以确保包不会缩小:
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
  },
  mode: 'development',
  optimization: {
    usedExports: true
  }
};
  1. 有了这些,我们接下来更新入口脚本,以利用这些新方法中的一个,并为简单起见删除lodash。这是使用src/index.js文件完成的:
  import _ from 'lodash';
  import { cube } from './math.js';

  function component() {
    const element = document.createElement('div');
    const element = document.createElement('pre');

    // Lodash, now imported by this script
    element.innerHTML = _.join(['Hello', 'webpack'], ' ');
    element.innerHTML = [
      'Hello Webpack!',
      '5 cubed is equal to ' + cube(5)
    ].join('\n\n');

    return element;
  }

  document.body.appendChild(component());

从前面的例子中,我们可以看到square方法不是从src/math.js模块导入的。这个函数可以被认为是死代码——本质上是一个可以丢弃的未使用的导出。

  1. 现在,再次运行npm构建来检查结果:
npm run build
  1. 一旦完成,找到dist/bundle.js文件——它应该在第 90-100 行的某个地方。在文件中搜索类似于以下示例的代码,以遵循此过程:
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
  'use strict';
  /* unused harmony export square */
  /* harmony export (immutable) */ __webpack_exports__['a'] = cube;
  function square(x) {
    return x * x;
  }

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

在本例中,您现在将看到一条unused harmony export square注释。请注意,它没有被导入。然而,它目前仍包含在捆绑包中。

  1. ECMA 脚本并不完美,所以向 Webpack 的编译器提供关于代码纯度的提示是很重要的。packages.json属性将有助于这些副作用:
{
  "name": "your-project",
  "sideEffects": false
}

前面的代码不包含副作用;因此,属性应标记为false以指示 Webpack 移除未使用的导出。

在这种情况下,副作用被定义为在导入时执行特殊行为的脚本,而不是公开多个导出等等。一个例子是聚合填充,它影响全局项目,通常不提供导出。

  1. 在代码有副作用的情况下,可以提供数组作为补救措施,如下例所示:
{
  "name": "your-project",
  "sideEffects": [
    "./src/some-side-effectful-file.js"
  ]
}

本例中的数组接受相对和绝对模式。

  1. 请注意,任何导入的文件都会受到树摇动的影响。例如CSS-loader如果用于导入一个 CSS 文件,则必须将其添加到副作用列表中,以防止在生产模式下被无意中丢弃:
{
  "name": "your-project",
  "sideEffects": [
    "./src/some-side-effectful-file.js",
    "*.css"
  ]
}
  1. 最后,也可以从module.rules配置选项中设置sideEffects。因此,我们已经使用importexport语法将待删除的死代码排队,但是我们仍然需要将其从包中删除。为此,将mode配置选项设置为production。这是通过附加配置文件webpack.config.js来完成的,如下所示:
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  mode: 'development',
  optimization: {
    usedExports: true
  }
  mode: 'production'
};

--optimize-minimize标志也可以用来启用TerserPlugin。既然我们已经明白了这一点,我们可以运行另一个npm构建。

现在很明显,整个包被缩小和损坏了。细看发现square功能缺失;相反,您有一个损坏的多维数据集函数:

function r(e){return e*e*e}n.a=r

随着缩小和树摇动,我们的包现在小了几个字节!虽然在这个人为的例子中,这看起来不太像,但是在处理具有复杂依赖树的大型应用时,树摇动会导致包大小的显著减少。

ModuleConcatenationPlugin是树木摇动工作所需要的。使用mode: "production"添加。如果不使用,记得手动添加ModuleConcatenationPlugin

要充分利用树木摇动,必须完成以下任务:

  • 使用 ES2015 模块语法(即importexport)。
  • 确保没有编译器将您的 ECMAScript 语法转换为 CommonJS 模块。
  • sideEffects属性添加到您的package.json文件中。
  • 使用production配置选项启用各种优化,包括树摇动和缩小。

当涉及到树摇动时,将您的应用视为一棵树通常会有所帮助。在这个类比中,源代码和库将分别是绿叶和树的活的部分。然而,死代码将代表枯叶。摇动那棵树将删除现在失效的代码。

这一点在迁移时尤其相关,值得考虑。考虑到不同版本的 Webpack 之间代码贬低的变化,在尝试这样的事情之前,让您的软件以最佳状态工作是很重要的。这将防止开发非常困难的 bug。

移民

迁移是指从一个版本的 Webpack 迁移到另一个版本。这通常涉及升级到最新版本。作为一名网络开发人员,在处理其他软件时,你可能已经知道这是一件棘手的事情,所以这一部分将是一个重要的部分,也许你可以在未来的开发中参考。

为了提供更详细的指南,包含了从 Webpack 3.0 迁移到 Webpack 4.0 的迁移策略,所以在继续第 5 版之前,让我们先来看一下。

从版本 3 迁移到版本 4 时的先决条件

在我们开始将我们的项目从 Webpack 版本 3 迁移到版本 4 之前,有几个先决条件需要检查。这些包括以下内容:

  • Node.js
  • 命令行界面
  • 插件
  • 模式

对于使用 Node.js 版本 4 或更低版本的开发人员来说,升级到 Node.js 版本 6 或更高版本是必要的。就命令行而言,命令行界面已经转移到一个单独的包中,称为webpack-cli。您需要在使用 Webpack 4 之前安装它。

更新插件时,很多第三方插件需要升级到最新版本才能兼容,所以请注意这一点。仔细阅读您的项目以找到需要更新的项目也是一个好主意。此外,请务必将新模式选项添加到您的配置中:

  1. 首先,根据配置类型,使用webpack.config.js将配置中的模式设置为productiondevelopment,如以下代码片段所示:
module.exports = {
    mode: 'production',
}

还有一种替代方法,可以通过使用 CLI 传递模式来实现,例如以下示例:

--mode production

前面的例子显示了通过命令行为production模式生成的任何 Webpack 命令的后半部分。以下示例显示了development模式的相同情况:

--mode development
  1. 下一步是删除不推荐使用的插件;插件应该从您的配置文件中删除,因为它们在生产模式下是默认的。以下示例将向您展示如何在webpack.config.js中进行编辑:
module.exports = {
 plugins: [
   new NoEmitOnErrorsPlugin(),
   new ModuleConcatenationPlugin(),
   new DefinePlugin({ "process.env.NODE_ENV":
     JSON.stringify("production") })
   new UglifyJsPlugin()
 ],
}

下面的示例向您展示了这在开发模式下是如何工作的。请注意,插件在开发模式下是默认的,再次使用webpack.config.js:

module.exports = {
 plugins: [
   new NamedModulesPlugin()
 ],
}
  1. 如果这是正确的,你会看到折旧插件已经被删除。您的配置文件, webpack.config.js ,应该如下所示:
module.exports = {
 plugins: [
   new NoErrorsPlugin(),
   new NewWatchingPlugin()
 ],
}

此外,CommonChunkPlugin在此过程中被移除,在 Webpack 4.0 中提供了optimization.splitChunks选项作为替代。

如果您正在从统计数据生成 HTML,现在可以使用optimization.splitChunks.chunks: "all"——这是大多数情况下的最佳配置。

关于import()和 CommonJS 也有一些工作要做。当使用import()加载任何非 ESM 脚本时,结果在 Webpack 4 中发生了变化:

  1. 现在,您需要访问默认属性来获取module.exports的值。请参见此处的non-esm.js文件,了解这一点:
module.exports = {
      sayHello: () => {
       console.log('Hello World');
     }
 };

这是一个简单的 JavaScipt 函数,您可以复制它的内容来跟随演示,看看结果是如何变化的。

  1. 下一个文件是 example.js 文件。它可以被称为任何你想要的,你可以执行任何你想要的行动。在这个例子中,它是一个简单的sayHello();函数:
function sayHello() {
 import('./non-esm.js').then(module => {
  module.default.sayHello();
 });
}

这些块展示了如何用 CommonJS 编写简单的函数。您应该将此约定应用于现有代码,以确保它不会被破坏。

  1. 使用自定义加载器转换.json文件时,现在需要在webpack.config.js中更改模块类型:
module.exports = {
 rules: [
  {
    test: /config\.json$/,
    loader: 'special-loader',
    type: 'javascript/auto',
    options: {...}
  }
 ]
};
  1. 即使在使用json-loader时,也可以将其移除;请参见以下示例:
module.exports = {
  rules: [
   {
     test: /\.json$/,
     loader: 'json-loader'
   }
  ]
};

完成后,所有必需的迁移先决条件都将完成。下一步是自动更新过程,它内置于 Webpack 中。

从版本 4 迁移到版本 5 时的先决条件

本指南旨在帮助您在直接使用 Webpack 时迁移到 Webpack 5。如果您使用更高级别的工具来运行 Webpack,请参考此工具了解迁移说明。

第 1 章对 Webpack 5 的介绍所述,Webpack 5 需要 Node.js 版本 10.13.0 (LTS)才能运行;但是,使用较新的版本可以更好地提高构建性能:

  1. 您应该确保通过作者提供的副本来检查相关插件和加载器的个别迁移说明,尤其是在跨主要版本升级时。在这种情况下,请注意生成过程中的不赞成警告。您可以通过这种方式调用 Webpack 来获取拒绝警告的堆栈跟踪,以找出哪个插件和加载器负责。Webpack 5 将删除所有不推荐使用的功能。若要继续,在生成过程中不应出现任何贬低警告。
  2. 确保您使用的是统计数据中的入口点信息。如果您正在使用HtmlWebpackPlugin,则不需要遵循此步骤。

对于包含静态 HTML 或以其他方式创建静态 HTML 的构建,您必须确保使用 stats JSON 文件的入口点来生成任何脚本并链接任何 HTML 标记。如果这是不可能的,您应该避免将splitChunks.chunks键设置为all并将任何设置放在splitChunks.maxSize键上。然而,这仅仅是一种变通方法,作为一种解决方案可能不太理想。

  1. 请务必将模式设置为productiondevelopment,以确保设置了相应的模式默认值。
  2. 此外,如果您正在使用以下选项,请务必将其更新为较新的版本:
optimization.hashedModuleIds: true => optimization.moduleIds:
  'hashed'
optimization.namedChunks: true => optimization.chunkIds: 'named'
optimization.namedModules: true => optimization.moduleIds: 'named'
NamedModulesPlugin => optimization.moduleIds: 'named'
NamedChunksPlugin => optimization.chunkIds: 'named'
HashedModulesPlugin => optimization.moduleIds: 'hashed'
optimization.occurrenceOrder: true => optimization: { chunkIds:
   'total-size', moduleIds: 'size' }
optimization.splitChunks.cacheGroups.vendors =>
   optimization.splitChunks.cacheGroups.defaultVendors
  1. 接下来,我们需要测试 Webpack 5 与您的应用的兼容性。为此,请为您的 Webpack 4 配置设置以下选项。如果这在 Webpack 4 中没有任何构建错误,我们将知道是否有任何连续的错误是版本 5 独有的。这听起来可能很乏味,但它消除了递归故障查找:
module.exports = {
 // ...
   node: {
     Buffer: false,
     process: false
   }
 };

上述选项从网络包 5 的配置中删除,默认设置为false。请确保在您的 Webpack 4 测试版本中这样做,但是在您的版本 5 版本中,它们需要再次删除。

  1. 接下来是一个简单快捷的命令行执行来升级您的 Webpack 版本:
npm: npm install webpack@next --dev
Yarn: yarn add webpack@next -D

现在,我们需要清理我们的配置。

It is advised that you change the [hash] placeholder in your configuration to [contenthash]. This has been proven to be more effective and can help to shore up your code.

如果您碰巧正在使用pnp-webpack-plugin,它现在在 Webpack 的第 5 版中被默认支持,但是它现在需要从您的配置模式中删除。

IgnorePlugin现在接受一个选项对象,因此如果您将它用作正则表达式,则需要重写它,如下所示:

new IgnorePlugin({ resourceRegExp: /regExp/ }).

对于通过import使用 WASM 的开发人员,您应该通过将experiments.syncWebAssembly变量设置为true来启用不推荐使用的规范。这将在 Webpack 5 中设置与 Webpack 4 中相同的行为。迁移到网络包 5 后,您现在应该更改实验值,以使用 WASM 的最新规范— { asyncWebAssembly: true, importAsync: true }

当使用自定义配置将name值替换为idHint时,您也应该小心。

在 Webpack 5 中,不支持从 JSON 模块进行命名导出,您将收到一条警告。要以这种方式导入任何内容,您应该使用const[version]=package;package.json开始导入。

  1. 现在清理构建代码是很好的做法。部分这意味着在使用const compiler =webpack(...);时关闭编译器。这是通过compiler.close();完成的。

运行构建后,可能会出现一些问题。例如,模式验证可能会失败,网络包可能会因错误而退出,或者可能会出现构建错误、构建警告或弃用警告。

在每种情况下,都会有一个中断的变更说明或一个错误消息,其中包含通过命令行提供的指令,就像往常一样。

在反对警告的情况下,暂时可能会有很多,因为 Webpack 5 是新的,插件需要时间来赶上核心变化。它们应该被忽略,直到每个版本都退出测试,这是一个很好的实践。

You can hide deprecation warnings by running the node with the --no-deprecation flag—for example, node --no-deprecation.

插件和加载器贡献者应该遵循反对消息中的警告建议来改进他们的代码。

如有必要,您可能还想关闭运行时代码中的 ES2015 语法。默认情况下,Webpack 的运行时代码使用 ES2015 语法来构建更小的包。如果您的构建目标是不支持该语法的环境,例如 IE 11,您将需要设置output.ecmaVersion: 5以恢复到 ES5 语法。

当向上迁移时,处理遗留问题将是最大的障碍,这个规则并不仅限于 Webpack 5。Webpack 5 有一些功能,将使传统平台用户的体验更加可口。在项目规划中需要考虑的一个方法是持久缓存。

启用持久缓存

当然,缓存是数据的中间存储,可以缩短加载时间并提高性能。持久缓存在数据库驱动的项目中非常常见,在这种项目中,从数据库中提取的数据被缓存,这样用户就可以拥有早期版本的副本。这样就可以一次全部加载,而不会对数据库造成太大的需求,因为数据传递的速度比基于服务器的文件条目要慢。

使用 Webpack 5,应用可以利用相同的操作,并在构建发生变化时提高用户的加载速度。

首先,请注意,默认情况下不启用持久缓存。你必须选择使用它。这是因为 Webpack 5 更看重安全性而不是性能。启用即使少量提高性能但以任何小的方式破坏代码的功能可能不是最好的主意。至少在默认情况下,该功能应该保持禁用状态。

默认情况下,序列化和反序列化可以工作;但是,开发人员可能会遇到缓存失效的问题。

缓存失效是指应用中的某些内容发生了有意的变化,比如开发人员更改了文件的内容;在这种情况下,Webpack 会认为旧内容的缓存是无效的。

Webpack 5 通过跟踪所使用的每个模块的fileDependenciescontextDependenciesmissingDependencies来做到这一点。Webpack 然后由此创建一个文件系统示意图。然后将文件系统与记录的副本进行交叉引用,这又会触发该模块的重建。

然后,输出包的缓存条目有一个为其生成的标记;这本质上是所有贡献者的杂凑。标签和缓存条目之间的匹配指示了可以由 Webpack 用于捆绑的内容。

Webpack 4 对内存缓存使用了相同的过程,除非启用持久缓存,否则它将在 Webpack 5 中工作,无需额外的配置。

当您使用npm升级加载程序或插件、更改配置或更改要在配置中读取的文件时,或者当升级配置中使用的依赖项时,当传递差异命令行参数以运行构建时,或者当您有自定义构建脚本并对其进行更改时,您还需要使缓存条目无效。

由于 Webpack 5 无法处理这些现成的异常,为了确保应用的完整性,持久缓存成为了一个可选的安全特性。

更新网络包

必须采取许多步骤来确保 Webpack 的更新行为正确。与我们的示例相关的步骤如下:

  • 升级并安装。
  • 将模式添加到您的配置文件中。
  • 加叉格。
  • 手动更新相关插件、加载器和实用程序。
  • 重新配置uglify
  • 跟踪任何进一步的错误并进行更新。

让我们详细检查每一步,并探究命令行到底发生了什么。这会帮助你更好地理解程序:

  1. 我们首先要做的就是升级 Webpack 并安装webpack-cli。这是在命令行中完成的,如下所示:
yarn add webpack
yarn add webpack-cli
  1. 前面的例子显示了如何使用yarn来实现这一点;它还会进行版本检查。这也应该在package.json文件中可见:
...
"webpack": "^5.0.0",
"webpack-cli": "^3.2.3",
...
  1. 完成后,应在webpack.config.dev.jswebpack.config.prod.js中添加相应的模式。参见以下webpack.config.dev.js文件:
module.exports = {
mode: 'development',

生产配置也做了类似的事情,因为这里每个模式有两个配置文件。以下是webpack.config.prod.js文件的内容:

module.exports = {
mode: 'production',

我们正在处理两个版本——旧版本(3)和新版本(4)。如果这是手动完成的,您可能会首先制作一个原始版本的。术语分叉指的是通常与此操作相关联的图标,它表示一条线从另一条线分开,以显示为一个分叉的分叉。因此,术语分叉已经意味着颠覆。分叉检查器将自动检查每个版本中需要作为操作一部分进行更新的差异。

  1. 现在,返回命令行添加以下分叉检查器:
add fork-ts-checker-notifier-webpack-plugin
yarn add fork-ts-checker-notifier-webpack-plugin --dev

package.json文件中应看到以下内容:

...
"fork-ts-checker-notifier-webpack-plugin": "^1.0.0",
...

前面的代码块显示分叉检查器已经安装。

  1. 现在,我们需要用命令行更新html-webpack-plugin:
yarn add html-webpack-plugin@next

package.json现在应该显示如下:

"html-webpack-plugin": "^4.0.0-beta.5",

现在,我们需要调整webpack.config.dev.jswebpack.config.prod.js文件中的插件顺序。

  1. 您应该采取这些步骤来确保HtmlWebpackPlugin先于 InterpolateHtmlPluginInterpolateHtmlPlugin被声明,如下例所示:
plugins: [
 new HtmlWebpackPlugin({
   inject: true,
   template: paths.appHtml
 }),
 new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
  1. 另外,请务必在命令行中更新ts-loaderurl-loaderfile-loader:
yarn add url-loader file-loader ts-loader
  1. package.json文件保存了关于所用版本的信息,就前面提到的加载器而言,应该如下所示:
"file-loader": "^1.1.11",
"ts-loader": "4.0.0",
"url-loader": "0.6.2",

如果您正在使用 React ,那么您将需要更新开发工具,如下所示:

yarn add react-dev-utils

同样,package.json文件将保存正在使用的 React 实用程序的版本信息:

"react-dev-utils": "6.1.1",

extract-text-webpack-plugin应替换为 mini-css-extract-plugin

  1. 注意在添加和配置mini-css-extract-pluginextract-text-webpack-plugin应该一起删除:
yarn add mini-css-extract-plugin
  1. 带有插件版本设置的package.json文件应该如下例所示:
"mini-css-extract-plugin": "^0.5.0",
Config:
  1. 完成所有这些之后,我们应该看看生产模式配置。这是通过以下webpack.config.prod.js文件完成的:
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
plugins: [
 ...
 new MiniCssExtractPlugin({
   filename: "[name].css",
   chunkFilename: "[id].css"
 }),
 ...
 ],
 module: {
   rules: [
    {
     test: /\.css$/,
     use: [
     {
       loader: MiniCssExtractPlugin.loader,
       options: {
       // you can specify a publicPath here
       // by default it use publicPath in webpackOptions.output
       publicPath: '../'
     }
    },
    "css-loader"
   ]
 },

我们可以看到版本之间webpack.config.prod.js的一些差异。前面介绍了版本 4 中的配置格式。

  1. 接下来,确保使用命令行和package.json文件更新和重新配置uglifyjs-webpack-plugin:
yarn add uglifyjs-webpack-plugin --dev
  1. 为了谨慎起见,我们也将在这里显示uglify插件的版本设置。使用package.json应用这些:
"uglifyjs-webpack-plugin": "^2.1.2"
 Config:
  1. 下一步也是最后一步是使用webpack.config.prod.js配置生产模式:
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
  ...
  optimization: {
    minimizer: [new UglifyJsPlugin()],
 },

一旦这些都完成了,您就应该完成更新过程了。但是,您可能会得到一个唯一的折旧错误,这意味着您需要使用错误消息跟踪这些错误,然后根据需要更新任何进一步的 Webpack 插件。如果您使用的是自定义插件或加载程序,情况尤其如此。

摘要

本章深入探讨了代码调试,并讨论了 HMR 和其他调试技术。您现在应该已经牢牢掌握了哪些工具和实用程序可以用来增强调试过程,包括使用node nightly来执行代码检查。然后我们深入研究了 HMR,这是网络包的一个显著而令人兴奋的特性。我们看到了如何对模块和样式表进行实时编辑,甚至涵盖了迁移问题。然后我们继续添加实用程序,这是任何升级的重要部分。从那里,我们带您完成了版本迁移,即从版本 3 到版本 4,以及后续步骤。此外,我们向您展示了如何从版本 4 迁移到版本 5。本节以一个关于将命令行升级更新为手动修改一些更复杂元素的冗长教程结束。

现在,您应该对自己的调试和升级技能充满信心,这将为下一章打下坚实的基础。在下一章中,我们将进入一些繁重的实时编码、定制和手动捆绑,这无疑会让你兴奋不已!

进一步阅读

本章涵盖了一些复杂的问题,通过进一步阅读可以更好地理解。以下是主题列表,以及在本章前面提到的相应内容的相关位置:

  • 调试优化救助
  • 问题 6074-为sideEffects添加对更复杂选择器的支持

问题

现在,试着回答以下与本章相关的一些问题。你可以在本书后面的评估部分找到答案:

  1. HMR 代表什么?
  2. 反应热加载器是做什么的?
  3. Webpack 通过什么接口更新?
  4. Node v6.3.0+的什么特性允许通过 Chrome DevTools 进行调试?
  5. 从 Webpack 版迁移到 4 版,使用自定义加载器转换.json文件时,还必须改变什么?
  6. 副作用列表如何帮助开发?
  7. 在生产模式下,不推荐使用的插件应该从哪里删除?**