umi3 + qiankun 實現一個簡單的微前端demo | 🏆 技術專題第四期徵文

微前端最近是個很火的概念,社區其實也有很多成熟的方案去實現。做爲一個umi的重度使用者,就必須來體驗一下阿里基於single-spa開發的qiankun了,而umi跟qiankun的最佳結合方案,無疑是qiankun爲umi量身訂造的插件@umijg/plugin-qiankun。那咱們就跟着官網的文檔開造吧。html

Demo實現

建立項目

  1. npx @umijs/create-umi-app
  2. npm install
  3. npm install @umijs/plugin-qiankun --save
  4. npm start

共建立3個項目,一個做爲主應用,2個做爲子應用前端

配置主應用

  1. 註冊子應用。在主應用的.umirc.ts中,開啓qiankun。這是umi3插件的一種開啓方式,只要插件的key(這裏的plugin-qiankun的key就是qiankun)有對應的配置就會開啓qiankun。
export default {
  qiankun: {
    master: {
      // 註冊子應用信息
      apps: [
        {
          name: 'app1', // 惟一 id,須要與微應用中的package.json中的name一致
          entry: '//localhost:8001', // html entry
        },
        {
          name: 'app2', // 惟一 id,須要與微應用中的package.json中的name一致
          entry: '//localhost:8002', // html entry
        }
      ],
    },
  },
};
複製代碼
  1. 裝載子應用。在須要加載子應用的路由上,配置microApp屬性爲子應用的name,便可與路由綁定
export default {
    routes: [
    {
      path: '/',
      component: '../layouts/index.js',
      routes: [
   		{
           path: '/app1',
           microApp: 'app1',
           // 配置Loading屬性
           microAppProps: {
            autoSetLoading: true,
            className: 'myContainer',
            wrapperClassName: 'myWrapper',
          }
        },
        {
          // app2
          // ...
        }
      ],
    },
  ],
}
複製代碼

配置子應用

  1. 插件註冊。在package.json中配置"name": "app1"。同時也須要配置.umirc.ts文件開啓qiankun。
export default {
  qiankun: {
    slave: {}
  }
}
複製代碼
  1. 環境變量配置。在.env中:
PORT=8001
HMR=none
複製代碼

若是遇到了由於dev scripts端口號等問題產生衝突以至於報錯,能夠先關閉HMR試試react

  1. 若是子應用須要在生命週期中加一些自定義邏輯,則須要在src/app.ts中導出一個對象qiankun
export const qiankun = {
  // 應用加載以前
  async bootstrap(props) {
    console.log('app1 bootstrap', props);
  },
  // 應用 render 以前觸發
  async mount(props) {
    console.log('app1 mount', props);
  },
  // 應用卸載以後觸發
  async unmount(props) {
    console.log('app1 unmount', props);
  },
};
複製代碼

如今運行3個項目,就能經過主應用來訪問子應用了,例如訪問http://localhost:8000/app1顯示的是子應用的頁面。這裏須要注意的是,子應用的端口號是_8001_,那麼原來的子應用發出的處於同一域名下的後端請求也是在_8001_端口上,但經過主應用來訪問,後端請求就會變爲_8000_端口,須要作一個代理轉發,能夠在.umirc.ts中配置proxynpm

這時候應用之間是基本獨立的,咱們還須要加入供應用間通信的全局狀態json

應用間通信

這裏主要使用@umijs/plugin-modeluseModel來將數據全局化。umi應該已經內置了@umijs/preset-react插件集,其中就包括了@umijs/plugin-modelbootstrap

主應用生產數據

src/app.ts中導出一個函數useQiankunStateForSlave,這個函數返回的內容會注入到props中傳遞給子應用。這裏有個坑,useState返回的setXXX方法不能命名爲setGlobalState,否則會在mount階段被qiankun的另外一個同名函數覆蓋。後端

// src/app.ts
export function useQiankunStateForSlave() {
const [globalState, setQiankunGlobalState] = useState({ str: 'aaa'})

  return {
    globalState,
    setQiankunGlobalState
  };
}
複製代碼

若是主應用也須要用到這個globalState的話,也能夠在組件中藉助useModel訪問@@qiankunStateForSlave:markdown

const { globalState } = useModel('@@qiankunStateForSlave');app

子應用消費數據

子應用經過useModel訪問@@qiankunStateFromMaster這個model以獲取主應用傳過來的propsasync

export default () => {
  const masterProps = useModel('@@qiankunStateFromMaster');

  useEffect(() => {
    masterProps.setQiankunGlobalState({ str: 'bbb' })
  }, [])

  return (
    <p>{JSON.stringify(masterProps.globalState)}</p>
  );
}
複製代碼

這樣,主應用負責管理globalState,子應用也能取到globalState並調用masterProps上的方法來通知主應用修改globalState

Demo 演示

子應用app2將主應用的globalState從aaa改爲bbb 微前端Demo

一些思考

如何管理應用權限

每一箇中後臺系統,基本都是須要獲取用戶權限來限制某些頁面的訪問權。那麼在微前端這種使用場景,最好是由主應用來統一管理每一個子應用的用戶權限等信息。最好還能不改變原來單獨訪問子應用時的邏輯。

假設token放在localStorage中,這裏想到了2個方案:

  1. 【統一處理】一旦進入任一子應用,主應用取出token發出一個checkLogin請求,後端檢查2個子應用的登陸態,只要任一沒登陸就觸發login邏輯,若是都登陸了就返回2個子應用的userInfo,主應用再分別傳遞userInfo給子應用。(須要後端加接口支持)
  2. 【分開處理】一旦進入任一子應用,主應用取出2個token發出2個checkLogin請求,經轉發至子應用的域名後,子應用的2個後端各自檢查登陸態,有則返回userInfo。主應用只要沒有拿到2份userInfo,就觸發login邏輯,再分別傳遞userInfo給子應用。

子應用經過當前域名識別出是否位於主應用,是則不走本身的登陸校驗邏輯。

能支持Umi2嗎?

本人也嘗試接入umi2版本的項目,即按照對應版本的qiankun插件

npm install @umijs/plugin-qiankun@umi2 --save

也能運行起來

可是這個版本的qiankun插件,子應用並不能使用@@qiankunStateForSlave來獲取masterProps,這個版本是使用const masterProps = useRootExports()來獲取masterProps。顯然這樣的話主應用還得額外處理供umi2子應用使用的全局state,感受不是很方便。

參考資料

umi官網 @umijs/plugin-qiankun

🏆 技術專題第四期 | 聊聊微前端的那些事......

相關文章
相關標籤/搜索