マイクロフロントエンド (Qiankun) プロジェクトの下で、dllplugin を使用してパブリック依存関係をパッケージ化および抽出する方法と発生した問題

序文

大規模なプロジェクトでは、依存関係の繰り返しの読み込みを避けるために、パブリック部分の依存関係を抽出する必要があることがよくあります。プラグインのパブリック部分を抽出する場合、externals の使用 [Webpack.note の externals の使用] を検討できます。
ただし、externals の前提は、依存関係に cdn があるか、対応する js ファイルを見つける必要があることです。たとえば、jQuery.min.js などです。つまり、これらの依存プラグインは umd 形式をサポートする必要があります。
dll プラグインは、インストールされた依存関係を node_module に直接パッケージ化するのに役立ち、add-asset-html-webpack-pluginプラグインと組み合わせて、生成されたパッケージ化された js ファイルを html に挿入するのに役立ちます (外部のように私たちを必要としません)次に、インポートするスクリプトを手動で記述します)

1. 必要なプラグインをインストールする

3 つのプラグイン

  • webpack-cli // 一般的なプロジェクトがインストールされました
  • add-asset-html-webpack-plugin // スクリプト タグを介して依存関係を動的に html に挿入するために使用されます
  • clean-webpack-plugin // フォルダーを空にする
npm install webpack-cli@^3.2.3 add-asset-html-webpack-plugin@^3.1.3 clean-webpack-plugin@^1.0.1 --dev

2. dll 構成関連ファイルの書き込み

ルート ディレクトリ (パスは自分で設定できます) で、新しいwebpack.dll.conf.jsファイルを作成します。

// webpack.dll.conf.js

// 引入依赖
const path = require('path');
const webpack = require('webpack');
const {
    
     CleanWebpackPlugin } = require('clean-webpack-plugin'); // 清空文件用的
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; // 压缩代码用的

// 具体配置
module.exports = {
    
    
  mode: 'production', // 告诉 webpack 当前环境为生产环境
  // 入口 dll 自定义模块名字: [依赖1,依赖2,...] 
  entry: {
    
    
    vue: ['vue', 'vue-router', 'vuex'], // 打包 vue、vue-router、vuex依赖打包到一个叫 vue 的dll模块中去
    elementui: ['element-ui'],
    vendor: ['axios']
  },
  // 出口
  output: {
    
    
    filename: 'dll.[name].js', // 其中[name]就是entry中的dll模块名字,因此filename就是dll.vue.js
    path: path.resolve(__dirname, './dll/js'), // 输出打包的依赖文件到dll/js文件夹中
    library: '[name]_library'// 暴露出的全局变量名,用于给 manifest 映射 
  },
  plugins: [
    // 重新打包时,清除之前打包的dll文件
    new CleanWebpackPlugin({
    
    
      cleanOnceBeforeBuildPatterns: [path.resolve(__dirname, './dll/**/*')] // ** 代表文件夹, * 代表文件
    }),
    // 生成 manifest.json 描述动态链接库包含了哪些内容
    new webpack.DllPlugin({
    
    
      // 暴露出的dll的函数名;此处需要和 output.library 的值一致
      // 输出的manifest.json中的name值
      name: '[name]_library',
      context: __dirname, // 在项目主要的配置中需要和这保持一致
      // path 指定manifest.json文件的输出路径
      path: path.resolve(__dirname, './dll/[name]-manifest.json'),  // DllReferencePlugin使用该json文件来做映射依赖。(这个文件会告诉我们的哪些文件已经提取打包好了)
    }),
    new BundleAnalyzerPlugin(),// 压缩
  ]
}

3. dll のパッケージ化と生成

次のコマンドを package.json に追加します

"scripts": {
    
    
    "build:dll": "webpack --config webpack.dll.config.js"
}

このコマンドを実行すると、2 番目のステップの dll の構成が実行され、dll が生成されます。
ここに画像の説明を挿入

4. プロジェクトのメイン構成でコンパイル済みファイルを無視する

コンパイル時間を節約するために、パブリック ライブラリの依存関係がコンパイルされていることを webpack に伝えて、パブリック ライブラリの webpack のコンパイル時間を短縮する必要があります。プロジェクトのルート ディレクトリで vue.config.js を見つけます (そうでない場合は、新しいものを作成します)。構成は次のとおりです。

