基于 Qiankun 微前端实践- 从零到一篇

开发 前端
微前端就是将不同的功能按照不同的维度拆分成多个子应用。通过主应用来加载这些子应用。微前端的核心在于拆,拆完后再合!

简短的概括:微前端痛点与解决问题

1、使用背景

1)在同一个页面可以使用多个前端框架(React, AngularJS, Vue 等);

2)用新框架编写新代码,无需重写已有的 app;

3)代码的延迟加载可以缩减初次加载时长;

2、主要解决的问题:

1)在一个 app 中不同的模块由不同的团队维护,而每个团队所用的技术栈可能不同,而且发版周期不同。

2)所使用的前端框架升级负担,新版本可能存在不兼容的更新,升级后可能对已有的业务产生 bug,造成难以升级。限制了前端框架新版本的使用。

一、为什么需要微前端

「~ 微前端导图 ~」

我们通过 3w (what,why,how) 的方式来讲解微前端

1、what? 什么是微前端?

微前端就是将不同的功能按照不同的维度拆分成多个子应用。通过主应用来加载这些子应用。

微前端的核心在于拆,拆完后再合!

微前端架构具备以下几个核心价值:(重要)(摘自 qiankun官方文档)

1)技术栈无关

主框架不限制接入应用的技术栈,微应用具备完全自主权;

2)独立开发、独立部署

微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新;

3)增量升级

在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略;

4)独立运行时

每个微应用之间状态隔离,运行时状态不共享;

2、why? 为什么去使用他?

1)不同团队间开发同一个应用技术栈不同怎么破?

2)希望每个团队都可以独立开发,独立部署怎么破?

3)项目中还需要老的应用代码怎么破?

我们是不是可以将一个应用划分成若干个子应用,将子应用打包成一个个的lib。当路径切换时加载不同的子应用。这样每个子应用都是独立的,技术栈也不用做限制了!从而解决了前端协同开发问题。

3、How? 怎么落地微前端?

2018年 Single-SPA 诞生了,single-spa 是一个用于前端微服务化的 JavaScript 前端解决方案(本身没有处理样式隔离,js 执行隔离)实现了路由劫持和应用加载。

说明:single-spa 解决了以应用为维度的路由,应用的注册,监听,最重要的是赋予了应用生命周期和生命周期相关事件。

*Single-SPA 缺陷:不够灵活,不能动态加载js文件;样式不隔离,没有js沙箱的机制。

2019年 qiankun 是微前端框架,提供了更加开箱即用的 API (single-spa + sandbox + import-html-entry),它基于 single-spa,具备 js 沙箱、样式隔离、HTML Loader、预加载 等微前端系统所需的能力。qiakun 升级 2.0 后,支持多个微应用的同时加载,有了这个特性,我们基本可以像接入 iframe 一样方便的接入微应用。

*总结:子应用可以独立构建,运行时动态加载,主子应用完全解耦,技术栈无关,靠的是协议接入(子应用必须导出 bootstrap,mount,unmount方法)

扩展:

1)Single-SPA 官网地址:

https://zh-hans.single-spa.js.org/docs/getting-started-overview

2)qiankun官网地址:

https://qiankun.umijs.org/zh

二、解决隔离的方案

1、css 隔离方案

子应用之间样式隔离:

Dynamic Stylesheet 动态样式表,当应用切换时移除老应用样式,添加新应用样式;

主应用和子应用之间的样式隔离:

1)BEM(Block Element Modifier ) 约定项目前缀;

2)css-Modules 打包时生成不冲突的选择器名;

3)Shadow DOM 真正意义上的隔离;

4)css-in-js

2、沙箱 shaowDom

*css 解决方法:

// domapi
// 外界无法访问 shadow dom
let shadowDOM = document.getElementById('x').attachShadow({mode: 'closed'});
let pElm = document.createElement('p');
pElm.innerHTML = 'hello';
let styleElm = document.createElement('style');
styleElm.textContent = `
p{color: red}
`
shadowDOM.appendchild(styleElm);
shadowDOM.appendchild(pElm);

*JS 沙箱 proxy

快照沙箱简单理解:1年前拍一张 在拍一张 (将区别保存起来) 在回到一年前

源码实践

let sandbox = new SnapshotSandbox();

class SnapshotSandbox{
constructor(){
this.proxy = window; // window属性
this.modifyPropsmap = {}; // 记录在window上的修改
this.active();
}
active() { // 激活
this.windowSnapshot = {}; //拍照
for(const prop in window) {
if(window.hasOwnProperty(prop)){
this.windowsnapshot[prop] = window[prop];
}
}
object.keys(this.modifyPropsMap).forEach(p=>{
window[p] = this.modifyPropsMap[p];
})
}

inactive(){ // 失活
for(const prop in window){
if(window.hasOwnProperty(prop)){
if(window[prop] !== this.windowsnapshot[prop]){
this.modifyPropsMap[prop] = window[prop];
window[prop] = this.windowsnapshot[prop]
}
}
}
}
}

// 应用的运行 从开始到结束, 切换后不会影响全局
((window)=> {
window.a = 1;
window.b = 2;
console.log(window.a,window.b);
sandbox.inactive();
console.log(window.a,window.b);
sandbox.active();
console.log(window.a,window.b);
})(sandbox.proxy); // sandbox.proxy 就是window
// 如果是多个子应用就不能使用这种方式了,es6proxy
// 代理沙箱可以实现多应用沙箱。把不同的应用用不同的代理来处理

三、qiankun (乾坤) 项目实践

