Webpack が開始されると、設定されたエントリから開始され、ファイル内のインポート ステートメントが解析され、再帰的に解析されます。この記事では、Webpack の最適化に関する知識を中心に、さらに詳しく最適化する方法を紹介します。ぜひ参考にしてください。
import ステートメントに遭遇すると、Webpack は次の 2 つのことを行います:
1. import ステートメントに従って、インポートされる対応するファイルを検索します。たとえば、require('react') import ステートメントに対応するファイルは ./node_modules/react/react.js で、require('./util') に対応するファイルは ./util.js です。
2. インポートするファイルの見つかったサフィックスに従って、設定内のローダーを使用してファイルを処理します。たとえば、ES6 を使用して開発された JavaScript ファイルは、babel-loader を使用して処理する必要があります。
上記2つはファイルの処理としては非常に速いですが、プロジェクトが大きくなるとファイル数も非常に多くなり、ビルド速度が遅いという問題が露呈します。
上記の 2 つは避けることはできませんが、速度を上げるためには最小限に抑える必要があります。
以下では、最適化する方法を一つずつ紹介していきます。
ローダー構成の最適化
ローダーによるファイルの変換操作には時間がかかるため、ローダーで処理できるファイルをできるだけ少なくする必要があります。
2-3 モジュールでは、ローダーを使用するときに、テスト、インクルード、除外の 3 つの構成項目を使用して、ローダーがルールを適用したいファイルをヒットできることを紹介しました。
ローダーによって処理されるファイルをできるだけ少なくするために、 include を使用して、処理する必要があるファイルのみをターゲットにすることができます。
Babel-loader を設定するときに、ES6 を使用するプロジェクトを例に挙げます。
module.exports = { module: { rules: [ { // 如果项目源码中只有 js 文件就不要写成 /\.jsx?$/,提升正则表达式性能 test: /\.js$/, // babel-loader 支持缓存转换出的结果,通过 cacheDirectory 选项开启 use: ['babel-loader?cacheDirectory'], // 只对项目根目录下的 src 目录中的文件采用 babel-loader include: path.resolve(__dirname, 'src'), }, ] }, };
Loader を設定するときに、include を通じてヒット範囲を絞りやすくするために、プロジェクトのディレクトリ構造を適切に調整できます。
resolve.modules 設定の最適化
2-4 Resolve で紹介された、resolve.modules は、Webpack がサードパーティ モジュールを検索するディレクトリを設定するために使用されます。
resolve.modules のデフォルト値は ['node_modules'] です。これは、まず現在のディレクトリの ./node_modules ディレクトリに移動して、探しているモジュールが見つからない場合は、上位レベルに移動することを意味します。ディレクトリ ../node_modules を検索します。見つからない場合は、../../node_modules などを検索します。これは、Node.js のモジュール検索メカニズムとよく似ています。
インストールされたサードパーティ モジュールがすべてプロジェクト ルート ディレクトリの ./node_modules ディレクトリに配置されている場合、デフォルトの方法でレイヤごとに検索する必要はありません。サードパーティ モジュールを保存する絶対パスを指定できます。構成は次のとおりです:
module.exports = { resolve: { // 使用绝对路径指明第三方模块存放的位置,以减少搜索步骤 // 其中 __dirname 表示当前工作目录,也就是项目根目录 modules: [path.resolve(__dirname, 'node_modules')] }, };
solve.mainFields 構成を最適化します
2-4 Resolve で紹介された、resolve.mainFields は、サードパーティ モジュールが使用するエントリ ファイルを構成するために使用されます。
インストールされたすべてのサードパーティ モジュールには、このモジュールのプロパティを記述するために使用される package.json ファイルがあり、いくつかのフィールドはエントリ ファイルの場所を記述するために使用され、どのフィールドが説明として使用されるかを構成するために使用されます。エントリーファイルの。
エントリーファイルを記述するフィールドが複数存在できる理由は、一部のモジュールは複数の環境で同時に使用でき、動作環境ごとに異なるコードを使用する必要があるためです。
isomorphic-fetch を例に挙げると、これはフェッチ API の実装ですが、ブラウザーと Node.js 環境の両方で使用できます。
その package.json には 2 つのエントリ ファイル説明フィールドがあります:
{ "browser": "fetch-npm-browserify.js", "main": "fetch-npm-node.js" }
isomorphic-fetch は、ブラウザーではネイティブ fetch または XMLHttpRequest 実装を介してフェッチ API の実装メカニズムが異なるため、異なる実行環境で異なるコードを使用します。 、http モジュールを通じて Node.js に実装されます。
resolve.mainFields のデフォルト値は、現在のターゲット設定に関連しています。対応関係は次のとおりです。
ターゲットが Web または Webworker の場合、値は ["browser", "module", "main" です。 "]
ターゲットが他の状況の場合、値は ["module", "main"] になります
例として web に等しいターゲットを取ると、Webpack は最初にサードパーティのブラウザーフィールドを使用しますmodule を使用してモジュールのエントリ ファイルを検索します。 If 存在しない場合は、モジュール フィールドが使用されます。
検索手順を減らすために、サードパーティモジュールのエントリファイルの説明フィールドを指定する場合、できるだけ少なく設定できます。
ほとんどのサードパーティ モジュールはメイン フィールドを使用してエントリ ファイルの場所を記述するため、Webpack は次のように構成できます:
module.exports = { resolve: { // 只采用 main 字段作为入口文件描述字段,以减少搜索步骤 mainFields: ['main'], }, };
この方法を使用して最適化する場合は、すべてのサードパーティ モジュールのエントリ ファイル記述フィールドを考慮する必要があります。 -ランタイムが依存するパーティモジュール。1 つのモジュールが間違っている場合でも、ビルドされたコードは正しく実行されない可能性があります。
resolve.alias 構成の最適化
2-4 Resolve で導入された、resolve.alias 構成アイテムは、エイリアスを介して元のインポート パスを新しいインポート パスにマップします。
実際のプロジェクトでは、多くの場合、いくつかの巨大なサードパーティ モジュールに依存します。React ライブラリを例に挙げると、node_modules ディレクトリにインストールされる React ライブラリのディレクトリ構造は次のとおりです。
├── dist
│ ├── react.js
│ └── react.min.js
├── lib
│ ... 还有几十个文件被忽略
│ ├── LinkedStateMixin.js
│ ├── createClass.js
│ └── React.js
├── package.json
└── react.js
可以看到发布出去的 React 库中包含两套代码:
一套是采用 CommonJS 规范的模块化代码,这些文件都放在 lib 目录下,以 package.json 中指定的入口文件 react.js 为模块的入口。
一套是把 React 所有相关的代码打包好的完整代码放到一个单独的文件中,这些代码没有采用模块化可以直接执行。其中 dist/react.js 是用于开发环境,里面包含检查和警告的代码。 dist/react.min.js 是用于线上环境,被最小化了。
默认情况下 Webpack 会从入口文件 ./node_modules/react/react.js 开始递归的解析和处理依赖的几十个文件,这会时一个耗时的操作。
通过配置 resolve.alias 可以让 Webpack 在处理 React 库时,直接使用单独完整的 react.min.js 文件,从而跳过耗时的递归解析操作。
相关 Webpack 配置如下:
module.exports = { resolve: { // 使用 alias 把导入 react 的语句换成直接使用单独完整的 react.min.js 文件, // 减少耗时的递归解析操作 alias: { 'react': path.resolve(__dirname, './node_modules/react/dist/react.min.js'), } }, };
除了 React 库外,大多数库发布到 Npm 仓库中时都会包含打包好的完整文件,对于这些库你也可以对它们配置 alias。
但是对于有些库使用本优化方法后会影响到后面要讲的 使用 Tree-Shaking 去除无效代码 的优化,因为打包好的完整文件中有部分代码你的项目可能永远用不上。
一般对整体性比较强的库采用本方法优化,因为完整文件中的代码是一个整体,每一行都是不可或缺的。
但是对于一些工具类的库,例如 lodash ,你的项目可能只用到了其中几个工具函数,你就不能使用本方法去优化,因为这会导致你的输出代码中包含很多永远不会执行的代码。
优化 resolve.extensions 配置
在导入语句没带文件后缀时,Webpack 会自动带上后缀后去尝试询问文件是否存在。
在 2-4 Resolve 中介绍过 resolve.extensions 用于配置在尝试过程中用到的后缀列表,默认是:
extensions: ['.js', '.json']
也就是说当遇到 require('./data') 这样的导入语句时,Webpack 会先去寻找 ./data.js 文件,如果该文件不存在就去寻找 ./data.json 文件,如果还是找不到就报错。
如果这个列表越长,或者正确的后缀在越后面,就会造成尝试的次数越多,所以 resolve.extensions 的配置也会影响到构建的性能。
在配置 resolve.extensions 时你需要遵守以下几点,以做到尽可能的优化构建性能:
后缀尝试列表要尽可能的小,不要把项目中不可能存在的情况写到后缀尝试列表中。
频率出现最高的文件后缀要优先放在最前面,以做到尽快的退出寻找过程。
在源码中写导入语句时,要尽可能的带上后缀,从而可以避免寻找过程。例如在你确定的情况下把 require('./data') 写成 require('./data.json') 。
相关 Webpack 配置如下:
module.exports = { resolve: { // 尽可能的减少后缀尝试的可能性 extensions: ['js'], }, };
优化 module.noParse 配置
在 2-3 Module 中介绍过 module.noParse 配置项可以让 Webpack 忽略对部分没采用模块化的文件的递归解析处理,这样做的好处是能提高构建性能。
原因是一些库,例如 jQuery 、ChartJS, 它们庞大又没有采用模块化标准,让 Webpack 去解析这些文件耗时又没有意义。
在上面的 优化 resolve.alias 配置 中讲到单独完整的 react.min.js 文件就没有采用模块化,让我们来通过配置 module.noParse 忽略对 react.min.js 文件的递归解析处理,
相关 Webpack 配置如下:
const path = require('path'); module.exports = { module: { // 独完整的 `react.min.js` 文件就没有采用模块化,忽略对 `react.min.js` 文件的递归解析处理 noParse: [/react\.min\.js$/], }, };
注意被忽略掉的文件里不应该包含 import 、 require 、 define 等模块化语句,不然会导致构建出的代码中包含无法在浏览器环境下执行的模块化语句。
相关推荐:
以上がファイル検索範囲を絞り込むWebpack最適化設定の詳細例の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。