//  vue.config.js
const webpack = require("webpack");

module.exports = {
    
    
      ...
      configureWebpack: {
    
    
         ['vue', 'elementui', 'vendor'].map(item => 
            config.plugins.push(
            new webpack.DllReferencePlugin({
    
    
                context: __dirname,
                manifest: require(path.resolve(__dirname, `./public/dll/${
      
      item}-manifest.json`))
              }),
          )
      );
     }
 }

5. 生成された dll.js ファイルを index.html で参照します。

上記の設定後、公開ライブラリが抽出され、コンパイル速度が速くなりますが、生成された dll ファイルが参照されない場合、プロジェクトの依存関係が実際にインポートされず、エラーが報告されます。
public/index.html を開いてスクリプト タグを手動で挿入できます。

<script src="./dll/js/vendor.dll.js"></script>

この時点で、パブリック ライブラリの抽出は完了していますが、スクリプトの最後の手動挿入がエレガントではないことを常に感じています. 以下に、生成された dll ファイルを自動的にインポートする方法を説明します.
configureWebpack プラグイン構成で vue.config.js を開き、ステップ 4 のconfigureWebpackに基づいてadd-asset-html-webpack-plugin の構成を続けます。

// vue.config.js 
// 已经引入所需依赖
const path = require('path')
const webpack = require('webpack')
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')

module.exports = {
    
    
      ...
      configureWebpack: {
    
    
         ['vue', 'elementui', 'vendor'].map(item => 
            config.plugins.push(
            new webpack.DllReferencePlugin({
    
    
                context: __dirname,
                manifest: require(path.resolve(__dirname, `./public/dll/${
      
      item}-manifest.json`))
              }),
          )
      );
      config.plugins.push(
        new AddAssetHtmlPlugin({
    
    
             // 引用的dll.js文件位置
             filepath: path.resolve(__dirname, './public/dll/js/*.js'),
             // dll 引用路径 对dll静态资源引用的位置
             publicPath: './dll/js/*.js'),
             // dll最终输出的目录 打包后具体在dist下的文件位置
             outputPath: './dll/js/*.js'),
             includeSourcemap: false
           })
      )
     }
 }

5 番目の手順を構成した後、プロジェクトを再起動し、ブラウザー ページを更新して、設定した 3 つの js 参照を確認します。
ここに画像の説明を挿入

6. サブアプリケーションはこれらの公開依存関係を使用します

マイクロ フロントエンドをプロジェクトの背景として、アピールの 5 つのステップはすべてメイン アプリケーションで構成されますが、サブ アプリケーション (パブリックな依存関係を使用する必要があるアプリケーション) では、構成は比較的単純です。
4 番目のステップの構成を対応するサブアプリケーションの webpack 構成に追加し、これらの依存関係を無視するように webpack に指示し、マニフェストに従ってこれらの依存関係をマップするだけです。

//  vue.config.js

module.exports = {
    
    
      ...
      configureWebpack: {
    
    
         ['vue', 'elementui', 'vendor'].map(item => 
            config.plugins.push(
            new webpack.DllReferencePlugin({
    
    
                context: __dirname,
                // 注意路径别写错
                manifest: require(path.resolve(__dirname, `../../main/public/dll/${
      
      item}-manifest.json`))
              }),
          )
      );
     }
 }

7. オンデマンドの依存関係の導入 (状況によるかどうかは別として)

ant-design-vue を例にとると、一般的なコンポーネント ライブラリは、必要に応じて次のものに依存します。

import Button from 'ant-design-vue/lib/button';
import 'ant-design-vue/dist/antd.css'; //css 样式就直接引入吧

元々はオンデマンドでインポートする必要があったため、ant-design-vue 全体を dll モジュールに直接パッケージ化するのは適切ではないため、webpack.dll.conf.js のエントリを次のように変更する必要があります。

// 入口 dll模块名字: [依赖1,,依赖2,,...] 
entry: {
    
    
    vendor: ['ant-design-vue/lib/button']
},

dllplugins の欠点:

