Dans le cadre du projet micro-frontend (Qiankun), comment utiliser dllplugin pour empaqueter et extraire les dépendances publiques et les problèmes rencontrés

avant-propos

Pour les projets de grande envergure, il est souvent nécessaire d'extraire les dépendances de la partie publique pour éviter le chargement répété des dépendances. Lors de l'extraction de la partie publique du plug-in, on peut envisager d'utiliser des externals [l'utilisation d'externals de Webpack.note].
Mais la prémisse des externes est que la dépendance doit avoir un cdn ou trouver son fichier js correspondant, par exemple : jQuery.min.js et autres, c'est-à-dire que ces plug-ins dépendants doivent supporter le format umd.
Le plug-in dll peut nous aider à empaqueter directement les dépendances installées dans node_module, combiné avec le plug-in add-asset-html-webpack-plugin pour nous aider à insérer le fichier js empaqueté généré dans html (n'a pas besoin de nous comme externes Ensuite, écrivez manuellement le script à importer)

1. Installez les plug-ins requis

trois plugins

  • webpack-cli // Le projet général a été installé
  • add-asset-html-webpack-plugin // Utilisé pour insérer dynamiquement des dépendances dans html via des balises de script
  • clean-webpack-plugin // vide le dossier
npm install webpack-cli@^3.2.3 add-asset-html-webpack-plugin@^3.1.3 clean-webpack-plugin@^1.0.1 --dev

2. Écrire les fichiers liés à la configuration dll

Dans le répertoire racine (le chemin peut être défini par vous-même), créez un nouveau fichier 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. Empaquetez et générez dll

Ajoutez la commande suivante à package.json

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

Après avoir exécuté cette commande, la configuration de la dll dans la deuxième étape sera exécutée pour générer la dll
insérez la description de l'image ici

4. Ignorer les fichiers compilés dans la configuration principale du projet

Afin de gagner du temps de compilation, nous devons dire à webpack que les dépendances de la bibliothèque publique ont été compilées pour réduire le temps de compilation de webpack pour la bibliothèque publique. Trouvez vue.config.js dans le répertoire racine du projet (sinon, créez-en un nouveau), la configuration est la suivante :

//  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. Référencez le fichier dll.js généré dans index.html

Après la configuration ci-dessus, la bibliothèque publique est extraite et la vitesse de compilation est plus rapide, mais si le fichier dll généré n'est pas référencé, les dépendances du projet ne sont pas réellement importées et une erreur sera signalée.
Vous pouvez ouvrir public/index.html et insérer des balises de script manuellement.

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

À ce stade, l'extraction de la bibliothèque publique est terminée, mais j'ai toujours l'impression que la dernière insertion manuelle du script n'est pas élégante.Ce qui suit décrit comment importer automatiquement le fichier dll généré.
Ouvrez vue.config.js sous configureWebpack plugins configuration, continuez à configurer add-asset-html-webpack-plugin en fonction de configureWebpack à l'étape 4

// 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
           })
      )
     }
 }

Après avoir configuré la cinquième étape, redémarrez le projet et actualisez la page du navigateur pour voir les trois références js que nous venons de définir.
insérez la description de l'image ici

6. Les sous-applications utilisent ces dépendances publiques

En prenant le micro-frontend comme arrière-plan du projet, les cinq étapes de l'appel sont toutes configurées dans l'application principale, tandis que dans les sous-applications (applications qui doivent utiliser des dépendances publiques), notre configuration est relativement simple.
Ajoutez simplement la configuration de la quatrième étape à la configuration webpack de la sous-application correspondante, dites à webpack d'ignorer ces dépendances et mappez ces dépendances en fonction du manifeste.

//  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. L'introduction de dépendances à la demande (que cela dépende ou non de la situation)

En prenant ant-design-vue comme exemple, la bibliothèque de composants générale dépend des éléments suivants selon les besoins :

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

Il était à l'origine nécessaire d'importer à la demande, nous ne sommes donc pas adaptés pour empaqueter directement l'intégralité de ant-design-vue dans le module dll, donc l'entrée dans webpack.dll.conf.js doit être modifiée pour :

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

Inconvénients des plugins dll :

