極端に最適化されたWebPACKのパッケージ_20180619

背景:

プロジェクトのフロントラインでは、具体的パックWebPACKのために最適化されますが、時間によってネットワークの最適化を行った後webpack-bundle-analyzer、このプラグインのjsコードでリピートビジネスにパッケージ化いくつかの共通のjsファイルを検索します。これらのコードは小さいが、究極の最適化を達成するためにあるか、それを最適化する一方で。このプロセスの最大の成果は、自分ができるようにすることですwebpack4.xより身近関連の設定項目を、彼らは非常に有能たい包装のWebPACKの方法を達成するために使用することができます。

:先輩は、フロントエンドの最適化の言葉と言わ前に覚えておいてくださいフロントエンドの最適化は、長所と短所を計量した後に作られた妥協案です。

最適化の結果:

プロジェクトがパックマルチモード注入口(ですので、この最適化の結果を見て、プロジェクトの足場こちらをクリックしてください)各ページへのリンクと、各ページには独自のjsファイルを持っています。

結果は以下の通りであります:

  • jsのコード量の削減:20キロバイト+
  • ネットワーク接続の長さを短く:500ミリ秒+

移動端末は、20キロバイトのアイテムを低減することができ、元に基づいて小さくはないが、それは、生成され包装さ20キロバイトの繰り返しの一部です。ネットワークジッタおよびその他の非があるだろうので、500ミリ秒以上のプロジェクトに3日間行の後に非常に明確に、統計的平均値を参照するには、当社の内部監視プラットフォームとのデータ、ほぼ800msのページの読み込み時間の節約を介してネットワークの最適化は、(、保守的な文言であります抗ファクター)。

最適化前の統計データ

統計データは、最適化されました

最適化ネットワーク分析の期間兼最高経営責任者(CEO)

1、DNSの事前解像度を追加

次のように事前に解析するドメイン名とDNSは、headタグのHTMLのMETAタグによる事前解析され追加するかどうかを指定し、コードの例は次のとおりです。

    <!--告诉浏览器开启DNS 预解析-->
    <meta http-equiv="x-dns-prefetch-control" content="on" />
    <!--添加需要预解析的域名-->
    <link rel="dns-prefetch" href="//tracker.didiglobal.com">
    <link rel="dns-prefetch" href="//omgup.didiglobal.com">
    <link rel="dns-prefetch" href="//static.didiglobal.com">
复制代码

上記のコードを追加することを初めて目にする前に、ページの解析時間静的リソース

DNS事前解決のコードを追加した後、上記の画像を比較した後、あなたは明らかに見つけることができtracker.didiglobal.comますが、ロードするために必要なドメイン名を事前に事前に解析を完了しました。これは、JSファイルは、(このようなページのオンデマンドロードなど)は、ページのレンダリングに影響を与えている場合は、ページのレンダリング性能を向上させることができます。

2、遅延は、ページのレンダリングコードの実行に影響を与えます

通常、我々は、例えば、いくつかのサードパーティのJSが依存引用クライアントjsbridgeメソッドを呼び出します、モバイル開発のプロセスを終了client.jsし、RBIアクセスサービスをconsole.log.js

通常の方法は、比較的暴力的な方法であるheadタグ、JSをダウンロードし、必要なときに後にいつでも呼び出すことができ、時間を進めるためのヘッドタグのDOM解決するために添加されるこれらのサードパーティのJSに依存することです。しかし、JSダウンロード時間と実行時間の頭の上にこれらのラベルは、間違いなくページのレンダリング時間に影響を与えます。

私たちのプロジェクトの最適化、薄緑の縦線はレンダリング時間の始まりである前に、次の絵が現状です。以下のチャートタイプから、我々のクライアントの参照方法で見つけることができるfusion.jsとRBI omega.jsページ開始時間のレンダリングに影響を与えている(いずれもJSは、headタグです)。

実際には、ページの前に私たちのビジネスシナリオは、これらのjsの結果を必要としないレンダリングされ、その後、なぜ我々は、これらのjsの非同期読み込み、それをすることはできませんか?

