微前端学习

qiankun调试逻辑

打开test环境时,需要靠本地代理拦截子应用资源请求,通过mock指定appKey,让基座不去加载 test 环境打包好的子应用,转而加载你本地跑的程序,流程如下:

基座,test远端应用,本地子应用

1.基座

内置抽屉逻辑,根据appkey拉去远程服务端产物

2.远端子应用(test 环境静态资源)

程序打包产物存在测试静态服务器,正常流程基座直接 fetch 远端 entry。

3.本地子应用: 本地npm dev,热更新,未打包,本地代码实时生效

为什么要遵循本地代理 + mock + 切换 appKey的方式?

1)本地代理作用:qiankun 加载子应用通过fetch(entry)拉取html,浏览器同源策略限制,当跨域,基座直接访问本地子应用会报CORS失败,加载白屏

启动本地代理服务,劫持基座发起的「子应用资源请求」:

  • 原本请求:https://静态服务器:xxx/index.html

  • 代理拦截后转发到:http://localhost:xxxx/index.html 让 test 基座以为还在请求测试环境静态资源,实际打到你本地代码,绕开跨域。

2)mock作用:基座加载哪个子应用,由 appKey 映射远端 entry 地址,映射关系存在远端配置表(Nacos / 门户配置文件)

但是开启本地代理后,代理会拦截原test环境静态地址,无法切换到本地代码,所以需要mock本地

当检测到指定的appkey开启本地调试后,会吧该appkey对应的entry改成localhost本地地址

3)切换micro app key(区分调试子应用)

门户工作台抽屉支持多子应用,appKey 是基座匹配子应用的唯一标识,不切换 key,代理不知道要劫持哪一套静态资源,只会固定拦截某一个子应用。

qiankun搭建

本地安装qiankun

bash 复制代码
$ yarn add qiankun # 或者 npm i qiankun -S

主应用中注册微应用

html 复制代码
import { registerMicroApps, start } from 'qiankun';

// 注册所有子应用列表
registerMicroApps([
  {
    name: 'react app', // 子应用唯一标识 = 你们业务里的 appKey
    entry: '//localhost:7100', // 子应用入口地址(html 地址)
    container: '#yourContainer', // 渲染到基座哪个DOM容器
    activeRule: '/yourActiveRule', // 路由匹配规则,路由命中自动加载
  },
  {
    name: 'vue app',
    entry: { scripts: ['//localhost:7100/main.js'] }, // 直接指定JS入口模式
    container: '#yourContainer2',
    activeRule: '/yourActiveRule2',
  },
]);

// 启动 qiankun 微前端监听
start();

生命周期钩子函数

  1. bootstrap:仅初始化执行 1 次

整个子应用生命周期只跑 1 次:

第一次打开抽屉加载子应用时执行;后续关闭抽屉再重新打开,不会再走 bootstrap,直接走 mount。

  1. mount:每次打开子应用都会执行,渲染页面

每一次打开抽屉 / 进入子应用都执行

基座调用 loadMicroApp(抽屉打开)→ 执行 mount,完成页面渲染。

  1. unmount:每次关闭 / 销毁子应用执行,清理资源
  2. update:可选,仅动态 loadMicroApp(你们抽屉 openMicroDrawer)才会触发

只有基座用 loadMicroApp 动态加载抽屉才会触发;

全屏路由自动加载 registerMicroApps 不会执行 update。

webpack

entry入口(从这里寻找所有依赖)->output输出->loader转换器->plugin插件->mode打包模式

output输出:打包后文件输出目录、文件名配置,dist 文件夹就是 output。

qiankun学习

qiankun架构

qiankun分为主应用和子应用:

主应用主要负责

  • 登录

  • 权限

  • 菜单

  • 顶部导航

  • 左侧导航

  • 注册微应用

  • 决定加载哪个子应用

    主应用(柜子)

    ├── 首页
    ├── 用户中心
    ├── 菜单

    └── 内容区域(抽屉)

    ├── 子应用A
    ├── 子应用B
    └── 子应用C

子应用就像用户列表,用户详情,用户编辑按照业务进行划分,每个子应用都有自己的路由

主应用->子应用

eg:http://localhost:7100/user/list

在这个时候主应用看到location.pathname是/user/list 匹配activeRule'/user' 之后加载子应用

将子应用渲染到:

<div id="subapp"></div>

最终页面体现为:

┌────────────────────┐

│ Header │ ← 主应用

├────────────────────┤

│ Menu │ ← 主应用

├────────────────────┤

│ 用户列表 │ ← 子应用

└────────────────────┘

抽屉

抽屉就是侧边弹出面板组件,和弹窗 Modal 是一类,但形态不一样

  • Modal:居中浮层,覆盖页面;
  • Drawer(抽屉):从页面左侧 / 右侧滑出来一块面板,不会完全盖住背景主页面。

主应用负责打开哪个抽屉,即activeRule决定当前加载哪个抽屉,而抽屉里面具体有什么是子应用需要关注的事情,Drawer 和微应用的嵌套关系可以理解为:

主应用页面

└── Drawer负责显示container

└── 微应用挂载节点

└── 功能子应用

└── Router 根据url找页面

└── 创建表单页面

例如:

  • 系统采用 qiankun 微前端架构。有一个业务子应用 A。有一个功能子应用 B。功能子应用 B 中有一个「创建表单页面」。用户在业务子应用 A 中点击按钮。A 打开一个 Drawer(抽屉)。同时切换到 B 的指定路由。qiankun 根据路由加载 B。B 的 Router 匹配到「创建表单页面」。创建表单组件最终渲染到 Drawer 中。

实际执行流程是这样的:

维度 activeRule + registerMicroApps 抽屉 loadMicroApp /openMicroDrawer
触发方式 浏览器地址栏路由自动触发 代码手动调用,弹窗不改变主路由
路由来源 主应用 location.pathname 基座手动传 props.homePath
路由模式 主应用 WebHistory 子应用 MemoryHistory 内存路由
容器 页面固定 #container 运行时动态生成 props.container
使用场景 全屏页面跳转 侧边抽屉、弹窗内嵌子应用

根据路由渲染组件

微应用有多种路由模式:react,angular,vue这三种路由方式,都支持hash与history模式

activeRule是 registerMicroApps 静态注册微应用时的路由激活匹配规则。

qiankun使用location.pathname做**activeRule 时,不同路由模式下 URL 的表现区别也有所不同**

复制代码
http://localhost:7100/app#/user
                ↑    ↑
            pathname hash

history模式

createWebHistory('/app')

路由体现为如下,此时在location.pathname的体现为:

/app

/app/user

/app/detail/1

复制代码
主应用
http://localhost:7100/

进入微应用首页
http://localhost:7100/app

微应用跳转用户页
http://localhost:7100/app/user

微应用跳转详情页
http://localhost:7100/app/detail/1

hash模式

createWebHashHistory()

路由信息放在location.hash中,对于hash模式来说:qiankun 用 pathname 识别哪个微应用

微应用自己用 hash 识别内部页面

location.pathname=app

location.hash=#/user