パブリック依存関係を使用するということは、パブリック依存関係を使用するすべてのアプリケーションが同じバージョンの依存関係を使用する必要があることを意味するため. 実際、これは欠点ではないと思います. 依存関係の必須バージョンです. 結局, 多くのプロジェクトがある場合,依存関係のバージョンは統一する必要があるため、サブアプリケーションを個別に開発する場合は、パブリックの依存関係をインストールするときにバージョン番号を一致させる必要があります。例: echarts の依存関係、メイン アプリケーションが5.1.0、サブ アプリケーションが4.1.0の場合、異なるバージョンの echarts の参照方法が異なるため、依存するサブアプリケーションはメインのパブリック依存関係を参照できません。サブアプリケーションを起動できないようにエラーを報告します。

プロセスで発生した問題:

1. サブアプリケーションがパブリック依存関係を使用する場合、何が起こっているのか正確にはわかりませんが、不可解なエラーが報告されることがあります。
たとえば、highcharts を使用する場合 (highcharts への参照は同じ)、アプリケーション a の使用は正常ですが、アプリケーション b は奇妙なエラーを報告し、アプリケーション c はさらにとんでもないことになり、サブアプリケーションを起動できません。直接。. .
これら 3 つの参照の dll 依存関係は一貫しています:
ここに画像の説明を挿入
アプリケーション b のエラー レポート:
ここに画像の説明を挿入
アプリケーション c のエラー レポート:
ここに画像の説明を挿入
何度も試行を繰り返した後、検索と除外により、アプリケーション b は dll モジュールの順序を変更しただけであることがわかりました。 put highcharts 最初に移動し、b を適用すると、すべて正常に動作します! (なんてこった!!!)
ここに画像の説明を挿入
次に、 c を使用して順序を変更し、ハイチャートを最初の場所に移動します。これは正常です。. .

2. antd コンポーネント ライブラリを引用すると、テーブルの全選択機能に問題があります。通常の状況では、ページフォームの使用は正常ですが、フォームはポップアップウィンドウにあり、クリックしてすべてを選択し、データを印刷することができます。つまり、確かにすべて選択があります。しかし、checkd のクラス名が追加されていません。すべてが選択されていないようです。そして、サブアプリケーションの dll への antd 参照を削除し、代わりにサブアプリケーション node_module で antd を使用するので、問題はありません。したがって、この問題は実際には dll モジュールのパッケージングに関連しています。(メインアプリのantdは1.7.5、サブアプリは1.7.2です)
ここに画像の説明を挿入
antdのバージョンと関係ないか確認するため、サブアプリのantdを1.7にバージョンアップしました。 5 で、この 1.7.5 依存関係を使用しました。結果は正常です (つまり、バージョン 1.7.5 とは関係がないということですか?)。しかし、後でサブアプリケーションを変更して dll 依存関係を使用するように戻し、メイン アプリケーションの antd コンポーネント ライブラリのバージョンをバージョン 1.7.2 に変更してから、dll を再パッケージ化しました (DLL を再パッケージ化するたびに、サブアプリケーションのサービス)、結果は問題を解決します。そのため、問題がバージョン 1.7.5 に関連しているかどうかを判断するのは困難です。

3. dll パッケージを使用してもプロジェクトのサイズは変わらない
実際、サブアプリケーションが dll を使用して抽出された依存関係を無視する場合、重要なポイントは、dll によって生成された xxx-manifest.json マッピング ファイルを確認することです。マッピングにサブアプリケーションの開発範囲 (依存関係の一貫したバージョン) で使用される依存関係が含まれている場合、サブアプリケーションは dll によって抽出された依存関係を無視するため、パッケージ化されたボリュームが削減されます。パッケージング後にボリュームが変わらない場合は、dll パッケージの依存関係を無視する必要があるサブアプリケーションまたはアプリケーションである可能性が高く、その依存関係はパッケージングの依存関係のマッピングと矛盾しているため、私のパブリックな依存関係はプロジェクトは次のように設計されています。

-根目录
-主应用
    -node_modules // 主应用非公共的依赖
-子应用文件夹
    -子应用a
        -node_modules // 只安装子应用a非公共的依赖
    -子应用b
        -node_modules // 只安装子应用b非公共的依赖