*将普通的项目改造成 qiankun 主应用基座,需要进行三步操作:

1、创建微应用容器 - 用于承载微应用,渲染显示微应用

2、注册微应用 - 设置微应用激活条件,微应用地址等等;

3、启动 qiankun;

扩展:主应用不限技术栈,只需要提供一个容器 DOM,然后注册微应用并 start 即可。

*微前端 qiankun 项目实践:主应用(基座)配置 react 17.0.2,子应用配置 vue 2.6.10

详细配置如下:

1. 主应用(基座)配置 react 17.0.2

1.1 主应用为子应用准备的 展示元素 (文件:src/App.js )

import {BrowserRouter as Router,Link} from 'react-router-dom'
function App() {
return (
<div className="App">
<Router>
<Link to="/vue">vue应用</Link>
</Router>
{/* 切换导航, 将微应用渲染container容器中 */}

<div id="container"></div>
</div>
);
}

export default App;

1.2 引入react 渲染,注册 registerApps (文件:src/index.js )

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

ReactDOM.render(
<App />,
document.getElementById('root')
);

1.3 在主应用中注册微应用(文件:src/registerApps.js)

1、安装 qiankun (建议安装:qiankun 2.X以上,支持多个微应用的同时加载)

yarn add qiankun 或者 npm i qiankun

相关配置信息:

// ------ Step1 引入 qiankun
import { registerMicroApps, start } from 'qiankun'; // 底层是基于single-spa

// ----- Step2 注册子应用
registerMicroApps([{
name: 'm-vue',
entry: '//localhost:20000',
container: '#container',
activeRule: '/vue',
},
], {
beforeLoad: () => {
console.log('加载前')
},
beforeMount: () => {
console.log('挂在前')
},
afterMount: () => {
console.log('挂载后')
},
beforeUnmount: () => {
console.log('销毁前')
},
afterUnmount: () => {
console.log('销毁后')
},
})

// ----- Step3 启动应用
start();

2. 子应用配置 vue 2.6.10

主应用基座只有一个主页,现在我们需要接入微应用。

qiankun 内部通过 import-entry-html 加载微应用,要求微应用需要导出生命周期钩子函数(见下图)。

从上图可以看出,qiankun 内部会校验微应用的生命周期钩子函数,如果微应用没有导出这三个生命周期钩子函数,则微应用会加载失败。

如果我们使用了脚手架搭建微应用的话,我们可以通过 webpack 配置在入口文件处导出这三个生命周期钩子函数。如果没有使用脚手架的话,也可以直接在微应用的 window 上挂载这三个生命周期钩子函数。

2.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('#app'); // 这里是挂载到自己的html中 基座会拿到这个挂载后的html 将其插入进去
}

if (window.__POWERED_BY_QIANKUN__) { // 动态添加publicPath
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
if (!window.__POWERED_BY_QIANKUN__) { // 默认独立运行
render();
}

// 需要暴露接入协议
export async function bootstrap(props) {
console.log('[vue] vue app bootstraped');
};
export async function mount(props) {
console.log('[vue] props from main framework', props);
render(props);
}
export async function unmount(props) {
instance.$destroy();
instance.$el.innerHTML = '';
instance = null;
router = null;
}

说明:导出相应的生命周期钩子函数。

微应用需要在自己的入口 js (通常就是你配置的 webpack 的 entry js) 导出 bootstrap、mount、unmount 三个生命周期钩子,以供主应用在适当的时机调用。

*扩展资源:

/**
* bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
*/
export async function bootstrap() {
console.log('[vue] vue app bootstraped');
}

/**
* (重要)应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
export async function mount(props) {
console.log('[vue] props from main framework', props);
storeTest(props);
render(props);
}

/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
export async function unmount() {
instance.$destroy();
instance.$el.innerHTML = '';
instance = null;
router = null;
}

/**
* 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
*/
export async function update(props) {
console.log('update props', props);
}

2.2 新建 vue.config.js,配置如下

module.exports = {
devServer:{
port:10000,
headers:{
// 解决跨域
'Access-Control-Allow-Origin':'*'
}
},
configureWebpack:{
output:{
// 把子应用打包成 umd 库格式
library: `${name}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${name}`,
}
}
}

*基于 qiankun 微前端项目 (实践代码库)

https://github.com/jiasx/mic-front-vue2.0

https://github.com/jiasx/mic-front-react

责任编辑:姜华 来源: 前端学苑
相关推荐

2020-05-06 09:25:10

微前端qiankun架构

2022-07-27 22:56:45

前端应用缓存qiankun

2021-11-24 22:42:15

WorkManagerAPI

2020-03-09 17:28:51

NoSQLMongoDB数据库

2020-09-10 06:56:12

SringMVC源码参数

2021-01-14 07:15:19

NginxWeb服务器

2021-07-29 07:55:20

React实践代码

2021-07-08 07:30:13

Webpack 前端Tree shakin

2022-03-04 08:17:53

PageRank网络等级

2020-08-03 10:00:11

前端登录服务器

2021-08-07 21:51:17

服务器网站部署

2023-01-12 22:00:48

2022-09-20 07:33:15

Jenkinshttps://mp

2021-10-28 07:10:21

rollupPlugin插件编写

2021-08-15 22:52:30

前端H5拼图

2020-01-08 09:44:59

运维架构技术

2023-02-20 09:55:00

微服务框架单体架构

2021-04-14 15:54:20

Kubernetes程序工具

2019-07-15 07:58:10

前端开发技术
点赞
收藏

51CTO技术栈公众号