前端單頁應用微服務化解決方案4 - 消息總線

文章轉發自 alili.tech前端

微前端的消息總線,主要的功能是搭建模塊與模塊之間通信的橋樑.react

黑盒子

問題1:

應用微服務化以後,每個單獨的模塊都是一個黑盒子, 裏面發生了什麼,狀態改變了什麼,外面的模塊是無從得知的. 好比模塊A想要根據模塊B的某一個內部狀態進行下一步行爲的時候,黑盒子之間沒有辦法通訊.這是一個大麻煩.git

問題2

每個模塊之間都是有生命週期的.當模塊被卸載的時候,如何才能保持後續的正常的通訊?github

ps. 咱們必需要解決這些問題,模塊與模塊之間的通信太有必要了.redux

打破壁壘

在github上single-spa-portal-example,給出來一解決方案.bootstrap

基於Redux實現前端微服務的消息總線(不會影響在編寫代碼的時候使用其餘的狀態管理工具).api

大概思路是這樣的: 數組

每個模塊,會對外提供一個 Store.js.這個文件 裏面的內容,大體是這樣的.app

import { createStore, combineReducers } from 'redux'

const initialState = {
  refresh: 0
}

function render(state = initialState, action) {
  switch (action.type) {
    case 'REFRESH':
      return { ...state,
        refresh: state.refresh + 1
      }
    default:
      return state
  }
}

// 向外輸出 Reducer
export const storeInstance = createStore(combineReducers({ namespace: () => 'base', render }))

複製代碼

對於這樣的代碼,有沒有很熟悉? 對,他就是一個普通的Reducer文件, 每個模塊對外輸出的Store.js,就是一個模塊的Reducer.less

Store.js 如何被使用?

咱們須要在模塊加載器中,導出這個Store.js

因而咱們對模塊加載器中的Register.js文件 (該文件在上一章出現過,不懂的同窗能夠往回看)

進行了如下改造:

import * as singleSpa from 'single-spa';

//全局的事件派發器 (新增)
import { GlobalEventDistributor } from './GlobalEventDistributor' 
const globalEventDistributor = new GlobalEventDistributor();


// hash 模式,項目路由用的是hash模式會用到該函數
export function hashPrefix(app) {
...
}

// pushState 模式
export function pathPrefix(app) {
...
}

// 應用註冊
export async function registerApp(params) {
    // 導入派發器
    let storeModule = {}, customProps = { globalEventDistributor: globalEventDistributor };

    // 在這裏,咱們會用SystemJS來導入模塊的對外輸出的Reducer(後續會被稱做模塊對外API),統一掛載到消息總線上
    try {
        storeModule = params.store ? await SystemJS.import(params.store) : { storeInstance: null };
    } catch (e) {
        console.log(`Could not load store of app ${params.name}.`, e);
        //若是失敗則不註冊該模塊
        return
    }

    // 註冊應用於事件派發器
    if (storeModule.storeInstance && globalEventDistributor) {
        //取出 redux storeInstance
        customProps.store = storeModule.storeInstance;

        // 註冊到全局
        globalEventDistributor.registerStore(storeModule.storeInstance);
    }

    //當與派發器一塊兒組裝成一個對象以後,在這裏以這種形式傳入每個單獨模塊
    customProps = { store: storeModule, globalEventDistributor: globalEventDistributor };

    // 在註冊的時候傳入 customProps
    singleSpa.registerApplication(params.name, () => SystemJS.import(params.main), params.base ? (() => true) : pathPrefix(params), customProps);
}

複製代碼

全局派發器 GlobalEventDistributor

全局派發器,主要的職責是觸發各個模塊對外的API.

GlobalEventDistributor.js

export class GlobalEventDistributor {

    constructor() {
        // 在函數實例化的時候,初始一個數組,保存全部模塊的對外api
        this.stores = [];
    }

    // 註冊
    registerStore(store) {
        this.stores.push(store);
    }

    // 觸發,這個函數會被種到每個模塊當中.便於每個模塊能夠調用其餘模塊的 api
    // 大體是每一個模塊都問一遍,是否有對應的事件觸發.若是每一個模塊都有,都會被觸發.
    dispatch(event) {
        this.stores.forEach((s) => {
            s.dispatch(event)
        });
    }

    // 獲取全部模塊當前的對外狀態
    getState() {
        let state = {};
        this.stores.forEach((s) => {
            let currentState = s.getState();
            console.log(currentState)
            state[currentState.namespace] = currentState
        });
        return state
    }
}

複製代碼

在模塊中接收派發器以及本身的Store

上面提到,咱們在應用註冊的時候,傳入了一個 customProps,裏面包含了派發器以及store. 在每個單獨的模塊中,咱們如何接收而且使用傳入的這些東西呢?

import React from 'react'
import ReactDOM from 'react-dom'
import singleSpaReact from 'single-spa-react'
import RootComponent from './root.component'
import { storeInstance, history } from './Store'
import './index.less'


const reactLifecycles = singleSpaReact({
  React,
  ReactDOM,
  rootComponent: (spa) => {
    // 咱們在建立生命週期的時候,把消息總線傳入的東西,以props的形式傳入組件當中
    // 這樣,在每一個模塊中就能夠直接調用跟查詢其餘模塊的api與狀態了
    return <RootComponent store={spa.customProps.store.storeInstance} globalEventDistributor={spa.customProps.globalEventDistributor} /> }, domElementGetter: () => document.getElementById('root') }) export const bootstrap = [ reactLifecycles.bootstrap, ] export const mount = [ reactLifecycles.mount, ] export const unmount = [ reactLifecycles.unmount, ] 複製代碼

未完待續 ...

相關文章

前端單頁應用微服務化解決方案1 - 思考

前端單頁應用微服務化解決方案2 - Single-SPA

前端單頁應用微服務化解決方案3 - 模塊加載器

前端單頁應用微服務化解決方案4 - 消息總線

前端單頁應用微服務化解決方案5 - 路由分發

前端單頁應用微服務化解決方案6 - 構建與部署

前端單頁應用微服務化解決方案7 - 靜態數據共享

前端單頁應用微服務化解決方案8 - 二次構建

Demo

前端微服務化 Micro Frontend Demo

微前端模塊加載器

微前端Base App示例源碼

微前端子項目示例源碼

相關文章
相關標籤/搜索