-根目录下的node_modules // 公共依赖都npm安装在这    

依存関係検索メカニズム (最初に現在のファイル node_modules を見つけ、次にグローバルな npm 依存関係まで層ごとに検索する) に従って、非公開依存関係を devDependencies に、公開依存関係を依存関係に変更します。プロジェクトの依存関係 (公開されている依存関係の開発と使用が同じであることを保証), and only need npm install --only=dev to install the dependencies of devDependencies during development. サブアプリケーションが個別にデプロイされる場合のみ、npm install が必要ですすべての依存関係をインストールします。

問題の結論:

  1. リソースのロードに関連する問題が発生した場合は、サブアプリケーションでの dll モジュールの挿入順序、つまり、上記の 6 番目の手順の配列での順序を調整してみてください。
  2. 一部のアプリケーションの依存関係について、機能上の問題がある場合は、バージョンをサブアプリケーションの開発と一致させてから、dll の依存関係を再パッケージ化してください。

要約:

繰り返し依存関係を抽出して、パッケージ化されたボリュームを大幅に削減し、プロジェクトの読み込みを最適化できるため、externals と dll は大規模なマルチアプリケーション プロジェクトに必要です. 少なくとも私が開発したマイクロ フロントエンド プロジェクトでは、テスト後、ボリュームが半分に減少しました. ただし、単一のアプリケーション プロジェクトの場合、あまり効果的ではない可能性があります. せいぜい、パッケージング時間は短縮されますが、ボリュームはあまり変わらない可能性があります. 結局、依存関係がまだあり、サイズを変更することは困難です.ただし、依存関係を繰り返し利用する場合は考えられるので、この2つの方法で最適化し、パッケージのサイズを小さくしてください。

dll をパッケージ化するときは、dll によってパッケージ化されたモジュールのほとんどがサードパーティ製の変更されていないモジュールであるため、毎回 dll のパッケージ化を実行する必要がないことに注意してください。固定の場所 (例: メイン アプリケーションの public の下) にコードを一緒に送信します。これにより、実行ビルド中の依存関係のパッケージ化の時間を短縮することもできます。また、dll の依存関係が変更されるたびに、dll を再パッケージ化する必要があります。 、dll を再パッケージ化した後、サブアプリケーションのサービスを再起動することを忘れないでください。dll が追加と削除に依存している場合は、サブアプリケーションの dll モジュールも追加して削除することを忘れないでください。

質問補足

共通の依存関係を抽出した後、qiankun フレームワークのサンドボックスが破棄され、異なるサブアプリケーションのグローバル フィルター、コンポーネント、ミックスインなどの間で相互干渉が発生する 問題の背景 サブアプリケーション A にフィルター関数 byteToSize があり

しかし、アプリケーション A はそれを使用しません。サブアプリケーション B にもこの機能があり、テーブルで使用されます。どちらのアプリケーションもフィルター関数をグローバル フィルターとして登録し、Vue インスタンスの下にマウントします。パッケージ化してオンラインにした後、最初にサブアプリケーション B を開き、次にサブアプリケーション A を開き、サブアプリケーション B に戻る限り、B が A の byteToSize フィルターを使用していることがわかります。

2 つの byteToSize 関数はグローバル関数であるため、後で記述されるサブアプリ A の byteToSize は、前のサブアプリ B の byteToSize をカバーしているようですが、2 つのアプリ サンドボックスは分離されていませんか? 次のアプリケーション コードが以前のアプリケーション コードに影響を与えるのはなぜですか? メインアプリ、サブアプリ問わず、登録されているvue.use、フィルター、コンポーネントなどのプラグインを共有することは可能ですか?

