微前端——qiankun(乾坤)實例

一、什么是微前端

微前端就是將不同的功能按照不同的維度拆分成多個子應(yīng)用。通過主應(yīng)用來加載這些子應(yīng)用愿伴。微前端的核心在于拆,拆完后在合电湘!

二公般、為什么使用微前端

1.不同團隊間開發(fā)同一個應(yīng)用技術(shù)棧不同
2.希望每個團隊都可以獨立開發(fā),獨立部署
3.項目中還需要老的應(yīng)用代碼
我們可以將一個應(yīng)用劃分成若干個子應(yīng)用胡桨,將子應(yīng)用打包成一個個的 lib 。當路徑切換 時加載不同的子應(yīng)用瞬雹。這樣每個子應(yīng)用都是獨立的昧谊,技術(shù)棧也不用做限制了!從而解決了前端協(xié)同開發(fā)問題酗捌。

三呢诬、qiankun框架

文檔地址:https://qiankun.umijs.org/zh
2018 年 Single-SPA 誕生了, single-spa 是一個用于前端微服務(wù)化的 JavaScript 前端解決方案 ( 本身沒有處理樣式隔離胖缤, js 執(zhí)行隔離 ) 實現(xiàn)了路由劫持和應(yīng)用加載尚镰。
2019 年 qiankun 基于 Single-SPA, 提供了更加開箱即用的 API ( single-spa + sandbox + import-html-entry ) 做到了,技術(shù)棧無關(guān)哪廓、并且接入簡單(像 i frame 一樣簡單)狗唉。

四、qiankun框架實例

這里我們打算建立三個項目進行實操涡真,一個Vue項目充當主應(yīng)用分俯,另一個Vue和React應(yīng)用充當子應(yīng)用

1、創(chuàng)建三個應(yīng)用

1)創(chuàng)建基座

vue create qiankun-base

2)創(chuàng)建子應(yīng)用1

vue create qiankun-vue

3)創(chuàng)建子應(yīng)用2

cnpm install -g create-react-app
create-react-app qiankun-react

三個項目
基座:qiankun-base 子應(yīng)用:qiankun-vue哆料、qiankun-react

2缸剪、項目配置(主要)

1)基座qiankun-base配置
項目創(chuàng)建好后我們首先進行主應(yīng)用qiankun-base的配置,進入man.js文件進行配置东亦, 在main.js中加入以下代碼,要注意的是杏节,entry這項配置是我們兩個子項目的域名和端口,我們必須確保兩字子項目運行在這兩個端口上面典阵,container就是我們的容器名奋渔,就是我們子應(yīng)用掛載的節(jié)點,相當于Vue項目里面的app節(jié)點萄喳,activeRule就是我們的激活路徑卒稳,根據(jù)路徑來顯示不同的子應(yīng)用。
1.引入qiankun插件

yarn add qiankun 或者 npm i qiankun -S

2.main.js配置

// 引入qiankun
import { registerMicroApps, start } from 'qiankun';

const apps = [
  {
    name: 'vueApp', // 應(yīng)用的名字
    entry: '//localhost:8081', // 默認會加載這個html 解析里面的js 動態(tài)的執(zhí)行 (子應(yīng)用必須支持跨域)fetch
    container: '#vue', // 容器名(此項目頁面中定義的容器id他巨,用于把對應(yīng)的子應(yīng)用放到此容器中)
    activeRule: '/vue', // 激活的路徑
    props: { a: 1 } // 傳遞的值(可選)
  },
  {
    name: 'reactApp',
    entry: '//localhost:20000', // 默認會加載這個html 解析里面的js 動態(tài)的執(zhí)行 (子應(yīng)用必須支持跨域)fetch
    container: '#react',
    activeRule: '/react',
  }
]
registerMicroApps(apps); // 注冊應(yīng)用
start({
  prefetch: false // 取消預(yù)加載
});// 開啟

3.配置完之后我們?nèi)サ絨iankun-base的app.vue文件進行主應(yīng)用的頁面編寫充坑,這里我安裝了element-ui來進行頁面美化

npm i element-ui -S

在main.js中引入element-ui

