webpack5-模块共享方案

webpack5-模块共享方案,第1张

目录
    • 一、引用
      • 1.1、npm包
      • 1.2、微前端
      • 1.3、其它方案
    • 二、模块联邦(module Federation)
    • 三、实践
      • 3.1、REMOTE1配置
      • 3.2、HOST宿主配置
      • 3.3、跨技术栈
        • 3.3.1、REMOTE2配置
    • 四、依赖共享
    • 五、总结

一、引用

在日常开发中,我们经常会有一种感觉,这个组件我在其它项目改了,又要copy一次?在协同开发的过程中磕磕碰碰,皆为了结果尽善尽美,手段无所不用其极,CV大法更是用的出神入化,后面发现副作用极其明显,项目杂乱、代码冗余等问题,尽管有相应的规范也难以约束人性,我们无时无刻不想把项目拆分、把模块抽离。

面对这个问题,我们通常会采用以下一些方案解决:

1.1、npm包


通常使用的将需要公共模块抽离通过npm发布,多系统引用实现模块共享,这也是常见的一种方案,但是都需要建立在模块比较稳定,不易经常改动,否则一旦模块改动需要去手动更新依赖,成本高还容易遗漏。

1.2、微前端

先看看网上十分火的一张图

微前端就是构建一个现代 Web 应用所需要的技术、策略和方法,并具备多个团队独立开发、部署的特性;微前端并不是一种新的技术,而是为了提高大型复杂应用的多团队协作,提升项目的可维护性、拓展性和灵活性,研究出的一种技术策略和方法,它是一种宏观上的架构模式;现在社区也有一些开源活跃方案如乾坤microapp等,各有优缺点。

引用一位大佬说的:

@kuitos-乾坤:
满足以下几点,你可能就不需要微前端

  • 系统里的所有组件都是由一个小的团队开发,并有足够动力去治理、改造这个系统中的所有组件
  • 系统及组织架构上,各部件之间本身就是强耦合、自洽、不可分离
  • 极高的产品体验要求,对任何产品交互上的不一致零容忍

满足以下几点,你可能就不需要微前端

  • 系统里的所有组件都是由一个小的团队开发,并有足够动力去治理、改造这个系统中的所有组件
  • 系统及组织架构上,各部件之间本身就是强耦合、自洽、不可分离
  • 极高的产品体验要求,对任何产品交互上的不一致零容忍

除此,还要考虑入侵程度、带来的性价比、共享公共依赖等因素。

1.3、其它方案
  • 打包UMD格式发布cdn
  • 打包为ESmodule模块,采用systemjs加载模块

本文要介绍的重点是webpack5出行的module Federation模块联邦。

二、模块联邦(module Federation)

@webpack官方
多个单独的构建应该形成一个应用程序。这些单独的构建之间不应该有依赖关系,因此它们可以单独开发和部署;这通常被称为微前端,但不限于此。

我们可以用module Federation实现去中心话的应用部署群,应用之前模块共享,如下:

上图所示,一个系统既可以作为模块的提供方也可以作为模块的消费者,公用模块我们不需要抽离,只需要通过配置将模块暴露给消费者即可,如此,就可以借助module Federation在一定程度上实现微前端。

三、实践

新建两个vue项目

  • HOST本地宿主系统,作为消费者
  • REMOTE1远程项目,作为提供者
3.1、REMOTE1配置

当前配置为提供方,暴露出的模块供消费者使用:

vue.config.js

const {ModuleFederationPlugin} = require('webpack').container;
const pkg = require('./package.json')
let publicPath = 'auto'
if (process.env.NODE_ENV === 'production') {
  publicPath = 'http://localhost:10000'
}
const config = {
  publicPath,
  devServer: {
    port: '10000',
    headers: {}
  },
  configureWebpack: {
    optimization: {
      splitChunks: false,
    },
    plugins: [
      new ModuleFederationPlugin({
        name: 'vue2Project',
        filename: 'remoteProvide.js',
        exposes: {
          './HelloWorld': './src/components/HelloWorld',
          './ElementUI': './src/element.js'
        },
        shared: {
          ...pkg.dependencies,
          vue: {
            singleton: true,
            requiredVersion: pkg.dependencies.vue,
            eager: true
          }
        }
      })
    ]
  }
}

module.exports = config

说明:exposes、filename、name都供消费者使用

  • name声明当前联邦模块的名称vue2Project
  • 以filename声明的文件名生成remoteProvide.js
  • exposes暴露了项目中HelloWorld组件和项目的ElementUI组件库
3.2、HOST宿主配置

当前配置为消费者,获取REMOTE1暴露出的组件

vue.config.js

