Webpack3+React16コード分割の実装

Webpack3+React16コード分割の実装

プロジェクトの背景

最近、webpackのバージョンが古いプロジェクトがあります。 リーダー層では今のところアップグレードやフレームワークの変更が受け入れられないのでo(╥﹏╥)o、現状のままで最適化するしかありません。

webpack3 + react16

webpack v3 構成チェック

プロジェクト構成が v1 から継承されていることは明らかです。v1 から v3 へのアップグレードは比較的簡単です。公式 Web サイト https://webpack.js.org/migrate/3/ を参照できます。

ローダーがルールになる
連鎖ローダーはサポートされなくなり、json-loaderを設定する必要がなくなりました。
UglifyJsPluginプラグインは最小化を有効にする必要があります

既存のパッケージの問題を分析する

図に示すように、webpack-bundle-analyzerを使用してパッケージをビルドした後

問題は明らかです:

より大きなパッケージ zxcvbn を除き、コードはベンダーとアプリに単純にパッケージ化されており、ファイルは非常に大きくなります。

動的インポート分割ベンダー

ベンダー コードを分析します。libphonenumber.js などの一部の大きなパッケージは頻繁に使用されません。それらを取り出して、関連する機能が使用されるときに要求します。

公式のReactコード分割ガイドを参照してください。https://react.docschina.org/docs/code-splitting.html#import

'google-libphonenumber' から { PhoneNumberUtil } をインポートします
関数usePhoneNumberUtil(){
  // PhoneNumberUtil を使用する
}

動的import()メソッドに変更すると、非同期データを取得するためにasync/awaitの両方がサポートされます。

const LibphonenumberModule = () => import('google-libphonenumber')
関数usePhoneNumberUtil(){
  LibphonenumberModule().then({PhoneNumberUtil} => {
    // PhoneNumberUtil を使用する
  })
}

Webpack はこの構文を解析すると、自動的にコード分割を実行します。

変更された効果:

libphonenumber.js (1.chunk.js) はベンダーから分離されており、プロジェクトの実際の動作では、usePhoneNumberUtil プロセスに入るときにのみ、libphonenumber.js ファイルがサーバーに要求されます。

ルートベースのコード分割

リアクトレイジー

React の公式コード分割ガイド「ルートベースのコード分割」(https://react.docschina.org/docs/code-splitting.html#route-based-code-splitting) を参照してください。

分割前の例:

'react' から React をインポートします。
'react-router-dom' から Route、Switch をインポートします。
const Home = import('./routes/Home');
About = import('./routes/About');

定数App = () => (
<ルーター>
 <Suspense fallback={<div>読み込み中...</div>}>
  <スイッチ>
   <ルートの正確なパス="/" コンポーネント={Home}/>
   <ルート パス="/about" コンポーネント={About}/>
  </スイッチ>
 </サスペンス>
</ルーター>
);

分割後の例:

React をインポートします。{ lazy } から 'react';
'react-router-dom' から Route、Switch をインポートします。
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));

定数App = () => (
// ルーティング設定は変更されません)

分割後の効果:

app.js はルートに応じて webpack によって自動的に別のファイルに分割されます。ルートが切り替えられると、対象のルートコードファイルがプルされます。

名前付きエクスポート

この段落は https://react.docschina.org/docs/code-splitting.html#named-exports から引用されています。

React.lazy現在、デフォルトのエクスポートのみをサポートしています。インポートされたモジュールで名前付きエクスポートを使用する場合は、デフォルト モジュールとして再エクスポートする中間モジュールを作成できます。これにより、ツリー シェイキングが失敗せず、不要なコンポーネントが含まれないことが保証されます。

// 多くのコンポーネント.js
エクスポート const MyComponent = /* ... */;
エクスポート const MyUnusedComponent = /* ... */;
// MyComponent.js
"./ManyComponents.js" から { MyComponent をデフォルトとして } をエクスポートします。
// MyApp.js
React をインポートします。{ lazy } から 'react';
const MyComponent = lazy(() => import("./MyComponent.js"));

AsyncComponent を自分で実装する

React.lazy でラップされた遅延読み込みルーティング コンポーネントは Suspense を追加する必要があります。強制したくない、または遅延実装を自由に拡張する必要がある場合は、AsyncComponent を定義および実装し、遅延と同じように使用できます。

'./components/asyncComponent.js' から AsyncComponent をインポートします。
const Home = AsyncComponent(() => import('./routes/Home'));
const About = AsyncComponent(() => import('./routes/About'));
// 非同期ロードコンポーネント
関数 AsyncComponent(getComponent) {
 戻りクラスAsyncComponentはReact.Componentを拡張します{
  静的コンポーネント = null
  状態 = { コンポーネント: AsyncComponent.Component }

  コンポーネントマウント() {
   if (!this.state.Component) {
    getComponent().then(({ デフォルト: コンポーネント }) => {
     AsyncComponent.Component = コンポーネント
     this.setState({ コンポーネント })
    })
   }
  }
  // コンポーネントはアンマウントされます
  コンポーネントのマウントを解除します(){
   // setState 関数を書き換え、何も返さない
   this.setState = () => {
    戻る
   }
  }
  与える() {
   const { コンポーネント } = this.state
   if (コンポーネント) {
    <Component {...this.props} /> を返します。
   }
   nullを返す
  }
 }
}

一般的なビジネスコード分割

ルートベースのコード分割が完了した後、パッケージ サイズを注意深く確認したところ、パッケージの合計サイズが 2.5M から 3.5M に増加していることがわかりました。

Webpack 分析ツールから、各ルーティング コードがコンポーネント、ユーティリティ、ロケールなどの個別のパブリック ファイルでパッケージ化されていることが原因であることが分かります。

webapck 構成を使用して、共通部分を個別にパッケージ化します。

コンポーネントファイルのマージとエクスポート

この例では、コンポーネントの下にあるすべてのファイルをまとめてエクスポートします。他のファイルについても同様です。

関数 readFileList(dir, filesList = []) {
 定数ファイル = fs.readdirSync(dir)
 files.forEach((item) => {
  fullPath = path.join(dir, item) とします。
  const stat = fs.statSync(fullPath)
  (stat.isDirectory())の場合{
   // すべてのファイルを再帰的に読み取る readFileList(path.join(dir, item), filesList)
  } それ以外 {
   /\.js$/.test(fullPath) && filesList.push(fullPath)
  }
 })
 ファイルリストを返す
}
exports.commonPaths = readFileList(path.join(__dirname, '../src/components'), [])

共通から抽出されたWebpack構成

'**' から conf をインポートします。
モジュール.エクスポート = {
 エントリー: {
  共通: conf.commonPaths、
  インデックス: ['babel-polyfill', `./${conf.index}`],
 },
 ... //その他の設定プラグイン:[
  新しい webpack.optimize.CommonsChunkPlugin('common')、
  ... // その他のプラグイン
 ]
}

