官網(wǎng)地址:https://qiankun.umijs.org/
微前端是一種多個團隊通過獨立發(fā)布功能的方式來共同構建現(xiàn)代化 web 應用的技術手段及方法策略
·獨立開發(fā)精算、獨立部署
主應用
1. 安裝 qiankun
$ yarnadd qiankun # 或者 npm i qiankun -S
2. 在主應用中注冊子應用(主應用的main.js)
import{ registerMicroApps, start }from'qiankun';
registerMicroApps([
{
name:'react app',// app name registered
entry:'//localhost:7100', //子應用的地址(不包含標識和路由)
container:'#vue’,// 子應用掛載的div
activeRule:'/yourActiveRule', // 子應用的標識仰美,路由中會詳細說明
},
]);
// 啟動 qiankun
start();
手動加載本項目暫不需要。當子應用信息注冊完之后叔扼,一旦瀏覽器的 url 發(fā)生變化官研,便會自動觸發(fā) qiankun 的匹配邏輯藻治,所有 activeRule 規(guī)則匹配上的子應用就會被插入到指定的 container 中载矿,同時依次調用子應用暴露出的生命周期鉤子限书。
如果子應用不是直接跟路由關聯(lián)的時候虫蝶,你也可以選擇手動加載子應用的方式:
import{ loadMicroApp }from'qiankun';
loadMicroApp(
{
name:'app',
entry:'//localhost:7100',
container:'#yourContainer',
}
);
子應用
子應用不需要額外安裝任何其他依賴即可接入 qiankun 主應用。
1倦西、入口文件 main.js修改能真,為了避免根 id #app與其他的 DOM 沖突,需要限制查找范圍,在container范圍中查找
import'./public-path';
import Vue from'vue';
import VueRouter from'vue-router';
import App from'./App.vue';
import routes from'./router';
import store from'./store';
Vue.config.productionTip =false;
let router =null;
let instance =null;
functionrender(props ={}){
//為了避免根 id #app 與其他的 DOM 沖突扰柠,需要限制查找范圍
const{ container }= props;
router =newVueRouter({
//如果是子應用就設置標識
base:window.__POWERED_BY_QIANKUN__ ?'/app-vue/':'/',
mode:'history',
routes,
});
instance =newVue({
router,
store,
render: h =>h(App),
}).$mount(container ? container.querySelector('#app'):'#app');
}
// 是否獨立運行
if(window.__POWERED_BY_QIANKUN__){
__webpack_public_path__ =window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}else{
render();
}
// 導出相應的生命周期鉤子
exportasyncfunctionbootstrap(){
console.log('[vue] vue app bootstraped');
}
exportasyncfunctionmount(props){
console.log('[vue] props from main framework', props);
render(props);
}
exportasyncfunctionunmount(){
instance.$destroy();
instance.$el.innerHTML ='';
instance =null;
router =null;
}
2.打包配置修改(vue.config.js):
設置跨域和打包格式
const{ name }=require('./package');// 不重要粉铐。自己起也可以
module.exports ={
devServer:{
headers:{
'Access-Control-Allow-Origin':'*',// 設置跨域
},
},
configureWebpack:{
output:{
library:`${name}-[name]`,// 配置導出庫的名稱
libraryTarget:'umd',// 把子應用打包成 umd 庫格式
jsonpFunction:`webpackJsonp_${name}`,//不是必須的
},
},
};
3.子應用生命周期鉤子介紹
子應用需要在自己的入口 js (通常就是你配置的 webpack 的 entry js) 導出 bootstrap、mount卤档、unmount三個生命周期鉤子蝙泼,以供主應用在適當?shù)臅r機調用。
/**
bootstrap 只會在子應用初始化的時候調用一次劝枣,下次子應用重新進入時會直接調用 mount 鉤子汤踏,不會再重復觸發(fā) bootstrap。
通常我們可以在這里做一些全局變量的初始化舔腾,比如不會在 unmount 階段被銷毀的應用級別的緩存等溪胶。
*/
exportasyncfunctionbootstrap(){
console.log('react app bootstraped');
}
/**
- 應用每次進入都會調用 mount 方法,通常我們在這里觸發(fā)應用的渲染方法
*/
exportasyncfunctionmount(props){
ReactDOM.render(<App />, props.container ? props.container.querySelector('#root'): document.getElementById('root'));
}
/**
- 應用每次 切出/卸載 會調用的方法稳诚,通常在這里我們會卸載子應用的應用實例
*/
exportasyncfunctionunmount(props){
ReactDOM.unmountComponentAtNode(props.container ? props.container.querySelector('#root'): document.getElementById('root'));
}
/**
- 可選生命周期鉤子哗脖,僅使用 loadMicroApp 方式(手動掛載)加載子應用時生效
*/
exportasyncfunctionupdate(props){
console.log('update props', props);
}
父子通信(父子應用store分離的方案實現(xiàn))
使用vuex的原因是為了獲取主應用的實時數(shù)據(jù),也為了實現(xiàn)子應用主應用的相互通信扳还。任何項目中vuex都有base.js才避,用來存儲公共信息。比如token氨距、用戶名等工扎。(現(xiàn)項目token等存儲在localstorage中)子應用單獨啟動時用自己的vuex中的base.js來存儲信息,base.js只能主應用進行修改衔蹲,同步到子應用肢娘。(不考慮子應用單獨運行可以不在子應用維護base.js)
step1:主應用向子應用傳遞store實例。
step2:子應用使用主應用共享的store實例并設置只屬于子應用的vuex
針對第一種情況舆驶,就是在入口文件中引入vuex橱健,并使用該插件,進而在創(chuàng)建vue實例的時候沙廉,傳入主應用共享的store拘荡。
Vuex正常使用的時候,所有的狀態(tài)值都是響應式的撬陵,可以直接用于Vue頁面之中珊皿,但是這里是非響應式的网缝,導致這個的原因其實十分簡單。這里的store實例是由主應用傳遞過來的蟋定,store中的狀態(tài)對于主應用的vue實例而言是親兒子粉臊,是響應式的,在子應用中驶兜,雖然可以使用共享store實例中的commit方法扼仲,但是對于子應用的實Vuex例而言,不是親兒子抄淑,是非響應式的屠凶,這樣分析之后,解決方案就十分明確:
在子應用中將共享的****store****實例進行響應式設置肆资,這是****Vue****現(xiàn)有的****API****方法****Vue.observable(store)
子應用的文件夾格式和使用方式
step4:子應用之間通信
子應用之間的通信可以通過父應用的vuex來傳遞矗愧。
登錄
1.1把子應用的登錄頁面移到主應用
1.2保留子應用的登錄,保證可以單獨啟動(目前登錄信息存儲在localstorage中)
路由
1.3因為qiankun的特性主應用調用子應用是路由前要加子項目標識
列:子應用中路由為/index郑原,子應用標識為/ child01贱枣,主應用的該路由就為/child01 /index
主應用和子應用設置標識必須相同
后臺返回的權限路由都要加子應用標識
遇到的問題
1、應用的部署颤专,需要通過nginx做反向代理,把指向子應用的訪問路徑轉發(fā)到子應用的服務入口
2钠乏、子應用套孫子應用未考慮栖秕,一個頁面多個子應用未考慮;
應用部署nginx
子應用中需配置晓避,跨域
location / {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested- With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
if ($request_method = 'OPTIONS') {
return 204;
}
}
讓微應用文件更新之后訪問新的文件
問題描述:發(fā)版vue項目后簇捍,總是要強制刷瀏覽器才能生效
vue-cli里的默認配置,css和js的名字都加了哈希值俏拱,所以新版本css暑塑、js和就舊版本的名字是不同的,不會有緩存問題锅必。
但是把打包好的index.html放到服務器里去的時候事格,index.html在服務器端可能是有緩存的,這需要在服務器配置不讓緩存index.html
解決方法如下:
前端在index.html中添加:
<meta http-equiv="Expires" content="0">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-control" content="no-cache">
<meta http-equiv="Cache" content="no-cache">
nginx 配置如下:
location = /index.html {
add_header Cache-Control "no-cache, no-store";
}
開發(fā)規(guī)范
1.所有的資源(圖片/音視頻等)都應該放到src目錄搞隐,不要放在public或者static
資源放 src 目錄驹愚,會經(jīng)過 webpack 處理,能統(tǒng)一注入 publicPath劣纲。否則在主項目中會404逢捺。
2.請給 axios 實例添加攔截器,而不是 axios 對象
// 正確做法:給 axios 實例添加攔截器
const instance = axios.create();
instance.interceptors.request.use(function () {/.../});
// 錯誤用法:直接給 axios 對象添加攔截器
axios.interceptors.request.use(function () {/.../});
3.避免 css 污染
組件內樣式的css-scoped是必須的癞季。
對于一些插入到body的彈窗劫瞳,無法使用scoped倘潜,請不要直接使用原class修改樣式,請?zhí)砑幼约旱腸lass志于,來修改樣式涮因。盡量不將元素插入body
.el-dialog{
/* 不推薦使用組件原有的class */
}
.my-el-dialog{
/* 推薦使用自定義組件的class */
}
4.謹慎使用 position:fixed
在父項目中,這個定位未必準確恨憎,應盡量避免使用蕊退,確有相對于瀏覽器窗口定位需求,可以用position: sticky憔恳,但是會有兼容性問題(IE不支持)瓤荔。如果定位使用的是bottom和right,則問題不大钥组。
5.給 body 输硝、 document 等綁定的事件,請在 unmount 周期清除
js 沙箱只劫持了 window.addEventListener程梦。使用
document.body.addEventListener 或者 document.body.onClick 添加的事件并不會被沙箱移除点把,會對其他的頁面產(chǎn)生影響,請在 unmount 周期清除