jsの非同期ロードが動的に作成使用して、適切な時期を選択することであるscript方法をラベルには、ページ内にロードJSにする必要があります。私たちのプロジェクトでは、我々はVUEライフサイクル選んだ、VUEを使用mounted中にロードするためのJS、我々はロードするために、クライアントのメソッドを呼び出す必要がどこになるRBIのニーズにfusion.jsライブラリをロードするために非同期コールバック経由の道を、終えロードJSの実行ユーザーの操作。

ここでは、簡単なサンプルコードは、次のとおりです。


export default function executeOmegaFn(fn) {
    // 动态加载js到当前的页面中来,并且当js加载完之后执行回调,注意需要判断js是否已经在当前环境加载过了
    scriptLoader('//xxx.xxxx.com/static/tracker_global/2.2.2/xxx.min.js', function () {
        fn && fn();
    });
}

// 异步加载 需要加载的js
mounted() {
    executeOmegaFn();
},
复制代码

数値は、改正後の効果を見つけることができますされ、チャート上のレンダリングにも前もっての種類の完成よりも完了するために、事前にページのレンダリング時間をページのレンダリングを開始2Sまたはそう。あなたは明らかページのレンダリングを参照してオメガ実行時間が非干渉されてダウンロードすることができます。

要約:

  1. コードブロックを解析するDNS事前HTMLテンプレートファイルにドメイン名を追加し、ブラウザは、事前にあらかじめロードされた静的ファイルのドメイン名を解決する必要があります。静的リソースをダウンロードして、静的ファイルのダウンロードをスピードアップするために。
  2. 不要な遅延のjsファイルをロードして実行レンダリング最初の画面では、最初の画面のレンダリング速度を向上させるためには、事前にページをレンダリングを開始します。

出力の最適化のWebPACK

1は、最適化されたコードをパック繰り返し

デフォルトのwebpack4.x 生産モードは、コードツリーに振盪になるだろう。しかし、この記事を読んだ後、あなたは、ほとんどの場合、ことがわかりますツリーが揺れていないと、重複コードを削除する方法。あなたのすべての卵を使用していませんでしたツリーを揺るがします

ビジネスに基づいlibディレクトリには、私は私のプロジェクトで書かれているライブラリを必要とそこに立っていた、てwebpack-bundle-analyzerそれが私たちのビジネスコードをファイルjsファイルにパッケージ化繰り返し見つけます。パッケージが含まれてJSファイルのサービスコードである次の図は、あなたが見ることができるのlibパッケージディレクトリの内容が繰り返さ

独自の最適化について話をするには、この時間を見て:

  • これは、統一にパッケージ化node_modulesディレクトリに依存しますvendor依存。
  • ;私はlibディレクトリに書かれており、共通の単一の共通にパッケージ化されているライブラリー
  • パッケージ化されたサードパーティコンポーネントライブラリ例えばキューブUIと日付picerスクロールアセンブリなどのコンポーネントライブラリコンポーネント、比較的大量の場合、需要に依存します。一度だけのjsファイルにパックした場合、それが共通で複数のページの参照にパッケージ化されている場合、独自のページを参照しています。背後に、サードパーティの最適化依存し、この部分は、ポリシー下で包装詳細に説明する最適化。

WebPACKのは、特に注記し、開梱上の私のプロフィールを初めて目:

splitChunks: {
    chunks: 'all',
    automaticNameDelimiter: '.',
    name: undefined,
    cacheGroups: {
        default: false,
        vendors: false,
        common: {
            test: function (module, chunks) {
                // 这里通过配置规则只将 common lib  cube-ui 和cube-ui 组件scroll依赖的better-scroll打包进入common中
                if (/src\/common\//.test(module.context) ||
                    /src\/lib/.test(module.context) ||
                    /cube-ui/.test(module.context) ||
                    /better-scroll/.test(module.context)) {
                    return true;
                }
            },
            chunks: 'all',
            name: 'common',
            // 这里的minchunks 非常重要,控制cube-ui使用的组件被超过几个chunk引用之后才打包进入该common中否则不打包进该js中
            minChunks: 2,
            priority: 20
        },
        vendor: {
            chunks: 'all',
            test: (module, chunks) => {
                // 将node_modules 目录下的依赖统一打包进入vendor中
                if (/node_modules/.test(module.context)) {
                    return true;
                }
            },
            name: 'vendor',
            minChunks: 2,
            // 配置chunk的打包优先级,这里的数值决定了node_modules下的 cube-ui 不会打包进入 vendor 中
            priority: 10,
            enforce: true
        }
    }
}
复制代码

