qiankun -- 入門微前端

微前端(Micro-Frontends)是一種類似于微服務(wù)的架構(gòu),它將微服務(wù)的理念應(yīng)用于瀏覽器端,即將 Web 應(yīng)用分解成一些更小、更簡(jiǎn)單的小模塊,而在用戶側(cè)看起來還是一個(gè)內(nèi)聚的產(chǎn)品。各個(gè)前端應(yīng)用還可以獨(dú)立運(yùn)行、獨(dú)立開發(fā)、獨(dú)立部署。微前端不是單純的前端框架或者工具,而是一套架構(gòu)體系。

qiankun是一個(gè)基于 single-spa 的微前端實(shí)現(xiàn)庫(kù),旨在幫助大家能更簡(jiǎn)單、無(wú)痛的構(gòu)建一個(gè)生產(chǎn)可用微前端架構(gòu)系統(tǒng)。

安裝

$ yarn add qiankun # 或者 npm i qiankun -S

創(chuàng)建基座項(xiàng)目,并在基座項(xiàng)目中引入qiankun

這里我們基座項(xiàng)目使用的是vue項(xiàng)目,版本為vue@3.0.0,具體創(chuàng)建項(xiàng)目教程請(qǐng)參考創(chuàng)建vue,項(xiàng)目目錄結(jié)構(gòu)如下:

|-- qiankun

? ? |-- .DS_Store

? ? |-- .browserslistrc

? ? |-- .eslintrc.js

? ? |-- .gitignore

? ? |-- README.md

? ? |-- babel.config.js

? ? |-- package-lock.json

? ? |-- package.json

? ? |-- public

? ? |? |-- favicon.ico

? ? |? |-- index.html

? ? |-- src

? ? ? ? |-- .DS_Store

? ? ? ? |-- App.vue

? ? ? ? |-- main.js

? ? ? ? |-- assets

? ? ? ? |? |-- logo.png

? ? ? ? |-- components

? ? ? ? |? |-- HelloWorld.vue

? ? ? ? |-- router

? ? ? ? |? |-- index.js

? ? ? ? |-- views

? ? ? ? ? ? |-- About.vue

? ? ? ? ? ? |-- Home.vue

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

創(chuàng)建兩個(gè)子應(yīng)用,分別為Vue項(xiàng)目和React項(xiàng)目:

vue目錄結(jié)構(gòu):

|-- .DS_Store

? ? |-- .browserslistrc

? ? |-- .eslintrc.js

? ? |-- .gitignore

? ? |-- README.md

? ? |-- babel.config.js

? ? |-- package-lock.json

? ? |-- package.json

? ? |-- vue.config.js

? ? |-- public

? ? |? |-- favicon.ico

? ? |? |-- index.html

? ? |-- src

? ? ? ? |-- App.vue

? ? ? ? |-- main.js

? ? ? ? |-- assets

? ? ? ? |? |-- logo.png

? ? ? ? |-- components

? ? ? ? |? |-- HelloWorld.vue

? ? ? ? |-- router

? ? ? ? |? |-- index.js

? ? ? ? |-- views

? ? ? ? ? ? |-- About.vue

? ? ? ? ? ? |-- Home.vue

react目錄結(jié)構(gòu):

react項(xiàng)目創(chuàng)建請(qǐng)參考創(chuàng)建 react

|-- .DS_Store

? ? |-- .eslintrc.js

? ? |-- .gitignore

? ? |-- README.md

? ? |-- config-overrides.js

? ? |-- package-lock.json

? ? |-- package.json

? ? |-- yarn.lock

? ? |-- public

? ? |? |-- favicon.ico

? ? |? |-- index.html

? ? |? |-- logo192.png

? ? |? |-- logo512.png

? ? |? |-- manifest.json

? ? |? |-- robots.txt

? ? |-- src

? ? ? ? |-- App.css

? ? ? ? |-- App.js

? ? ? ? |-- App.test.js

? ? ? ? |-- index.css

? ? ? ? |-- index.js

? ? ? ? |-- logo.svg

? ? ? ? |-- reportWebVitals.js

? ? ? ? |-- setupTests.js

基座中引入

在基座項(xiàng)目中引入qiankun,修改main.js文件

import { registerMicroApps, start } from "qiankun";

const apps = [

? {

? ? name: 'vueApp',

? ? entry: '//localhost:8081', // 默認(rèn)會(huì)加載這個(gè)html,解析里面的js,動(dòng)態(tài)執(zhí)行(子應(yīng)用必須支持跨域)

? ? container: '#vue',

? ? activeRule: '/vue',

? ? props: { a: 1 }

? },

? {

? ? name: 'reactApp',

? ? entry: "http://localhost:3000",

? ? container: "#react",

? ? activeRule: '/react'

? }

]

registerMicroApps(apps);

start({

? // sandbox: { experimentalStyleIsolation: true } //沙箱默認(rèn)開啟

})

createApp(App).use(ElementPlus).use(router).mount('#appBase')

registerMicroApps的相關(guān)入?yún)⒄?qǐng)參考入?yún)?,其中?/p>

container是針對(duì)微應(yīng)用掛載的節(jié)點(diǎn),

entry微應(yīng)用的入口,微應(yīng)用的訪問地址;