Parce que l'utilisation de dépendances publiques signifie que toutes les applications qui utilisent des dépendances publiques doivent utiliser la même version des dépendances. En fait, je pense que ce n'est pas une lacune. C'est une version obligatoire des dépendances. Après tout, quand il y a beaucoup de projets, les versions des dépendances doivent être unifiées, donc lors du développement de sous-applications indépendamment, les numéros de version doivent être cohérents lors de l'installation des dépendances publiques. Par exemple: dépendance echarts , l'application principale est 5.1.0 , et la sous-application est 4.1.0 , en raison des différentes méthodes de référence des echarts avec différentes versions, la sous-application dépendante ne peut pas faire référence à la dépendance publique de la main l'application et signale une erreur, de sorte que la sous-application ne peut pas démarrer.

Problèmes rencontrés dans le processus :

1. Lorsque des sous-applications utilisent des dépendances publiques, elles signalent parfois des erreurs de manière inexplicable, bien que je ne sache pas exactement ce qui se passe.
Par exemple, lorsque highcharts est utilisé (les références aux highcharts sont les mêmes), l'utilisation de l'application a est normale, mais l'application b signalera une erreur étrange, et l'application c est encore plus scandaleuse, et la sous-application ne peut pas être démarrée directement. . .
Les dépendances dll de ces trois références sont cohérentes :
insérez la description de l'image ici
le rapport d'erreur de l'application b :
insérez la description de l'image ici
le rapport d'erreur de l'application c :
insérez la description de l'image ici
après de nombreuses tentatives répétées, par recherche et exclusion, il s'est avéré que l'application b ne modifiait que l'ordre des modules dll, et simplement put highcharts Passez en premier, appliquez b et tout fonctionne bien ! (Que diable !!!)
insérez la description de l'image ici
Ensuite, utilisez c pour essayer de changer l'ordre, déplacez les highcharts à la première place, et c'est normal. . .

2. Citant la bibliothèque de composants antd, il y a un problème avec la fonction sélectionner tout de la table. En temps normal, l'utilisation de la page formulaire est normale, mais le formulaire est dans une fenêtre pop-up, cliquez pour tout sélectionner, et il est possible d'imprimer les données, c'est-à-dire qu'il y a bien un tout sélectionner, mais le nom de classe de checkd n'est pas ajouté, il semble que tous ne soient pas sélectionnés. Et je supprime la référence antd de la sous-application à la dll, et utilise à la place l'antd dans la sous-application node_module, donc il n'y a pas de problème, donc ce problème est bien lié au packaging du module dll. (L'antd de l'application principale est 1.7.5 et la sous-application est 1.7.2)
insérez la description de l'image ici
Afin de confirmer si cela a quelque chose à voir avec la version d'antd, j'ai mis à jour l'antd de la sous-application vers 1.7. 5, puis utilisé cette dépendance 1.7.5, Le résultat est normal (ça veut dire que ça n'a rien à voir avec la version 1.7.5 ?). Mais plus tard, j'ai changé la sous-application pour utiliser les dépendances dll, et j'ai changé la version de la bibliothèque de composants antd de l'application principale en version 1.7.2, puis j'ai reconditionné la dll (chaque fois que je reconditionne la dll, je redémarrerai le service de la sous-application), Le résultat résout le problème. Il est donc difficile de dire si le problème est lié à la version 1.7.5 ou non.

3. La taille du projet ne change pas après l'utilisation du package dll.
En fait, lorsque la sous-application utilise la dll pour ignorer les dépendances extraites, le point clé est de regarder le fichier de mappage xxx-manifest.json généré par la dll. Si le mappage contient les dépendances utilisées lors du développement de la sous-application (versions cohérentes des dépendances), la sous-application ignorera les dépendances extraites par la dll, réduisant ainsi le volume empaqueté. Si le volume ne change pas après l'empaquetage, il y a une forte probabilité qu'il s'agisse d'une sous-application ou d'une application qui doit ignorer les dépendances d'empaquetage dll. Ses dépendances sont incompatibles avec le mappage des dépendances d'empaquetage, donc les dépendances publiques de mon projet sont conçus comme suit :

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

Selon le mécanisme de recherche de dépendances (trouvez d'abord le fichier node_modules actuel, puis recherchez couche par couche jusqu'à la dépendance globale npm) et modifiez les dépendances non publiques en devDependencies et les dépendances publiques en dépendances.Cette conception n'est pas seulement pratique pour la gestion dépendances du projet (Garantie que le développement et l'utilisation des dépendances publiques sont les mêmes), et n'ont besoin que de npm install --only=dev pour installer les dépendances de devDependencies pendant le développement. Uniquement lorsque les sous-applications sont déployées indépendamment, npm install est requis pour installer toutes les dépendances.