私はプロジェクト全体のキューブUIコンポーネントは、プロジェクトで導入された統一の使用を必要とJS書きました。具体的なコードはここを参照してくださいここを参照してくださいメソッドを呼び出します

この時、この文書に導入される低周波成分を大量に使用しないことに注意してください、特定の理由は以下のコードのコメントを見ることができます。


/**
 * @file cube ui 组件引入统一配置文件 建议这里只引入每个页面使用的基础组件,对于复杂的组件比如scroll datepicer组件
 * 在页面中单独引入,然后在webpack中同过 minChunk 来指定当这些比较大的组件超过 x 引用数时才打进common中否则单独打包进页面的js中
 * @date 2019/04/02
 * @author [email protected]
 */

/* eslint-disable  */
import Vue from 'vue';
import {
    Style,
    Toast,
    Loading,
    // 这里去除 scroll是在页面中单独引入,以使webpack打包时可以根据引用chunk选择是否将该组件打包进入页面的js中还是选择打包进入common中
    // Scroll,
    createAPI
} from 'cube-ui';


export default function initCubeComponent() {
    Vue.use(Loading);
    // Vue.use(Scroll);
    createAPI(Vue, Toast, ['timeout'], true);
}

复制代码

プロジェクトは現在、大きなページのようにJSのJSコードに別のビジネスにパッケージ化スクロールキューブUIコンポーネントを使用してこのページをpay_history。

以上の2つのページがある場合は、自動的にパッケージ共通に入りますWebPACKの構成によると、このコンポーネントときを使用してスクロールします。図はその結果をパックされ、ページサイズを縮小JS、音量は大commonjsファイルになります。

要約:

  • サードパーティアセンブリ負荷依存性のために最適化され、不要な実行時間の負荷の損失を低減します。

2、不要なインポートを削除します

時々私はコメントアウトすることなく、第三者ではなく、最後に引用されたインポートは表現の使用に依存していないか、それがライン上にあるときにコメントし、実行する必要はありません、書き込み時には気付かなかったimport文、結果がパッケージ化されますこれは、JSの輸入が含まれています。たとえば、次のコード:


import VConsole from 'vconsole';
// 测试的时候我们可能打开了下面的注释,但是在上线的时候只是注释了下面的代码,webpack打包的时候仍然会将vconsole打包进目标js中。
// var vConsole = new VConsole();

复制代码

要約:

  • 何のインポートインポート機能や文を直接インポートします式はコメントアウトしないか、削除された場合、無効なimport文を識別します。

3、バベルプリセット-ENVおよび構成最適化autoprefix

バベル+現在のフロントエンドコードを書き込むために使用ES6組成物は、ほとんど使用されていないバベル-ポリフィルの Aを。ほとんどの場合、それはグローバル変数を汚染し、ポリフィルの導入の全体量は、パッケージ後の目標のjsファイルは非常に大きくなります。

今、ほとんどの場合、バベル-プリセット-ENVはポリフィルを行い使用します。もっとインテリジェントまたは高度な実践は、ポリフィルのオンラインサービスを使用することです参照リンクを

使用してプリセット-ENV時間は、ほとんどの場合は、互換性のあるブラウザのリストを設定するには、無視されます。または構成にネットワークから直接検索し、直接ペーストを使用し、出力結果のコピーに入りません。実際には、この構成では、私たちのjsファイルのサイズに影響します。

あなたが使用している場合はautoprefix自動的にベンダーのCSSに接頭辞を追加し、また、あなたはブラウザのリストを設定する必要があります。この構成リストはまた、CSSファイルのサイズに影響します。公式文書browserslist

