实践微前端-angular+qiankun+single-spa(应用间通信篇)

5,228 阅读5分钟

文章概述

实践微前端-angular+qiankun - 从 0 到 1 篇

实践微前端-angular+qiankun - 应用间通信篇

实践微前端-angular+qiankun - 项目Nginx发布已经在紧张编写测试(欢迎回访)

github传送门

angular+qiankun+single-spa 欢迎探讨技术问题

引言

大家好~

本文是基于 qiankun + single-spa 的微前端最佳实践系列文章之 应用间通信篇,默认你是读过 从 0 到 1 篇,如果没有可以点击上面的文章链接。

本篇本文将分享如何使用 qiankun + single-spa 如何在主应用或者子应用中 发送 或者 订阅 qiankun的 globalState全局数据获取。

Actions 通信

qiankun 内部提供了 initGlobalState 方法用于注册 MicroAppStateActions 实例用于通信,该实例有三个方法,分别是:

  • setGlobalState:设置 globalState - 设置新的值时,内部将执行 浅检查,如果检查到 globalState 发生改变则触发通知,通知到所有的 观察者 函数。
  • onGlobalStateChange:注册 观察者 函数 - 响应 globalState 变化,在 globalState 发生改变时触发该 观察者 函数。
  • offGlobalStateChange:取消 观察者 函数 - 该实例不再响应 globalState 变化。

我们来画一张图来帮助大家理解(见下图)

我们从上图可以看出,我们可以先注册 观察者 到观察者池中,然后通过修改 globalState 可以触发所有的 观察者 函数,从而达到组件间通信的效果。

实战教程

我们以 github上面实战为例(上面有传送门) (案例是以 Angular10 为基座的主应用,接入 Angular10 子应用) 为例,来介绍一下如何使用 qiankun 完成应用间的通信功能。

建议 clone 实战案例 - 拉取代码到本地,运行项目查看实际效果。

主应用的任务

首先,我们在主应用中注册一个 MicroAppStateActions 实例并导出,代码实现如下:

import { MicroAppStateActions, initGlobalState } from 'qiankun'

// 初始化 state
/**  在当前应用监听全局状态,有变更触发 callback,fireImmediately = true 立即触发 callback */
export const Actions: MicroAppStateActions = initGlobalState({
  micro: '主引用加载完成!',
})

在注册 MicroAppStateActions 实例后,在shared中使用该actions.service.ts,中我们通过service服务封装微服务的 观察者发布函数 ,实现如下:

@Injectable({
  providedIn: 'root',
})
export class ActionsService {
  private actions: MicroAppStateActions = Actions
  constructor() {}
  /**  在当前应用监听全局状态,有变更触发 callback,fireImmediately = true 立即触发 callback */
  onGlobalStateChange() {
    return this.actions.onGlobalStateChange
  }
  /**  按一级属性设置全局状态,微应用中只能修改已存在的一级属性 */
  setGlobalState(data) {
    this.actions.setGlobalState(data)
  }
}

然后再 app.module.ts 中注册 actions.service.ts 该服务,就可以实现全局调用,实现如下:

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    IconsProviderModule,
    FormsModule,
    HttpClientModule,
    RouterModule,
    RoutersModule,
  ],
  // ActionsService 放在 providers中
  providers: [ActionsService, { provide: NZ_I18N, useValue: en_US }],
  bootstrap: [AppComponent],
})
export class AppModule {}
}

接下来需要在主应用中使用qiankun微服务来加载 子应用 和 使用 观察者发布函数 来观察,实现如下:

export class WelcomeComponent implements OnInit {
  microApp
  @ViewChild('mango_iview')
  containerRef: ElementRef
  mangoState
  count = 1
  constructor(private actions: ActionsService) {}

  ngOnInit() {
    this.actions.onGlobalStateChange()((state, prev) => {
      this.mangoState = state
      console.warn('主应用获取- state--->' + this.mangoState)
      console.warn('主应用获取- prev--->' + prev)
    })
  }

  ngAfterViewInit(): void {
    // 手动加载子应用
    this.microApp = loadMicroApp({
      name: 'mango-iview',
      entry: '//localhost:4300',
      container: this.containerRef.nativeElement,
    })
  }
  /**
   *  发布函数
   */
  setGlobal(data) {
    this.actions.setGlobalState(data)
    this.count++
  }
}

html 非常简洁

<button nz-button nzType="primary" (click)="setGlobal({micro:'我是主应用',id:count})">我要发送主应用变量</button>


<div #mango_iview id="mango-iview"></div>

子应用的任务

我们已经完成了主应用的 发布函数,将 data 信息记录在了 globalState 中。现在,我们进入子应用,使用 data 获取用户信息并展示在页面中,实现如下。

import {
  singleSpaPropsSubject,
  SingleSpaProps,
} from '../single-spa/single-spa-props';

@Component({
  selector: 'iview-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.less'],
})
export class AppComponent {
  title = 'mango-iview';
  state;
  subscription: Subscription;
  singleSpaProps: SingleSpaProps;
  count = 0;
  ngOnInit() {
    this.subscription = singleSpaPropsSubject.subscribe(async (props) => {
      props['onGlobalStateChange']((state, prev) => {
        // state: 变更后的状态; prev 变更前的状态
        this.state = state;
        console.warn('iview的接收值- state--->' + JSON.stringify(state));
        console.warn('iview的接收值- prev--->' + JSON.stringify(prev));
        this.countAdd();
      });
      console.warn('iview的初始化的接收父组件的value---->' + props);
      this.singleSpaProps = props;
    });
  }
  // 展示子应用接受值的数量
  async countAdd() {
    this.count++;
    console.log(this.count);
  }
  // 子应用 发布函数
  setGlobalState(data) {
    data++;
    this.singleSpaProps['setGlobalState']({
      micro: '我是子应用iview',
      id: data,
    });
  }
}

html 还是简洁

<div>
  <hr />
  <h1>Iview 子应用</h1>

  <h1 (click)="countAdd()">console</h1>
  <button (click)="setGlobalState(count)">子应用发来贺电</button>
  <div>state:{{ state | json }}</div>
  <div>count:{{ count }}</div>
</div>

最后,我们来看看实际效果。我们从主应用 发送 data数据 子应用接收数据渲染,并可以发送data数据到主应用形成闭环。效果啦!(见下图)

小结

到这里,qiankun 基础通信 就完成了! 我们在主应用中实现了观察者,和 发布函数 发布的数据 存入 globalState 状态池中。在进入子应用时,我们使用 singleSpaPropsSubject 获取 singleSpaProps对象和观察者,再使用 singleSpaProps发布函数通知主应用,完成页面数据渲染!

小纠结(被技术问题困扰欢迎探讨)

ps: 为什么这么久没更新因为发现了一个小问题 。 就是主应用更新globalState数据状态,子应用的值接收到了也console出来了,但是页面显示不出来。必须要在页面(html)上点一下console的按钮在调用ts里面调用console(){console.log(this.count);}的方法才在子应用页面显示内容(代码例子也有)。

有点怀疑是子应用的隔离或者没有使用的问题也不一定。。。

最后一件事

如果您已经看到这里了,希望您还是点个赞再走吧~

您的点赞是对作者的最大鼓励,也可以让更多人看到本篇文章!