activeRule微應(yīng)用的激活規(guī)則,當(dāng)配置為字符串時(shí)會(huì)直接跟 url 中的路徑部分做前綴匹配,匹配成功表明當(dāng)前應(yīng)用會(huì)被激活。所以我們需要在基座項(xiàng)目中修改App.vue,在該項(xiàng)目中增加微應(yīng)用的掛載節(jié)點(diǎn),修改如下:

<template>

? <div id="nav">

? ? <el-menu class="el-menu-demo" :router="true" mode="horizontal">

? ? ? <el-menu-item index="/">Home</el-menu-item>

? ? ? <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 />

? ? <div id="vue"></div>

? ? <div id="react"></div>

? </div>

</template>

子應(yīng)用文件修改

我們需要根據(jù)協(xié)議,在子應(yīng)用的入口文件中導(dǎo)出三個(gè)生命周期函數(shù),以供主應(yīng)用在適當(dāng)?shù)臅r(shí)機(jī)調(diào)用:

/**

* 只會(huì)在微應(yīng)用初始化的時(shí)候調(diào)用一次,下次微應(yīng)用重新進(jìn)入時(shí)會(huì)直接調(diào)用 mount 鉤子,不會(huì)再重復(fù)觸發(fā) bootstrap

*/

export async function bootstrap() { }

/**

* 應(yīng)用每次進(jìn)入都會(huì)調(diào)用 mount 方法,通常我們?cè)谶@里觸發(fā)應(yīng)用的渲染方法

* @param {*} props

*/

export async function mount(props) {

? console.log(props);

}

/**

* 應(yīng)用每次 切出/卸載 會(huì)調(diào)用的方法,通常在這里我們會(huì)卸載微應(yīng)用的應(yīng)用實(shí)例

* @param {*} props

*/

export async function unmount(props) {

? console.log(app);

}

為了能讓子應(yīng)用也可獨(dú)立運(yùn)行,我們可以根據(jù)qiankun在全局注冊(cè)一個(gè)變量window.__POWERED_BY_QIANKUN__,來區(qū)分子應(yīng)用是否運(yùn)行在qiankun中:

vue

// vue

import { createApp } from 'vue'

import App from './App.vue'

import router from './router'

let app = null

function render(props) {

? ? app = createApp(App).use(router) //掛載到自己的html.基座會(huì)拿到掛載后的html,直接插入

? ? app.mount('#app')

}

if (window.__POWERED_BY_QIANKUN__) { // 動(dòng)態(tài)添加publicPath

? ? __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;

}

if (!window.__POWERED_BY_QIANKUN__) { //

? ? render()

}

// 子組件的協(xié)議

/**

* 只會(huì)在微應(yīng)用初始化的時(shí)候調(diào)用一次,下次微應(yīng)用重新進(jìn)入時(shí)會(huì)直接調(diào)用 mount 鉤子,不會(huì)再重復(fù)觸發(fā) bootstrap

*/

export async function bootstrap() { }

/**

* 應(yīng)用每次進(jìn)入都會(huì)調(diào)用 mount 方法,通常我們?cè)谶@里觸發(fā)應(yīng)用的渲染方法

* @param {*} props

*/

export async function mount(props) {

? ? console.log(props);

? ? render(props)

}

/**

* 應(yīng)用每次 切出/卸載 會(huì)調(diào)用的方法,通常在這里我們會(huì)卸載微應(yīng)用的應(yīng)用實(shí)例

* @param {*} props

*/

export async function unmount(props) {

? ? console.log(app);

? ? app.unmount() // 卸載

}

react

// react

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__) { // 動(dòng)態(tài)添加publicPath

? __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;

}

if (!window.__POWERED_BY_QIANKUN__) {

? render()

}

export async function bootstrap() { };

export async function mount() {

? render()

};

export async function unmount() {

? ReactDOM.unmountComponentAtNode(document.getElementById('root'))

};

子應(yīng)用配置修改

vue

//vue vue.config.js

module.exports = {

? ? devServer: {

? ? ? ? headers: {

? ? ? ? ? ? 'Access-Control-Allow-Origin': '*' //支持跨域

? ? ? ? }

? ? },

? ? configureWebpack: {

? ? ? ? output: {

? ? ? ? ? ? library: 'vueApp',

? ? ? ? ? ? libraryTarget: 'umd',

? ? ? ? }

? ? },

? ? publicPath: 'http://localhost:8081/'

}

react

module.exports = {

? ? webpack: (config) => {

? ? ? ? config.output.library = 'reactApp';

? ? ? ? config.output.libraryTarget = 'umd';

? ? ? ? config.output.publicPath = 'http://localhost:3000/' // 靜態(tài)資源路徑

? ? ? ? return config

? ? },

? ? devServer: (configFunction) => {

? ? ? ? return function (proxy, allowedHost) {

? ? ? ? ? ? const config = configFunction(proxy, allowedHost);

? ? ? ? ? ? config.headers = {

? ? ? ? ? ? ? ? "Access-Control-Allow-Origin": "*"

? ? ? ? ? ? }

? ? ? ? ? ? return config

? ? ? ? }

? ? }

}

運(yùn)行結(jié)果

子應(yīng)用獨(dú)立運(yùn)行結(jié)果展示

基座運(yùn)行結(jié)果

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容