例えば、今のWindows Phoneの携帯電話は、今や携帯端末プロジェクトはWP互換性のあるPCや携帯電話を考慮する必要がないため、我々はポリフィルやCSSベンダープレフィックスを削除することはできません追加、ほぼ絶滅している場合には-ms-、次に設定する方法、それの接頭辞をそれは?

次のように私の構成は次のとおりです。

"browsers": [
    "> 1%",
    "last 2 versions",
    "iOS >= 6.0",
    "not ie > 0",
    "not ie_mob  > 0",
    "not dead"
]
复制代码

ここでは簡単な言及CSSの新機能を適切に使用することの重要性。次のコードは、私たちの古いプロジェクトの一つで見たものです。実際には、一目見ただけでは問題ありませんが、最近のブラウザで閲覧したが問題がありますか?

.example {
    display: flex;
    display: -webkit-box;
}

.test {
   flex:1
}

复制代码

この文言はに常にあるフレックスレイアウト矛盾によって引き起こさ解決の問題。クロムに.example力のあるdisplay: -webkit-box柔軟ポーチレイアウト移行言葉遣い。では.testその発効flex:1し、この新しい標準文言です。表示レイアウトに問題が発生します。

autoprefix後のコード

.example {
    display: -ms-flexbox;
    display: flex;
    display: -webkit-box;
}

.test {
   -webkit-box-flex:1;
       -ms-flex:1;
           flex:1
}

复制代码

同じことがまた、上記autoprefix、レイアウトエラーの前に何の問題を引き起こすことはできません。

要約:

  1. 独自のビジネス・シナリオによると、具体的なポリフィルの設定を追加します。
  2. 新しいCSS3プロパティ、およびautoprefixを使用していますが、自動的にプレフィックス加工会社を追加する場合は、あなただけがライン上で元のコードでは、最新の標準文言を使用する必要があります。

4、WebPACKのランタイムファイルインライン

WebPACKのコンパイルされたコードを使用して、コードをする場合、複数のチャンクに生成され、WebPACKのは、これらのチャンクをロードする方法ですか?

異なるモジュール(ランタイムモジュールのWebPACK物品と呼ばれる)との間の関係を維持するために、モジュールローダの独自の実装をWebPACKの。それは数字の文字列で識別されている異なるモジュールを識別し(特にデモのコンパイル結果でシンプルな外観を書き込む場合があります)。

ファイルが変更される数字のランタイムモジュールの文字列にコードを変更した場合、コードはWebPACKの中にパッケージされていない場合は、プロセスのこの部分は、それが私たちの出力にデフォルト設定されますvendorコード。ロング修正コードにつながる、ハッシュは、ベンダーのファイルが変更されますブラウザのキャッシュを利用する方法を生成しません。

WebPACKの構成は、引き出し別のファイルを生成するコードのこのセクションを提供していることができます。頻繁にコードの変更、および符号量のこの部分が小さいため、HTTP要求をパッケージ化する際、テンプレートにリンクされたHTMLファイルにコードのこれらの内側部分を減少させるために選択することができます。

Webpack4.xは、以下の構成で実現することができますoptimization.runtimeChunk: 'single'あなたはユナイテッドのhtmlコードを入力したい場合は、ランタイム内で生産され、あなたはこのWebPACKのプラグを使用することができますinline-manifest-webpack-plugin

5、不要な非同期文を削除

非同期と待つ糖衣構文は、非同期プログラミングの問題を解決することができます。構文はまた、フロントエンド・コード中の糖の調製に使用することができます。あなたはバベルとtypescriptですコンパイルされたコードを使用するかどうか、それは実際には非同期で、発電機にコンパイル待っています。

究極のコードサイズの需要があれば、私は非同期の使用をお勧めしますし、フロントエンドのコードで待っていません。多くのサードパーティの非同期処理を使用して依存的であるためPromise、node_modulesは、私たちの使用に依存して、一般的にも、コンパイル済みのファイルES5の源である、それがためにあるPromiseのポリフィルを行います。そして、我々はバベルの設定を所有もなりPromiseますが、非同期をミックスして待っていた場合、バベルが関連大きくなり、ポリフィルを行う発電機のランタイムコードを。

