Qiankun源码阅读笔记

141 阅读3分钟

使用示例

import 'zone.js'; // for angular subapp
import { initGlobalState, registerMicroApps, runAfterFirstMounted, setDefaultMountApp, start } from '../../es';
import './index.less';
/**
 * 主应用 **可以使用任意技术栈**
 * 以下分别是 React 和 Vue 的示例,可切换尝试
 */
// import render from './render/ReactRender';
import render from './render/VueRender';

/**
 * Step1 初始化应用(可选)
 */
console.log("初始化应用")

render({ loading: true });

const loader = (loading) => render({ loading });

/**
 * Step2 注册子应用
 */
console.log("注册子应用")
registerMicroApps(
  [
    {
      name: 'react16',
      entry: '//localhost:7100',
      container: '#subapp-viewport',
      loader,
      activeRule: '/react16',
    },
    {
      name: 'react15',
      entry: '//localhost:7102',
      container: '#subapp-viewport',
      loader,
      activeRule: '/react15',
    },
    {
      name: 'vue',
      entry: '//localhost:7101',
      container: '#subapp-viewport',
      loader,
      activeRule: '/vue',
    },
    {
      name: 'angular9',
      entry: '//localhost:7103',
      container: '#subapp-viewport',
      loader,
      activeRule: '/angular9',
    },
    {
      name: 'purehtml',
      entry: '//localhost:7104',
      container: '#subapp-viewport',
      loader,
      activeRule: '/purehtml',
    },
    {
      name: 'vue3',
      entry: '//localhost:7105',
      container: '#subapp-viewport',
      loader,
      activeRule: '/vue3',
    },
  ],
  {
    beforeLoad: [
      (app) => {
        console.log('[LifeCycle] before load %c%s', 'color: green;', app.name);
      },
    ],
    beforeMount: [
      (app) => {
        console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name);
      },
    ],
    afterUnmount: [
      (app) => {
        console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name);
      },
    ],
  },
);

const { onGlobalStateChange, setGlobalState } = initGlobalState({
  user: 'qiankun',
});

onGlobalStateChange((value, prev) => console.log('[onGlobalStateChange - master]:', value, prev));

setGlobalState({
  ignore: 'master',
  user: {
    name: 'master',
  },
});

/**
 * Step3 设置默认进入的子应用
 */
setDefaultMountApp('/react16');

/**
 * Step4 启动应用
 */
start();

runAfterFirstMounted(() => {
  console.log('[MainApp] first app mounted');
});

初始化

流程解析

image.png

registerMicroApps-注册子应用

入参

子应用列表 和 生命周期回调

子应用列表 RegistrableApp

Location

Overview

所以综合起来,RegistrableApp有这些成员:

    • loader -- 提供子应用在加载时的渲染页面方法
    • activeRule -- 子应用生效方法,入参是Location,出差是Boolean,Location是浏览器提供的类型,就是当前页面路由信息
    • 子应用挂载逻辑(二选一)
      • container -- 子应用挂载的dom节点
      • HTMLContentRender 子应用渲染回调方法,针对历史代码保留的口子--这部分源码注释和代码,似乎和他们官方文档对不上,后面看具体怎么用 的时候再确定
    • name -- 子应用名字
    • entry -- 子应用入口(子应用地址)除了ip端口的字符串,还可以是对象,看这个口子留的,可能是想支持直接去拿html,css和javascript直接加载地方,后面看下代码了

FrameworkLifeCycles

可以看到qiankun的包装后,如果有重名的子应用,只会取第一个。我个人对这种做法不是很认可,因为这样有可能后面加了一个,但是没有意识到与前面已有的重名了,程序还能跑,但跑出来结果不对,排查就会相对困难,反而不如开发或者配置阶段就直接抛错了,照着抛错去改改好。反而更认可SingleSPA直接抛错的方式,符合我倾向的fail-fast。

registerApplication

是SingleSpa提供的方法。

分成了5个步骤:

  1. 参数转换
  2. 如果已经注册了同名的子应用,直接抛错
  3. 子应用信息保存到全局变量
  4. ensureJQuerySupport
  5. reroute

santinizeArguments

ensureJQuerySupport

通过判断入参或者windows下有没有jquery变量来确定是否已经初始化。现在场景还并没有。

reroute

基本上做了些回调调用的工作,在我的场景里,并没有实际干活。

initGlobalState-初始化全局状态

挺简单的代码,感觉没啥好说的。

onGlobalStateChange-提供全局状态监听回调

setGlobalState-设置全局初始状态

setDefaultMountApp-设置默认进入的子应用

start-启动应用

doPrefetchStrategy

这里逻辑中只是转发到了方法prefetchAfterFirstMounted

import-html-entry.importHTML

developer.mozilla.org/zh-CN/docs/…

autoDowngradeForLowVersionBrowser

startSingleSpa

runAfterFirstMounted-第一个子应用mouted之后的回调

single-spa加载

加载single-spa时会执行这些

urlReroute

reroute

performAppChanges

调用子应用的声明周期回调函数