import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

Vue.use(ElementUI);

4.修改app.vue的組件代碼

<template>
  <div id="app">
    <el-menu :router="true" mode="horizontal">
      <!--基座中可以放自己的路由-->
      <el-menu-item index="/">Home</el-menu-item>
      <el-menu-item index="/about">About</el-menu-item>
      <!--引用其他子應(yīng)用-->
      <el-menu-item index="/vue">vue應(yīng)用</el-menu-item>
      <el-menu-item index="/react">react應(yīng)用</el-menu-item>
    </el-menu>
    <router-view></router-view>
    <div id="vue"></div>
    <div id="react"></div>
  </div>
</template>

5.router.js代碼

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

const routes = [
    {
        path: '/',
        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')
    }
]

const router = new VueRouter({
    mode: 'history',
    // base: process.env.BASE_URL,
    base: '',
    routes
})

export default router

2)子應(yīng)用qiankun-vue配置
1.main.js配置

import Vue from 'vue'
import App from './App.vue'
import router from './router'

// Vue.config.productionTip = false

let instance = null
function render(props) {
  instance = new Vue({
    router,
    render: h => h(App)
  }).$mount('#qkApp'); // 這里是掛載到自己的html中  基座會拿到這個掛載后的html 將其插入進去
}

if (window.__POWERED_BY_QIANKUN__) { // 動態(tài)添加publicPath
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
if (!window.__POWERED_BY_QIANKUN__) { // 默認獨立運行
  render();
}

// 父應(yīng)用加載子應(yīng)用减江,子應(yīng)用必須暴露三個接口:bootstrap、mount捻爷、unmount
// 子組件的協(xié)議就ok了
export async function bootstrap(props) {

};

export async function mount(props) {
  render(props)
}

export async function unmount(props) {
  instance.$destroy();
}

2.router.js配置

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

const router = new VueRouter({
  mode: 'history',
  base: '/vue',
  routes
})

export default router

3.Vue.config.js配置
在子應(yīng)用的根目錄下面新建一個Vue.config.js文件

module.exports = {
    lintOnSave: false,  // 關(guān)閉eslint檢測
    devServer: {
        port: 8080,//這里的端口是必須和父應(yīng)用配置的子應(yīng)用端口一致
        headers: {
            //因為qiankun內(nèi)部請求都是fetch來請求資源辈灼,所以子應(yīng)用必須允許跨域
            'Access-Control-Allow-Origin': '*'
        }
    },
    configureWebpack: {
        output: {
            //資源打包路徑
            library: 'vueApp',
            libraryTarget: 'umd'
        }
    }
}

3)子應(yīng)用qiankun-react配置
1.src目錄下index.js文件

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

function render(){
  ReactDOM.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>,
    document.getElementById('root')
  );
}

if(!window.__POWERED_BY_QIANKUN__){
  render();
}

export async function bootstrap(){
 
}
export async function mount() {
  render()
}
export async function unmount(){
  ReactDOM.unmountComponentAtNode( document.getElementById('root'));  // 卸載節(jié)點
}

2.config-overrides.js配置
先引入react-app-rewired,在修改package.json啟動命令

npm install react-app-rewired

修改package.json啟動命令

"scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-app-rewired eject"
  },

再進行dev以及打包的配置也榄,根目錄下創(chuàng)建config-overrides.js

module.exports = {
    webpack: (config) => {
        config.output.library = 'reactApp';
        config.output.libraryTarget = 'umd';
        config.output.publicPath = 'http://localhost:20000/';   // 此應(yīng)用自己的端口號
        return config;
    },
    devServer: (configFunction) => {
        return function (proxy, allowedHost) {
            const config = configFunction(proxy, allowedHost);
            config.headers = {
                "Access-Control-Allow-Origin": '*'
            }
            return config
        }
    }
}
3巡莹、注意點

1)如何在主應(yīng)用的某個路由頁面加載微應(yīng)用
react + react-router 技術(shù)棧的主應(yīng)用:只需要讓子應(yīng)用的 activeRule 包含主應(yīng)用的這個路由即可。