次のコードは、非同期式で表示されますが、すべての時間はローカル呼び出し、このメソッドを待つの非同期式を使用する必要はありませんコードを読み込むことでもあっ決定され、使用しませんでした:コードの実際のケースを見てください

以下に示すように、非同期式を添加した後、結果をコンパイル。これは、中に複数のオブジェクトファイル出力で見つけることができgeneraotr runtime、コード、およびコードのこの部分のボリュームはかなり大きいです

これは、プリコンパイルされたファイルのサイズです

不要な非同期式を除去した後、ほぼ減少図コンパイルされたファイルのサイズ、コードサイズで見ることができ3KB

図6は、サードパーティの最適化に依存しています

最初のセクションでは、サードパーティ製のパッケージの依存関係を最適化するための簡単な方法を導入しており、ここで要約の下で行います。

  • サードパーティの依存度がコンポーネントライブラリ全額を導入していない、サポートに頼る場合は、サードパーティを使用する需要途中でコンパイルされ、ロードされたコンパイル、使用;
  • サードパーティのコンポーネントが大量に依存している、とのプロジェクトでそれほど頻繁に使用している場合は、ページがオンデマンドでロードされている、あなたは回数を超える被引用数は、彼がでパッケージ公共の共通のコンポーネントに入る前にすると、ルールを設定することを選択することができ、そうでない場合は、サービスへのアセンブリコードに直接行きます。
  • サードパーティは、ヘッドタグを記述しないようにしよう、スクリプトタグやリンクによって組み込ま依存している場合には、負荷が導入​​され、必要に応じて選択することができます。

コンパイル後、あること、コンパイラを使用する場合、コードの重複のパッケージの問題を解決することができます。私はアセンブリ20を使用する場合は、キューブ-UI、需要の導入を意味するが、私は、アラートコンポーネントを1つだけ持っている、すべての導入を避けるために、出力ファイルの音量を上げます。

オンデマンドで導入された7、lodash

lodashライブラリ本当に良いの使用が、それは、より大きなボリュームのパッケージの全体量の導入後、欠点があります。だから、lodashは、必要に応じてそれを導入することはできませんか?

もちろん、私たちは、あなたがして、ドキュメントの実行コマンドに応じlodashとしてエクスポートすることができ、NPMにlodash-ES、このモジュールを検索することができES6モジュールその後、一人でインポートモードを使用して、関数の方法により導入することができます。

実際には、最適化する必要はありませんかlodash最適化最後に、これはいくつかの論争で、Baiduの大規模なコンクリートの下で高い灰色のTは、この記事を読むことができlodash中のWebPACKの最適化しようとします大きな灰色はまた、この記事では、記事の冒頭で述べた実証ビジネスニーズに基づいて、様々なトレードオフを最適化する妥協案を行うことです。

WebPACKの重要な知識の概要

1つのハッシュ差,, contenthashのchunkhash

hash プロジェクト全体のハッシュ値を変更するために建設される限り、プロジェクトファイルの変更があったとして、プロジェクト全体の建設に関連しており、すべてのファイルが同じハッシュ値を共有しています。

chunkhash各構築物は、ハッシュ値を生成した後、ハッシュ計算を使用して、ファイルの内容は基本的に変化しない場合であっても、同じではありません。これはキャッシュ効果を実装する方法はありません同じように、私たちは別のハッシュ値計算、すなわちchunkhashを変更する必要があります。chunkhashハッシュが同じではない、それはファイルエントリ(エントリ)に応じてファイルの依存関係を解析し、対応するチャンクの構築は、対応するハッシュ値を生成します。私たちは、公共図書館やプログラムファイル入口領域の数に本番環境に構築するために、別のパッケージを分離し、我々は、ハッシュ値を生成するchunkhash方法を使用して、限り、我々はコードの公共図書館を変更しないよう、私たちは、そのハッシュ値を保証することはできません影響を受けました。

