微前端最近是個很火的概念,社區其實也有很多成熟的方案去實現。做爲一個umi
的重度使用者,就必須來體驗一下阿里基於single-spa
開發的qiankun
了,而umi跟qiankun的最佳結合方案,無疑是qiankun爲umi量身訂造的插件@umijg/plugin-qiankun
。那咱們就跟着官網的文檔開造吧。html
npx @umijs/create-umi-app
npm install
npm install @umijs/plugin-qiankun --save
npm start
共建立3個項目,一個做爲主應用,2個做爲子應用前端
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
}
],
},
},
};
複製代碼
export default {
routes: [
{
path: '/',
component: '../layouts/index.js',
routes: [
{
path: '/app1',
microApp: 'app1',
// 配置Loading屬性
microAppProps: {
autoSetLoading: true,
className: 'myContainer',
wrapperClassName: 'myWrapper',
}
},
{
// app2
// ...
}
],
},
],
}
複製代碼
package.json
中配置"name": "app1"
。同時也須要配置.umirc.ts文件開啓qiankun。export default {
qiankun: {
slave: {}
}
}
複製代碼
.env
中:PORT=8001
HMR=none
複製代碼
若是遇到了由於dev scripts端口號等問題產生衝突以至於報錯,能夠先關閉HMR試試react
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
中配置proxy
。npm
這時候應用之間是基本獨立的,咱們還須要加入供應用間通信的全局狀態json
這裏主要使用@umijs/plugin-model
的useModel
來將數據全局化。umi應該已經內置了@umijs/preset-react
插件集,其中就包括了@umijs/plugin-model
bootstrap
在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
子應用app2將主應用的globalState從aaa改爲bbb
每一箇中後臺系統,基本都是須要獲取用戶權限來限制某些頁面的訪問權。那麼在微前端這種使用場景,最好是由主應用來統一管理每一個子應用的用戶權限等信息。最好還能不改變原來單獨訪問子應用時的邏輯。
假設token放在localStorage中,這裏想到了2個方案:
子應用經過當前域名識別出是否位於主應用,是則不走本身的登陸校驗邏輯。
本人也嘗試接入umi2版本的項目,即按照對應版本的qiankun插件
npm install @umijs/plugin-qiankun@umi2 --save
也能運行起來
可是這個版本的qiankun插件,子應用並不能使用@@qiankunStateForSlave
來獲取masterProps,這個版本是使用const masterProps = useRootExports()
來獲取masterProps。顯然這樣的話主應用還得額外處理供umi2子應用使用的全局state,感受不是很方便。