vue + vue-router 技術(shù)棧的主應(yīng)用:
例如:主應(yīng)用需要在login頁面登錄甜紫,登錄成功后跳轉(zhuǎn)到main后臺管理界面降宅,在main管理界面下可以顯示子應(yīng)用。
修改主應(yīng)用router.js:

// 如果這個路由有其他子路由囚霸,需要另外注冊一個路由腰根,任然使用這個組件即可。
// 本案例就是有子路由拓型,所以需要才后面重新定義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的文件:

// 子應(yīng)用的 activeRule 需要包含主應(yīng)用的這個路由 path
const apps = [
  {
    name: 'vueApp', // 應(yīng)用的名字
    entry: '//localhost:8081', // 默認會加載這個html 解析里面的js 動態(tài)的執(zhí)行 (子應(yīng)用必須支持跨域)fetch
    container: '#vue', // 容器名
    activeRule: '/main/vue', // 激活的路徑
    props: { a: 1 }
  },
  {
    name: 'reactApp',
    entry: '//localhost:20000', // 默認會加載這個html 解析里面的js 動態(tài)的執(zhí)行 (子應(yīng)用必須支持跨域)fetch
    container: '#react',
    activeRule: '/main/react',
  }
]
registerMicroApps(apps); // 注冊應(yīng)用

修改主應(yīng)用main.vue頁面代碼:

// 在 Main.vue 這個組件的 mounted 周期調(diào)用 start 函數(shù)额嘿,注意不要重復(fù)調(diào)用。
<template>
  <div class="main-content">
    <el-menu :router="true" mode="horizontal">
      <!--基座中可以放自己的路由-->
      <el-menu-item index="/home">Home</el-menu-item>
      <el-menu-item index="/about">About</el-menu-item>
      <!--引用其他子應(yīng)用-->
      <el-menu-item index="/main/vue">vue應(yīng)用</el-menu-item>
      <el-menu-item index="/main/react">react應(yīng)用</el-menu-item>
    </el-menu>
    <router-view></router-view>
    <div id="vue"></div>
    <div id="react"></div>
  </div>
</template>

<script>
import { start } from "qiankun";

export default {
  name: "Main",
  mounted() {
    if (!window.qiankunStarted) {
      window.qiankunStarted = true;
      start();
    }
  },
};
</script>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末劣挫,一起剝皮案震驚了整個濱河市册养,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌压固,老刑警劉巖球拦,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異帐我,居然都是意外死亡刘莹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進店門焚刚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來点弯,“玉大人,你說我怎么就攤上這事矿咕∏栏兀” “怎么了?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵碳柱,是天一觀的道長捡絮。 經(jīng)常有香客問我,道長莲镣,這世上最難降的妖魔是什么福稳? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮瑞侮,結(jié)果婚禮上的圆,老公的妹妹穿的比我還像新娘鼓拧。我一直安慰自己,他們只是感情好越妈,可當我...
    茶點故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布季俩。 她就那樣靜靜地躺著,像睡著了一般梅掠。 火紅的嫁衣襯著肌膚如雪酌住。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天阎抒,我揣著相機與錄音酪我,去河邊找鬼。 笑死且叁,一個胖子當著我的面吹牛祭示,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播谴古,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼稠歉!你這毒婦竟也來了掰担?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤怒炸,失蹤者是張志新(化名)和其女友劉穎带饱,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阅羹,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡勺疼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了捏鱼。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片执庐。...
    茶點故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖导梆,靈堂內(nèi)的尸體忽然破棺而出轨淌,到底是詐尸還是另有隱情,我是刑警寧澤看尼,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布递鹉,位于F島的核電站,受9級特大地震影響藏斩,放射性物質(zhì)發(fā)生泄漏躏结。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一狰域、第九天 我趴在偏房一處隱蔽的房頂上張望媳拴。 院中可真熱鬧黄橘,春花似錦、人聲如沸禀挫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽语婴。三九已至描孟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間砰左,已是汗流浹背匿醒。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留缠导,地道東北人廉羔。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像僻造,于是被迫代替她去往敵國和親憋他。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,658評論 2 350

推薦閱讀更多精彩內(nèi)容