手把手教你如何使用 webpack5 的模块联邦新特性

  • A+
所属分类:Web前端
摘要

想象一下,在webpack5还没出来前,前端使用第三方组件库,例如使用 dayjs 日期处理库,都是通过 npm i dayjs -s 安装 dayjs 模块到项目里,然后就可以通过 require 或者 import 来使用。整个过程也不是很麻烦,但是如果某一天 dayjs 这个库修复了一个重大bug,我们得重新通过 npm 来安装最新版本,然后重启项目,那么有没有一个更简单的方案呢?

想象一下,在webpack5还没出来前,前端使用第三方组件库,例如使用 dayjs 日期处理库,都是通过 npm i dayjs -s 安装 dayjs 模块到项目里,然后就可以通过 require 或者 import 来使用。整个过程也不是很麻烦,但是如果某一天 dayjs 这个库修复了一个重大bug,我们得重新通过 npm 来安装最新版本,然后重启项目,那么有没有一个更简单的方案呢?

本文首发于:https://www.1024nav.com/blog/webpck5-module-federation

模块联邦

为了减少对 npm 包的安装频率,还有避免本地安装 node_modules 包,webpack开发团队想出了一个解决方案,能否将第三方库放到网络上单独部署,如果项目需要使用再从该库的地址上直接拉取下来使用,这样就不需要通过 npm 来安装,更新指定依赖包,也方便实时保持库的新版本。感觉有点类似以前通过 cdn 引入第三方js库的感觉

模块联邦还可以实现微前端服务

什么是微前端

微前端是一个技术栈无关的多应用集成思想,将一个大应用拆分成多个子应用来开发,每个子应用可以使用自己的技术栈(vue,angularjs,reactjs,jquery)进行独立开发,部署。最后通过主应用基座来集成各个子应用。具体细节这里不展开讲

模块联邦如何实现模块共享

webpack提供了一个 ModuleFederationPlugin 插件,能够实现模块的发布和引入, ModuleFederationPlugin 插件常用的配置有以下几个

  • name : 必须,唯一的库名,使用者通过 [name]/[exposes]使用
  • filename :必须,暴露的文件名,在使用者通过 remotes 来引入
  • library :可选, umd的名称,类似于jQuery的$,lodash的_
  • exposes : 可选,配置暴露指定的模块,供其他人使用
  • remotes : 可选,表示使用其他远程的模块
  • shared :可选, 配置共享库,例如 shared: ['react', 'react-dom'] 意思是宿主和引用依赖的模块共享配置的库,如果host有该库则不需要再次下载,直接使用host已有的库

接下来一步一步实现一个模块联邦功能

模块的开发和部署

通过CRA(create react app)来创建一个应用。我们创建一个 common-component 项目

然后配置 ModuleFederationPlugin ,在 config-overrides.js 文件里面新增以下配置。关于 cra 如何自定义 webpack 配置可以查看这篇文章

module.exports = override(   config => {     config.output.publicPath = 'http://localhost:3000/';     return config;   },   addWebpackPlugin(new ModuleFederationPlugin({     name: 'ccomponent',     filename: "remoteEntry.js",     exposes: {       './button': './src/button/index.jsx',     }   })), ); 

注意这里的 publicPath 是必须的,获取组件的时候会从当前 host 下获取 button 这个组件,通过 ModuleFederationPlugin 的 exposes 选项暴露 button 组件。

我们在 src 新建一个 button 组件

// src/button/index.jsx import React from 'react' import PropTypes from 'prop-types'  const Button = props => {   const {     children   } = props;   return (     <button>       { children }     </button>   ) }  Button.propTypes = {   children: PropTypes.node.isRequired }  export default Button 

通过 npm run start 启动,这时候可以访问到 http://localhost:3000/remoteEntry.js

模块的使用

我们再通过 CRA 创建一个名称 app 的项目,同样的在 config-overrides.js 里面引入 ModuleFederationPlugin

module.exports = override(   addWebpackPlugin(new ModuleFederationPlugin({     name: 'app',     remotes: {       'ccomponent': 'ccomponent@http://localhost:3000/remoteEntry.js',     }   })), ); 

cccomponent 是上面 common-component 项目里面配置的 name

然后在 app 里面使用 common-component 的 button 组件,这里使用 React.lazy 来异步加载该组件

const Button = React.lazy(() => import("ccomponent/button"));  function App() {   return (     <div className="App">       <React.Suspense fallback="Loading Component">         <Button>hello</Button>       </React.Suspense>     </div>   ); } 

到此为止,就可以在 app 项目里面看到 button 了,666~

总结

webpack提供对模块联邦会使以后模块使用更加方便,快捷,一切工具都是为了方便开发,给 webpack 的贡献者点个赞!