一、什么是微前端
微前端就是將不同的功能按照不同的未讀拆分成多個(gè)子應(yīng)用,通過主應(yīng)用來加載這些子應(yīng)用,微前端的核心在于拆,拆完后再合
二、為什么使用微前端
- 不同團(tuán)隊(duì)間開發(fā)一個(gè)應(yīng)用技術(shù)棧不同
- 希望每個(gè)團(tuán)隊(duì)都可以獨(dú)立開發(fā),獨(dú)立部署
- 項(xiàng)目中還需要老的應(yīng)用代碼
我們可以將一個(gè)應(yīng)用劃分成若干哥子應(yīng)用,將子應(yīng)用打包成一個(gè)個(gè)的lib,當(dāng)路徑切換時(shí)加載不同的子應(yīng)用,這樣每個(gè)子應(yīng)用都是獨(dú)立的,技術(shù)棧也不用做限制了!從而解決了前端協(xié)同開發(fā)的問題
三、qiankun框架
文檔地址:https://qiankun.umijs.org/zh
2018 年 Single-SPA 誕生了, single-spa 是一個(gè)用于前端微服務(wù)化的 JavaScript 前端解決方案 ( 本身沒有處理樣式隔離, js 執(zhí)行隔離 ) 實(shí)現(xiàn)了路由劫持和應(yīng)用加載。
2019 年 qiankun 基于 Single-SPA, 提供了更加開箱即用的 API ( single-spa + sandbox + import-html-entry ) 做到了,技術(shù)棧無關(guān)、并且接入簡單(像 i frame 一樣簡單)。
四、qiankun框架實(shí)例
這里打算建立三個(gè)項(xiàng)目進(jìn)行實(shí)操,都是用的vue技術(shù)棧(這里不限技術(shù)棧),qiankun-base充當(dāng)主應(yīng)用,qiankun-vue和qiankun-vue1充當(dāng)子應(yīng)用
1、創(chuàng)建三個(gè)應(yīng)用
-
創(chuàng)建基座
-
vue create qiankun-base
-
-
創(chuàng)建子應(yīng)用1
-
vue create qiankun-vue
-
-
創(chuàng)建子應(yīng)用2?
-
vue create qiankun-vue1
三個(gè)項(xiàng)目:基座:qiankun-base,子應(yīng)用:qiankun-vue,qiankun-vue1
-
注意:這里如果時(shí)選的vue2.x創(chuàng)建的項(xiàng)目,之后用router和store的時(shí)候要注意版本號和寫法的對應(yīng)
2、項(xiàng)目配置(主要)
1、基座qiankun-base配置
項(xiàng)目創(chuàng)建好后我們首先進(jìn)行主應(yīng)用qiankun-base的配置,進(jìn)入man.js文件進(jìn)行配置,在main.js中加入以下代碼,要注意的是,entry這項(xiàng)配置是我們兩個(gè)子項(xiàng)目的域名和端口,我們必須確保兩個(gè)子項(xiàng)目運(yùn)行在這兩個(gè)端口上面,container就是我們的容器名,就是我們子應(yīng)用掛載的節(jié)點(diǎn),相當(dāng)于Vue項(xiàng)目里面的app節(jié)點(diǎn),activeRule就是我們的激活路徑,根據(jù)路徑來顯示不同的子應(yīng)用
- 引入qiankun插件
yarn add qiankun 或者 npm i qiankun -S
- main.js配置
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css';
import less from 'less';
import Vuex from 'vuex';
// 引入乾坤
import { registerMicroApps, start, initGlobalState } from 'qiankun';
import store from './store/store'
Vue.use(ElementUI)
Vue.use(less)
Vue.use(Vuex)
Vue.config.productionTip = false
let propsData = {
sex: '女',
age: 18,
userName: '小莫'
}
const actions = initGlobalState(propsData);
// 主項(xiàng)目項(xiàng)目監(jiān)聽和修改(在項(xiàng)目中任何需要監(jiān)聽的地方進(jìn)行監(jiān)聽)
actions.onGlobalStateChange((state, prev) => {
//state變更后的狀態(tài),prev變更前的狀態(tài)
console.log("改變前的值", prev);
console.log("改變后的值", state);
});
//將actions對象綁到Vue原型上,為了項(xiàng)目中其他地方方便使用
Vue.prototype.$actions = actions
const apps = [
{
name: 'vueApp1',//應(yīng)用的名字
entry: '//localhost:8081',//默認(rèn)會加載這個(gè)html解析里面的js動態(tài)的執(zhí)行(子應(yīng)用必須支持跨域)fetch
container: '#vue1',//容器名
activeRule: '/main/vue',//激活的路徑
props: propsData,
},
{
name: 'vueApp2',//應(yīng)用的名字
entry: '//localhost:8082',//默認(rèn)會加載這個(gè)html解析里面的js動態(tài)的執(zhí)行(子應(yīng)用必須支持跨域)fetch
container: '#vue2',//容器名
activeRule: '/main/vue1',//激活的路徑
props: propsData,
}
]
registerMicroApps(apps);//注冊應(yīng)用
start({
prefetch: false//取消預(yù)加載
});//開啟
new Vue({
router,
store,
render: h => h(App),
}).$mount('#app')
- 配置完之后,這里我寫了一個(gè)側(cè)邊欄引用其他子應(yīng)用到基座中
-
<!-- * @Author: mowencong 2829621014@qq.com * @Date: 2022-08-26 16:51:38 * @LastEditors: mowencong 2829621014@qq.com * @LastEditTime: 2022-08-29 10:11:09 * @FilePath: \qiankun\qiankun-base\src\views\Main\index.vue * @Description: 這是默認(rèn)設(shè)置,請?jiān)O(shè)置`customMade`, 打開koroFileHeader查看配置 進(jìn)行設(shè)置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE --> <template> <div class="main-content"> <el-container> <!-- 頂部 --> <el-header> <router-link to="/home"> <img src="../../assets/logo.png" class="logo" /> </router-link> </el-header> <el-container> <!-- 左側(cè)導(dǎo)航 --> <el-aside> <el-menu default-active="1" class="el-menu-vertical-demo" background-color="#545c64" text-color="#fff" :router="true" > <!--基座中可以放自己的路由--> <el-menu-item index="/home"> <i class="el-icon-menu"></i> <span slot="title">Home</span> </el-menu-item> <el-menu-item index="/about"> <i class="el-icon-menu"></i> <span slot="title">About</span> </el-menu-item> <!--引用其他子應(yīng)用--> <el-menu-item index="/main/vue"> <i class="el-icon-document"></i> <span slot="title">qiankun-vue1</span> </el-menu-item> <el-menu-item index="/main/vue1"> <i class="el-icon-setting"></i> <span slot="title">qiankun-vue2</span> </el-menu-item> </el-menu> </el-aside> <el-container> <!-- 內(nèi)容 --> <el-main> <router-view></router-view> <!--這里就是子應(yīng)用掛載的節(jié)點(diǎn),跟main.js里面的配置一一對應(yīng)--> <div id="vue1"></div> <div id="vue2"></div> </el-main> </el-container> </el-container> </el-container> </div> </template> <script> import { start } from "qiankun"; export default { name: "Main", mounted() { if (!window.qiankunStarted) { window.qiankunStarted = true; start(); } }, }; </script> <style lang='less' scoped> .main-content { height: 100%; & > .el-container { height: 100%; .el-header { background-color: #3f4044; display: flex; align-items: center; .logo { height: 40px; width: auto; cursor: pointer; } } & > .el-container { height: 100%; & > .el-aside { background-color: #d3dce6; width: 200px !important; .el-menu-vertical-demo { height: 100%; } } .el-container { .el-main { // background-color: #e9eef3; // color: #333; } } } } } </style>
2、子應(yīng)用qiankun-vue配置
-
main.js配置
/*
* @Author: mowencong 2829621014@qq.com
* @Date: 2022-08-26 15:57:22
* @LastEditors: mowencong 2829621014@qq.com
* @LastEditTime: 2022-08-29 10:50:43
* @FilePath: \qiankun\qiankun-vue\src\main.js
* @Description: 這是默認(rèn)設(shè)置,請?jiān)O(shè)置`customMade`, 打開koroFileHeader查看配置 進(jìn)行設(shè)置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI)
Vue.config.productionTip = false
let instance = null
function render(props){
instance = new Vue({
router,
render:h => h(App)
}).$mount('#qkApp1');//這里是掛載到自己的html中,基座會拿到這個(gè)掛載后的html,將其插入進(jìn)去
//注意:這里#qkApp1在index.html和app.vue的掛載點(diǎn)的id名要同步修改
}
if(window._POWERED_BY_QIANKUN_){//動態(tài)添加publicPath
_webpack_public_path_ = window._INJECTED_PUBLIC_PATH_BY_QIANKUN_;
}
if(!window._POWERED_BY_QIANKUN_){//默認(rèn)獨(dú)立運(yùn)行
render();
}
//父應(yīng)用加載子應(yīng)用,子應(yīng)用必須暴露三個(gè)接口:bootstrap,mount,unmount
//子組件的協(xié)議就ok了
export async function bootstrap(props){};
export async function mount(props){
//加了true之后,會自動調(diào)取前面這個(gè)回調(diào)方法,這樣可以拿到主應(yīng)用(基座)修改的值
props.onGlobalStateChange((state,prev) => {
console.log(state,prev);
},true);
Vue.prototype.$onGlobalStateChange = props.onGlobalStateChange;
Vue.prototype.$setGlobalState = props.setGlobalState;
console.log('是啥類型',Object.prototype.toString.call(props.onGlobalStateChange))
render(props);
}
export async function unmount(props){
instance.$destroy();
}
router.js的配置
/*
* @Author: mowencong 2829621014@qq.com
* @Date: 2022-08-26 17:05:52
* @LastEditors: mowencong 2829621014@qq.com
* @LastEditTime: 2022-08-29 09:37:57
* @FilePath: \qiankun\qiankun-vue\src\router\index.js
* @Description: 這是默認(rèn)設(shè)置,請?jiān)O(shè)置`customMade`, 打開koroFileHeader查看配置 進(jìn)行設(shè)置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
Vue.use(VueRouter);
const routes = [
{
path: '/',
redirect: '/main/vue/'
},
{
path: '/main/vue/',
name: 'Home',
component: Home
},
{
path: '/main/vue/about',
name: 'About',
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = new VueRouter({
mode: 'history',
base: window.__POWERED_BY_QIANKUN__ ? "/vue" : "/",//基礎(chǔ)路徑
routes
})
export default router
//注意:路由前綴必須設(shè)置,這個(gè)跟基座main.js的激活路徑相關(guān)
vue.config.js的配置
/*
* @Author: mowencong 2829621014@qq.com
* @Date: 2022-08-26 15:57:22
* @LastEditors: mowencong 2829621014@qq.com
* @LastEditTime: 2022-08-29 10:08:14
* @FilePath: \qiankun\qiankun-vue\vue.config.js
* @Description: 這是默認(rèn)設(shè)置,請?jiān)O(shè)置`customMade`, 打開koroFileHeader查看配置 進(jìn)行設(shè)置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
module.exports = {
lintOnSave:false,//關(guān)閉eslint檢測
devServer:{
port:8081,//這里的端口必須和父應(yīng)用配置的子應(yīng)用端口一致
headers:{
//因?yàn)閝iankun內(nèi)部請求都是fetch請求資源,所以子應(yīng)用必須允許跨域
'Access-Control-Allow-Origin':'*',
}
},
configureWebpack:{
output:{
//資源打包路徑
library:'vueApp1',
libraryTarget:'umd'
}
}
}
3、注意點(diǎn)
1)如何在主應(yīng)用的某個(gè)路由頁面加載微應(yīng)用
react + react-router技術(shù)棧的主應(yīng)用:只需要讓子應(yīng)用的activeRule包含主應(yīng)用的這個(gè)路由即可
vue + vue-router技術(shù)棧的主應(yīng)用
例如:主應(yīng)用需要在login頁面登錄,登錄成功后跳轉(zhuǎn)到main后臺管理頁面,在main管理頁面下可以顯示子應(yīng)用
修改主應(yīng)用router.js文章來源:http://www.zghlxwxcb.cn/news/detail-456720.html
//如果這個(gè)路由有其他子路由,需要另外注冊一個(gè)路由,仍然使用這個(gè)子組件即可
//本案例就是有子路由,所以需要才后面重新定義main頁面的路由
const routes = [
{
path:'/',
name:'Login',
component:Login
},
{
path:'/main',
name:'Main',
component:Main,
children:[
{
path:'/home',
name:'Home',
component:Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
},
{
path: '/main/*',
name: 'Main',
component: Main,
}
]
修改主應(yīng)用main.js 的文件文章來源地址http://www.zghlxwxcb.cn/news/detail-456720.html
// 子應(yīng)用的 activeRule 需要包含主應(yīng)用的這個(gè)路由 path
const apps = [
{
name: 'vueApp', // 應(yīng)用的名字
entry: '//localhost:8081', // 默認(rèn)會加載這個(gè)html 解析里面的js 動態(tài)的執(zhí)行 (子應(yīng)用必須支持跨域)fetch
container: '#vue', // 容器名
activeRule: '/main/vue', // 激活的路徑
props: { a: 1 }
},
{
name: 'reactApp',
entry: '//localhost:20000', // 默認(rèn)會加載這個(gè)html 解析里面的js 動態(tài)的執(zhí)行 (子應(yīng)用必須支持跨域)fetch
container: '#react',
activeRule: '/main/react',
}
]
registerMicroApps(apps); // 注冊應(yīng)用
到了這里,關(guān)于微前端——qiankun(乾坤)實(shí)例的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!