conclusion du problème :

  1. Si vous rencontrez des problèmes liés au chargement des ressources, vous pouvez essayer d'ajuster l'ordre d'insertion des modules dll dans la sous-application, c'est-à-dire l'ordre dans le tableau à la sixième étape ci-dessus.
  2. Pour certaines dépendances d'application, s'il y a des problèmes fonctionnels, essayez de garder la version cohérente avec le développement de la sous-application, puis reconditionnez les dépendances dll.

Résumer:

externals et dll sont nécessaires pour les grands projets multi-applicatifs, car les dépendances répétées peuvent être extraites pour réduire considérablement le volume packagé et optimiser le chargement du projet. Au moins dans le projet micro-frontend que j'ai développé, après les tests, le volume est réduit de moitié. Cependant, pour un projet d'application unique, cela peut ne pas être très efficace. Tout au plus, le temps de conditionnement sera réduit, mais le volume ne changera peut-être pas beaucoup. Après tout, il y aura toujours des dépendances et la taille est difficile à modifier. Cependant, cela peut être envisagé dans le cas d'utilisation répétée de dépendances.Utilisez ces deux méthodes pour optimiser et réduire la taille du package.

Lors de l'empaquetage de la dll, rappelez-vous que vous n'avez pas besoin d'effectuer l'empaquetage de la dll à chaque fois, car la plupart des modules empaquetés par la dll sont des modules tiers inchangés. Généralement, vous n'avez besoin de l'empaqueter qu'une seule fois et de le stocker dans un emplacement fixe (par exemple : sous le public de l'application principale. ) et soumettez le code ensemble, ce qui peut également réduire le temps d'empaquetage des dépendances lors de la construction de l'exécution, et la dll doit être reconditionnée à chaque fois que les dépendances de la dll sont modifiées , et après avoir reconditionné la dll, n'oubliez pas de redémarrer le service pour la sous-application, si la dll dépend de l'ajout et de la suppression, n'oubliez pas également d'ajouter et de supprimer le module dll de la sous-application.

Supplément aux questions

Une fois les dépendances communes extraites, le bac à sable du framework qiankun est détruit, ce qui entraîne une interférence mutuelle entre les filtres globaux, les composants, les mixins, etc. dans différentes sous-applications. Contexte du problème Il existe une fonction de filtre byteToSize dans la sous-application
A
, mais l'application A ne l'utilise pas. La sous-application B dispose également de cette fonction, qui est utilisée dans une table. Les deux applications enregistrent leur fonction de filtrage en tant que filtre global et la montent sous l'instance de Vue. Après avoir empaqueté et mis en ligne, j'ai constaté que tant que j'ouvre d'abord la sous-application B, puis la sous-application A, puis que je reviens à la sous-application B, je constaterai que B utilise le filtre byteToSize de A.

Il semble que les deux fonctions byteToSize étant des fonctions globales, le byteToSize de la sous-application A écrite plus tard recouvre le byteToSize de la sous-application précédente B, mais les deux applications sandbox ne sont-elles pas isolées ? Pourquoi le code d'application suivant affecte-t-il le code d'application précédent ? Est-il possible que quelque soit l'application principale ou la sous-application, les plug-ins enregistrés tels que vue.use, filter, component, etc. soient partagés ?

