手把手教你如何使用 webpack5 的模組聯邦新特性

想像一下,在webpack5還沒出來前,前端使用第三方組件庫,例如使用 dayjs 日期處理庫,都是通過 npm i dayjs -s 安裝 dayjs 模組到項目里,然後就可以通過 require 或者 import 來使用。整個過程也不是很麻煩,但是如果某一天 dayjs 這個庫修復了一個重大bug,我們得重新通過 npm 來安裝最新版本,然後重啟項目,那麼有沒有一個更簡單的方案呢?

本文首發於://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 = '//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 啟動,這時候可以訪問到 //localhost:3000/remoteEntry.js

模組的使用

我們再通過 CRA 創建一個名稱 app 的項目,同樣的在 config-overrides.js 裡面引入 ModuleFederationPlugin

module.exports = override(
  addWebpackPlugin(new ModuleFederationPlugin({
    name: 'app',
    remotes: {
      'ccomponent': 'ccomponent@//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 的貢獻者點個贊!