const { ModuleFederationPlugin } = require('webpack').container;
module.exports = {
  configureWebpack: {
    optimization: {
      splitChunks: false,
    },
    plugins: [
      new ModuleFederationPlugin({
        name: 'host',
        filename: 'hello-world.js',
        remotes: {
          'vue2Project': 'vue2Project@http://localhost:10000/remoteProvide.js',
        },
        shared: {
          vue: {
            eager: true,
            import: "vue",
            shareKey: "vue",
            shareScope: "default",
            singleton: true
          }
        }
      })
    ]
  }
}

说明:

  • 只需要关注remotes,形势如:[hostName]:[remoteName]@[remoteAddr]
    hostName:本地需要调用的别名,随意命名
    remoteName:远程项目的name,如remote1的vue2Project
    remoteAddr:远程项目的地址,如remote1的remoteProvide.js

使用组件:

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld />
    <el-button type="primary">test</el-button>
  </div>
</template>

<script>
import HelloWorld from 'vue2Project/HelloWorld'
import {ElementUI} from 'vue2Project/ElementUI'
export default {
  name: 'App',
  components: {
    HelloWorld,
    [ElementUI.Button.name]: ElementUI.Button
  }
}
</script>

先看下暴露出来的是个啥?


说明:REMOTE1暴露的组件已经全部拿到了,标准的vue组件,我们正常引入即可

  • 引入HelloWorldElementUI的Button组件,格式按hostName/exposes.[key]形势引入

呈现效果:

实时修改remote1的HelloWorld组件代码之后,呈现效果:

已经可以实现了组件共享,由REMOTE1提供的组件在HOST宿主系统使用,并且修改了组件可以做到实时更新。

3.3、跨技术栈

有时我们希望能够接入其它的技术栈的组件,以react为例,create-react-app脚手架新建项目

  • REMOTE2远程项目,作为提供者
3.3.1、REMOTE2配置

webpack.config.js增加配置提供消费者使用的组件

plugins: [
	new webpack.container.ModuleFederationPlugin({
		name: 'react2Project',
		// library: { type: 'var', name: 'vue2Project' },
		filename: 'remoteProvide.js',
		exposes: {
		  './App': './src/App.js',
		}
	}),
	......其它配置,
]

说明:

  • 暴露当前的App.js组件提供消费者使用

在HOST宿主系统中增加配置,vue.config.js增加remotes配置react2Project:

remotes: {
	'vue2Project': 'vue2Project@http://localhost:10000/remoteProvide.js',
	'react2Project': 'react2Project@http://localhost:3000/remoteProvide.js',
},

在组件中使用

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld />
    <el-button type="primary">test</el-button>
	<div id="reactApp"></div>
  </div>
</template>

<script>
import HelloWorld from 'vue2Project/HelloWorld'
import {ElementUI} from 'vue2Project/ElementUI'
import ReactApp from 'react2Project/App'
// eslint-disable-next-line no-unused-vars
import React from 'react'
import { createRoot } from 'react-dom/client';
export default {
  name: 'App',
  mounted() {
    const container = document.getElementById('reactApp');
    const root = createRoot(container);
    root.render(<ReactApp/>);
  },
  components: {
    HelloWorld,
    [ElementUI.Button.name]: ElementUI.Button
  }
}
</script>

说明:

  • vue中mounted钩子触发才确定能拿到reactApp的dom,所以react代码需要写在挂载完成钩子中。
  • 依赖reactreact-dom,需要安装
  • 依赖react的jsx语法,需要安装react的jsx插件@babel/plugin-transform-react-jsx,同时配置babel插件plugins: ["@babel/plugin-transform-react-jsx"]

呈现效果:

在使用跨技术栈接入需要做相应的兼容才能实现,现在也有一些三方库做了兼容方案,比如vuera…等

四、依赖共享

在如上的实践之后,有时候也希望一次只运行一个库实例,解决多版本导致的兼容问题等,module Federation提供了方案,如下在宿主系统和远程系统配置:

new ModuleFederationPlugin({
	// 其它配置...,
	shared: {
	  ...pkg.dependencies,
	  vue: {
	    requiredVersion: pkg.dependencies.vue,
	    eager: true
	  }
	}
})

以上我配置了生产环境所有的依赖,如果在宿主系统中有配置相关依赖,就可根据配置做到共享依赖,webpack还提供了其它共享依赖更详细的配置,详见shared配置

demo代码地址

五、总结
  • module Federation可以直接使用,为更大型的前端应用提供了开箱解决方案
  • 需要升级为webpack5
  • 跨技术栈需要做兼容技术方案

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/870966.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)

保存