CommonsChunkPlugin は、webpack3 でサードパーティのライブラリと共通モジュールを抽出するために使用されます。渡されるパラメータcommonは既存のエントリのチャンクであり、共通モジュールのコードがこのチャンクにマージされます。

共通コードから抽出

各ルートの重複コードを抽出すると、パッケージの合計サイズは再び 2.5M になります。追加の共通バンドル ファイルがあります。 (コモンは大きすぎるので、実際にはさらに分割できます)

要約する

webpack パッケージングには、最適化できる領域がまだたくさんあります。また、webpack のバージョンによっても違いがあります。アンパックの考え方は、共通するものを抽出し、使用シナリオに応じてオンデマンドでロードすることです。

Webpack3+React16 コード分割の実装に関するこの記事はこれで終わりです。Webpack3+React16 コード分割に関するより関連性の高いコンテンツについては、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き閲覧してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Reactはページコード分割とオンデマンド読み込みを実装します
  • React でコード分割を実装する 4 つの方法

<<:  インストールされていないバージョンの MySQL を使用する手順とパスワードを忘れた場合の解決策

>>:  Linux で仮想コンソール セッションをロックする方法

推薦する

CSS で点線の境界線のスクロール効果を実装するサンプルコード

マウスをある領域の上に置くと、その領域に点線の境界線と線のアニメーションが表示されるというクールな効...

JavaScriptのクローン作成についての簡単な説明

目次1. 浅いクローニング2. ディープクローニング1. 浅いクローニング浅いクローンでは配列やオブ...

Vueは動的コンポーネントを使用してTAB切り替え効果を実現します

目次問題の説明Vueの動的コンポーネントとはアプリケーションシナリオの説明実装手順ステップ 1 (新...

HTML の 2 つのタブ ナビゲーション間の競合の解決方法

まず問題の説明から始めましょう:同じページで、1 つのタブに float:left が必要で、もう ...

Elasticsearchツールcerebroのインストールと使用チュートリアル

Cerebro は、Elasticsearch バージョン 5.x より前の Elasticsear...

CSS における z-index: 0 と z-index: auto の違い

最近、スタック コンテキストについて学習しています。学習の過程で、z-index が 0 の場合と ...

MySQLが基礎データ構造としてB+ツリーを使用する理由

MySQL の基盤となるデータ構造が B+ ツリーであることは誰もが知っていますが、ではなぜ赤黒ツリ...

Vueは要素ツリーコントロールを通じてツリーテーブルを実装します

目次実装効果図依存関係をインストールするカスタムツリーコントロールその他の実装要約するVueでは、要...

インデックスを設計する際の原則は何ですか? インデックスの障害を回避するにはどうすればよいでしょうか?

目次主キーインデックス頻繁にクエリされるフィールドのインデックスを作成する大きなフィールドのインデッ...

HTML メタの使用例

使用例コードをコピーコードは次のとおりです。 <!DOCTYPE html> <!...

JavaScript配列の一般的なメソッドの詳細な説明

目次元の配列を変更しない方法1. 連結文法:パラメータ:戻り値: 2. 参加する文法:パラメータ:戻...

CSS3アニメーションを使用して、小さい円から大きい円に拡大し、外側に広がる効果を実現する例

序文この記事では、CSS3アニメーションを使用して、円が小さいものから大きく拡大し、外側に広がる効果...

MySQL コマンドを使用してインデックスを作成、削除、およびクエリする方法の紹介

MySQL データベース テーブルでは、インデックスを作成、表示、再構築、削除できるため、クエリ速度...

secure_file_priv nullの問題を解決する

secure_file_priv = ' ';管理者としてcmdを実行します。 my...

JavaScript を学ぶときに知っておくべき 3 つのヒント

目次1. 魔法の拡張演算子1. 配列をコピーする2. 配列を結合する3. オブジェクトを展開する2....