contenthashWebPACKのコンパイルされたコードを使用している場合、我々は、CSSファイル内のJSファイルを参照することができます。したがって、これら二つの文書は、同じ価値観を共有する必要がありchunkhash。しかし、問題はモジュールがCSSファイルで、その結果、変更されたため、内容が変更されていない場合でも、JS、CSSファイルを構築するために繰り返される場合は、コードを変更しています。今回は、使用することができextra-text-webpack-plugin、それを構築するために繰り返されることはありません、モジュールはCSSファイルに配置されている場合でも、CSSファイルの内容が変わらない限り、他の文書の内容を変更することを保証するために、contenthash値に。

2、splitChunksコメント

現在webpack4.xのための文書を照会することができるようにするネットワークsplitChunks、英語の読書のために障害物がない場合は中国語の翻訳を完了していないが、あなたは英語が良くない場合は、次のパラメータと中国の解釈を参照することができ、公式文書を読むために直接行くことができます:

以下の条件に従って最初Webpack4.xは自動的にコードブロック分割:

  • 新しいコードブロックを共有することができるか、これらのモジュールは、node_modules内部のフォルダから参照されています
  • 新しいコード・ブロックは、30キロバイト(MIN + gziped前の体積)よりも大きいです
  • デマンドローディングコードブロックは、最大数は5以下であるべきです
  • コードブロックの初期負荷は、最大数が3以下であるべきです

// 配置项解释如下
splitChunks: {
    // 默认作用于异步chunk,值为all
    //initial模式下会分开优化打包异步和非异步模块。而all会把异步和非异步同时进行优化打包。也就是说moduleA在indexA中异步引入,indexB中同步引入,initial下moduleA会出现在两个打包块中,而all只会出现一个。
    // all 所有chunk代码(同步加载和异步加载的模块都可以使用)的公共部分分离出来成为一个单独的文件
    // async 将异步加载模块代码公共部分抽离出来一个单独的文件
    chunks: 'async',
    // 默认值是30kb 当文件体积 >= minsize 时将会被拆分为两个文件 某则不生成新的chunk
    minSize: 30000,
    // 共享该module的最小chunk数  (当>= minchunks时才会被拆分为新的chunk)
    minChunks: 1,
    // 最多有5个异步加载请求该module
    maxAsyncRequests: 5,
    // 初始话时最多有3个请求该module
    maxInitialRequests: 3,
    // 名字中间的间隔符
    automaticNameDelimiter: '~',
    // 打包后的名称,如果设置为 truw 默认是chunk的名字通过分隔符(默认是~)分隔开,如vendor~ 也可以自己手动指定
    name: true,
    // 设置缓存组用来抽取满足不同规则的chunk, 切割成的每一个新的chunk就是一个cache group
    cacheGroups: {
        common: {
            // 抽取的chunk的名字
            name: 'common',
            // 同外层的参数配置,覆盖外层的chunks,以chunk为维度进行抽取
            chunks: 'all',
            // 可以为字符串,正则表达式,函数,以module为维度进行抽取,
            // 只要是满足条件的module都会被抽取到该common的chunk中,为函数时第一个参数
            // 是遍历到的每一个模块,第二个参数是每一个引用到该模块的chunks数组
            test(module, chunks) {
                // module.context 当前文件模块所属的目录 该目录下包含多个文件
                // module.resource 当前模块文件的绝对路径

                if (/scroll/.test(module.context)) {
                    let chunkName = ''; // 引用该chunk的模块名字

                    chunks.forEach(item => {
                        chunkName += item.name + ',';
                    });
                    console.log(`module-scroll`, module.context, chunkName, chunks.length);
                }
            },
            // 优先级,一个chunk很可能满足多个缓存组,会被抽取到优先级高的缓存组中, 数值打的优先被选择
            priority: 10,
            // 最少被几个chunk引用
            minChunks: 2,
            //  如果该chunk中引用了已经被抽取的chunk,直接引用该chunk,不会重复打包代码 (当module未发生变化时是否使用之前的Module)
            reuseExistingChunk: true,
            // 如果cacheGroup中没有设置minSize,则据此判断是否使用上层的minSize,true:则使用0,false:使用上层minSize
            enforce: true
        }
    }
};


复制代码

参考記事

ます。https://juejin.im/post/5d0aea6fe51d4550a629b286で再現

おすすめ

転載: blog.csdn.net/weixin_34253539/article/details/93167697