1. 検証と再発の問題
すべてのアプリケーションの vue が共有されている (同じであると見なされている) 場合、アプリケーションの vue.use 登録プラグインは既に複数の側面を登録しています。この点を証明するために、特定のサブアプリケーション (antd プラグインなど) のアプリケーションを削除しました. 再パッケージ化した後、このサブアプリケーションのページを開いたところ、すべてが正常であることがわかりました, つまり,サブアプリケーションの ui コンポーネント ライブラリの antd プラグインは、実際にはもう登録する必要はありません。もちろん、このスタイルのプラグインを繰り返し登録しても効果はありませんが、フィルタや他の問題のグローバル コンポーネントは、繰り返し使用登録によるカバレッジ コンフリクト汚染を引き起こしやすいです。
上記の分析の結果によると、後で登録されたプラグインは以前のものを上書きするはずなので、サブアプリケーションをクリックするたびに、現在のフィルター、コンポーネントなどをロードするべきではありませんか? そこで、各サブアプリケーションの main.js に印刷コードを追加したところ、サブアプリケーションが初めて読み込まれたときにのみ印刷されることがわかりました。つまり、以前に理解したマイクロフロントエンドでのサブアプリケーションのアンロードは、予約なしでサブアプリケーションを完全に殺すのではなく、vue インスタンスをアンインストールすることであり、後続のサブアプリケーションはロードされません。 main.js のコードが再度実行され、同じドメインのマイクロ フロントエンド サブアプリケーションは実際には vue プロジェクトと何の違いもありません (思わずにはいられません)。 , ドメインをまたがってフロントエンド プロジェクトを展開するのでなければ, マイクロ フロントエンド テクノロジを使用する必要はまったくないように思われます. 結局, vue フレームワークはすでにモジュール方式で開発されています. しかし, のマイクロ フロントエンド同じドメインは、少なくともマイクロフロントエンドを使用しないモジュラー開発よりも優れています。これは否定できません)。
2. 問題の原因
と、サブアプリケーションの dllplugin 構成を削除し、システムを再パッケージ化して表示すると、コンソールにレポートが表示されます。

キャッチされていない TypeError: プロパティを再定義できません: $router

エラー、メイン アプリケーションはスクリプト タグを使用して vue-router を導入するため、サブアプリケーションの vue.use はそれ自体の node_modules 内の vue-router に依存できなくなり、スクリプトの vue-router のみを使用できます$router が同じ vue の下で繰り返し定義されているため、エラーが報告されます。
どうしようもなく、メインアプリケーションのパブリック依存関係の構成をキャンセルし、パブリック依存関係を使用しない前に構成を復元し、パッケージ化後に問題がなくなったことを確認することしかできません。メインアプリの script タグで導入された方法に頼ると、Qiankun の import-html-entry の下で、サブアプリとメインアプリの共通依存関係の js を切り分けることができません。 , それらは同じウィンドウの下に共存し、パブリック依存関係に登録されます. グローバル変数は共有されます, 強力なJSサンドボックス分離を持つiframeとは異なり.
3. 問題を解決する
実際には、vue は public 依存関係にすぎず、アプリケーションが互いに干渉する原因となります。パブリックの依存関係から vue の依存関係を完全に削除すると、解決されますか? しかし、それを削除すると、コンソールはまだレポートすることがわかりました

キャッチされていない TypeError: プロパティを再定義できません: $router

間違い。これは非常に奇妙です。そのため、vue に関連するすべてのパブリック依存関係を削除しようとし、echarts パブリック依存関係を 1 つだけ残そうとしましたが、このエラーを削除できませんでした。明らかに、vue と vue-router は index.html のスクリプト タグとして導入されていませんが、このエラーは引き続き報告されます。このエラーは、サブアプリケーションが DllReferencePlugin を使用しない場合にのみ報告されません。DllReferencePlugin は、さまざまなサブアプリケーションの js コンテキスト スコープをシステム全体に拡張するために特別な操作を行ったように感じます。現在、この DllReferencePlugin は qiankun フレームワークの js サンドボックスを壊しているようです。

結局、アイデアは qiankun の GitHub 質問エリアで解決されました。
ここに画像の説明を挿入
一言で言えば、パブリック依存関係の抽出をあきらめるか、vue2.x を vue3.x にアップグレードするかのどちらかです。Vue3.x は createApp を使用してインスタンスを介して登録するため、グローバルな汚染は発生しません. これが最善の解決策です. フィルター関数の名前を一意に変更することについては、根本的な解決にはならないと思います. メソッド名は一意にすることができますが、グローバルなminxinには名前がなく、すべてのアプリケーションのすべてのページに混在するだけです.これは非常に悪いです。

おすすめ

転載: blog.csdn.net/weixin_43589827/article/details/118632092