Webpack 3+React 16コード分割の実現


プロジェクトの背景
最近のプロジェクトにはwebpackのバージョンが古いプロジェクトがあります。アップグレードとフレーム交換のため、しばらくはleader層に受け入れられません。o(ini)oは既存の条件でしか最適化できません。

webpack3 + react16
webpack v 3配置検査
明らかに項目の構成はv 1から継承されています。v 1->v 3のアップグレードは簡単です。公式サイトhttps://webpack.js.org/migrate/3/を参照すればいいです。
loadersはrulesになります
チェーンの書き方をサポートしないloaderは、json-loaderは配置が不要です。
UglifyJs Pluginプラグインは自分でminimizeを開く必要があります。
既存のカバンの問題を分析する
webpack-bundle-analzerを使ってパッケージを構築した後、図のように

問題は非常に明らかである:
zxcvbnという大きなカバンが取り外された以外に、コードはベンダーとapのために簡単に包装されています。ファイルは大きいです。
ダイナミックimport分割ベンダー

ベンダーのコードを分析して、いくつかの大きなカバン、例えばlibphonenumber.js、使うシーンはそんなに頻繁ではありません。
react公式コード分割ガイドを参照してください。https://react.docschina.org/docs/code-splitting.html#import

import { PhoneNumberUtil } from 'google-libphonenumber'
function usePhoneNumberUtil(){
  //   PhoneNumberUtil
}
動的import()方式に変更しました。thenとasync/awaitは非同期データを取得するためにサポートされています。

const LibphonenumberModule = () => import('google-libphonenumber')
function usePhoneNumberUtil(){
  LibphonenumberModule().then({PhoneNumberUtil} => {
    //   PhoneNumberUtil
  })
}
Webpackがこの文法を解析すると、自動的にコード分割が行われます。
修正後の効果:

libphonenumber.js(1.chunnk.js)はベンダーから切り離され、プロジェクトの実際の実行中に、usePhone NumberUtilフローに入る時だけ、libphonumber.jsファイルをサーバに要求します。
ルートによるコード分割
React.lazy
react公式コード分割ガイド-ルートベースコード分割https://react.docschina.org/docs/code-splitting.html#route-based-code-spliting
分割前の例:

import React from 'react';
import { Route, Switch } from 'react-router-dom';
const Home = import('./routes/Home');
const About = import('./routes/About');

const App = () => (
<Router>
 <Suspense fallback={<div>Loading...</div>}>
  <Switch>
   <Route exact path="/" component={Home}/>
   <Route path="/about" component={About}/>
  </Switch>
 </Suspense>
</Router>
);
分割後の例:

import React, { lazy } from 'react';
import { Route, Switch } from 'react-router-dom';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));

const App = () => (
//       
)
分割後の効果:
app.jsはルートによってwebpackによって自動的に異なるファイルに分割され、ルートが切り替わると、目的のルートコードファイルが引っ張られます。

名前付きエクスポート
この部分はhttps://react.docschina.org/docs/code-splitting.html#named-exportsから引用される。React.lazyは現在、デフォルトのエクスポートのみをサポートしています。導入したいモジュールがネーミング導出を使用すると、中間モジュールを作成して、デフォルトモジュールとして再エクスポートできます。これはtree sharkingが間違いなく、不要な部品を導入する必要がないことを保証します。

// ManyComponents.js
export const MyComponent = /* ... */;
export const MyUnusedComponent = /* ... */;

// MyComponent.js
export { MyComponent as default } from "./ManyComponents.js";

// MyApp.js
import React, { lazy } from 'react';
const MyComponent = lazy(() => import("./MyComponent.js"));
自分でAyncComponentを実現します。
React.lazy小包の不精なロードルートコンポーネントは、Suspenseを追加する必要があります。強制的に使用したくない場合、または自由拡張lazyの実現が必要であれば、AyncComponentを実現することが定義され、使用方法はlazyと同じです。

import AsyncComponent from './components/asyncComponent.js'
const Home = AsyncComponent(() => import('./routes/Home'));
const About = AsyncComponent(() => import('./routes/About'));

// async load component
function AsyncComponent(getComponent) {
 return class AsyncComponent extends React.Component {
  static Component = null
  state = { Component: AsyncComponent.Component }

  componentDidMount() {
   if (!this.state.Component) {
    getComponent().then(({ default: Component }) => {
     AsyncComponent.Component = Component
     this.setState({ Component })
    })
   }
  }
  // component will be unmount
  componentWillUnmount() {
   // rewrite setState function, return nothing
   this.setState = () => {
    return
   }
  }
  render() {
   const { Component } = this.state
   if (Component) {
    return <Component {...this.props} />
   }
   return null
  }
 }
}
common業務コードの分離
ルートベースのコード分割が完了したら、パッケージのサイズをよく見てみると、全体のサイズが逆に大きくなり、2.5 Mは3.5 Mに増加します。

webpack分析ツールから見ると、張本人は個々のルートコードの中でcomponents、utils、locarsのような公共文書を個別に包装しています。
webapckの配置を使って、comonの部分を個別に包装して解決します。
componentsファイル統合エクスポート
例はcomponentsの下のすべてのファイルを一緒にエクスポートし、他のファイルは同じです。

function readFileList(dir, filesList = []) {
 const files = fs.readdirSync(dir)
 files.forEach((item) => {
  let fullPath = path.join(dir, item)
  const stat = fs.statSync(fullPath)
  if (stat.isDirectory()) {
   //         
   readFileList(path.join(dir, item), filesList)
  } else {
   /\.js$/.test(fullPath) && filesList.push(fullPath)
  }
 })
 return filesList
}
exports.commonPaths = readFileList(path.join(__dirname, '../src/components'), [])
webpack配置はcomonから外れます。

import conf from '**';
module.exports = {
 entry: {
  common: conf.commonPaths,
  index: ['babel-polyfill', `./${conf.index}`],
 },
 ... //    
 plugins:[
  new webpack.optimize.CommonsChunkPlugin('common'),
  ... // other plugins
 ]
}
webpack 3では、Commons ChunkPluginを使用して、第三者ライブラリと共通モジュールを抽出し、着信パラメータcommonは、entrtyが既に存在しているchunkであると、このchunkに共通モジュールコードを統合する。
commonを抽出した後のコード
各ルートの重複したコードを抽出すると、パケットの総サイズはまた2.5 Mになる。commonのbundleファイルが一つ増えました。commonが大きすぎて、実際には分解し続けることができます)

締め括りをつける
webpackパッケージはまだ最適化できるところがたくさんあります。また、webpackバージョンの間にも違いがあります。
ここでWebpack 3+React 16コード分割の実現に関する記事を紹介します。もっと関連するWebpack 3+React 16コード分割の内容は以前の文章を検索してください。または下記の関連記事を引き続きご覧ください。これからもよろしくお願いします。