1. Problèmes de vérification et de récurrence
Si la vue de toutes les applications est partagée (considérée comme étant la même), alors le plug-in d'enregistrement vue.use de l'application a déjà enregistré plusieurs côtés. Afin de prouver ce point, j'ai supprimé l'application d'une certaine sous-application, comme le plug-in antd. Après reconditionnement, j'ai ouvert la page de cette sous-application et j'ai constaté que tout est normal, c'est-à-dire, le plug-in antd de la bibliothèque de composants ui dans la sous-application n'est en fait plus nécessaire de s'enregistrer. Bien sûr, l'enregistrement répété de plug-ins de ce style n'a aucun effet, mais les filtres ou les composants globaux dans d'autres problèmes sont faciles à causer une pollution des conflits de couverture en raison d'un enregistrement à utilisation répétée.
Selon les résultats de l'analyse ci-dessus, les plug-ins enregistrés plus tard devraient écraser les précédents, donc chaque fois que vous cliquez sur une sous-application, ne devriez-vous pas charger vos filtres, composants, etc. actuels ? J'ai donc ajouté le code d'impression au main.js de chaque sous-application et constaté qu'il ne s'imprimerait que lorsque la sous-application serait chargée pour la première fois. C'est-à-dire que le déchargement des sous-applications dans le micro-frontend que j'ai compris auparavant ne consiste pas à tuer complètement les sous-applications sans aucune réserve, mais à désinstaller l'instance vue, et les sous-applications suivantes ne sont pas chargées pour la première fois, et ils ne sont même pas chargés. Le code dans main.js sera exécuté à nouveau, et la sous-application micro-frontend du même domaine n'est vraiment pas différente d'un projet vue (je ne peux pas m'empêcher de penser , s'il ne s'agit pas de déployer des projets front-end sur plusieurs domaines, il semble qu'il n'y ait vraiment pas besoin d'utiliser la technologie Micro-frontend, après tout, le framework vue est déjà développé de manière modulaire. même domaine est au moins meilleur que le développement modulaire sans micro-frontend, ce qui est indéniable).
2. La cause du problème
et lorsque je supprime la configuration dllplugin de la sous-application, reconditionne et affiche le système, la console signalera

Erreur de type non interceptée : impossible de redéfinir la propriété : $router

Erreur, car l'application principale utilise la balise de script pour introduire vue-router, la vue.use de la sous-application ne peut plus s'appuyer sur la vue-router dans ses propres node_modules, et ne peut utiliser que la vue-router du script balise de l'application principale, sinon elle le sera Une erreur est signalée car $router est défini à plusieurs reprises sous la même vue.
Impuissant, je ne peux qu'annuler la configuration des dépendances publiques de l'application principale, restaurer la configuration avant de ne pas utiliser les dépendances publiques, puis constater que le problème a disparu après l'empaquetage. Si vous vous fiez à la méthode introduite par la balise script de l'application principale, sous l'entrée import-html de Qiankun, il est impossible d'isoler le js des dépendances communes de la sous-application et de l'application principale, car après tout , ils coexistent sous la même fenêtre et sont enregistrés dans les dépendances publiques. Les variables globales sont partagées, contrairement à iframe qui a une forte isolation bac à sable JS.
3. Résoudre le problème
En fait, ce n'est rien d'autre que vue en tant que dépendance publique, qui provoquera des interférences entre les applications. Si je supprime complètement la dépendance vue des dépendances publiques, cela sera-t-il résolu ? Mais quand je l'enlève, j'ai trouvé que la console signalera toujours

Erreur de type non interceptée : impossible de redéfinir la propriété : $router

erreur. C'est très étrange. J'ai donc essayé de supprimer toutes les dépendances publiques liées à vue, et j'ai même essayé de ne laisser qu'une seule dépendance publique echarts, mais je n'ai pas pu supprimer cette erreur. De toute évidence, vue et vue-router ne sont pas introduits en tant que balises de script dans index.html, mais cette erreur sera toujours signalée ! Cette erreur ne sera pas signalée uniquement lorsque la sous-application n'utilise pas DllReferencePlugin. Il semble que DllReferencePlugin ait effectué une opération spéciale pour étendre la portée du contexte js de différentes sous-applications à l'ensemble du système. Maintenant, il semble que ce DllReferencePlugin semble casser le bac à sable js du framework qiankun.

Au final, l'idée a été résolue dans la zone de questions GitHub de qiankun :
insérez la description de l'image ici
en un mot, soit abandonner l'extraction des dépendances publiques, soit mettre à jour vue2.x vers vue3.x. Vue3.x utilise createApp pour s'enregistrer via des instances, afin qu'il n'y ait pas de pollution globale, c'est la meilleure solution. Quant à modifier le nom de la fonction de filtrage pour qu'il soit unique, je pense que ce n'est pas une solution fondamentale.Le nom de la méthode peut être unique, mais le minxin global n'a pas de nom, et il ne sera mélangé que dans toutes les pages de toutes les applications, ce qui est très mauvais.

Je suppose que tu aimes

Origine blog.csdn.net/weixin_43589827/article/details/118632092
conseillé
Classement