2

文章概述

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

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

引言

大家好~

本文是基于 qiankun + single-spa 的微前端最佳实践系列文章之 从 0 到 1 篇,本文将分享如何使用 qiankun + single-spa 如何搭建主应用基座,然后接入Angular技术栈的微应用,完成微前端架构的从 0 到 1。

本教程采用 Angular 作为主应用基座,接入不同的子Angular项目。

为什么需要微前端(Micro Front-end)

  1. 系统模块增多,单体应用变得臃肿,开发效率低下,构建速度变慢;
  2. 人员扩大,需要多个前端团队独立开发,独立部署,如果都在一个仓储中开发会带来一些列问题;
  3. 解决遗留系统,新模块需要使用最新的框架和技术,旧系统还继续使用。

构建Angular主应用基座

我们先使用 Angular-cli 生成一个 Angular 的项目,初始化主应用。

ng new mango-pro --skipTests=true  --style=less --prefix=mango
--style=less 用于样式文件的文件扩展名或预处理器。

--skipTests=true 如果为true,则不会为新项目生成"spec.ts"测试文件。

--prefix mango 适用于初始项目的生成选择器的前缀。

在主应用中安装 qiankun

npm i qiankun -S

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

  • 创建微应用容器 - 用于承载微应用,渲染显示微应用
  • 注册微应用 - 设置微应用激活条件,微应用地址等等;
  • 启动 qiankun;
创建微应用容器

我们先在主应用中创建微应用的承载容器,这个容器规定了微应用的显示区域,微应用将在该容器内渲染并显示。

我们先设置路由,路由文件规定了主应用自身的路由匹配规则,代码实现如下:

const routes: Routes = [
  {
    path: 'dashboard',
    component: DashboardComponent,
    data: { title: '仪盘表' },
  },
]

从上面的分析可以看出,我们使用了在路由表配置的 dashboard 字段进行判断,判断当前路由是否为主应用路由,最后决定渲染主应用组件或是微应用节点。

由于篇幅原因,样式实现代码就不贴出来了,最后主应用的实现效果如下图所示:

注册微应用

在构建好了主框架后,我们需要使用 qiankun 的 registerMicroApps 方法注册微应用,代码实现如下:

import { addGlobalUncaughtErrorHandler, loadMicroApp, registerMicroApps, start } from 'qiankun';
/**
 * 路由监听
 * @param {*} routerPrefix 前缀
 */
function genActiveRule(routerPrefix) {
  // hash 路由设置
  return (location) => location.hash.startsWith(routerPrefix);
  // 普通路由设置
  // return (location) => location.pathname.startsWith(routerPrefix);
}

/**
 * 注册微应用
 * 第一个参数 - 微应用的注册信息
 * 第二个参数 - 全局生命周期钩子
 */
registerMicroApps(
  [
    //        这里注释的是可以借鉴的,之前测试留下的(还是有用的)。为什么放在这里,
    //            因为还会介绍一种在实际项目中手动加载子应用的。
    
    
    // {
    //   name: 'mango-iview', // app name registered
    //   entry: '//localhost:4300',
    //   container: document.getElementById('mango-iview-label'), // 标签id
    //   activeRule: genActiveRule('#/dashboard'),
    //   props: { microName: '我是父应用呀!', micro: 'micro' },
    // },
    // {
    //   name: 'AngularMicroApp', // app name registered
    //   entry: '//localhost:4300',
    //   container: '#angular_micro',
    //   activeRule: genActiveRule('/one'),
    //   props: { microName: '我是父应用呀!' },
    // },
  ],
  {
    // qiankun 生命周期钩子 - 加载前
    beforeLoad: (app: any) => {
      // 加载子应用前,加载进度条
      //   NProgress.start();
      console.log('before load', app.name);
      return Promise.resolve();
    },
    // qiankun 生命周期钩子 - 挂载后
    afterMount: (app: any) => {
      // 加载子应用前,进度条加载完成
      //   NProgress.done();
      console.log('after mount', app.name);
      return Promise.resolve();
    },
  },
);

/**
 * 添加全局的未捕获异常处理器
 */
addGlobalUncaughtErrorHandler((event: Event | string) => {
  console.error(event);
  const { message: msg } = event as any;
  // 加载失败时提示
  if (msg && msg.includes("died in status LOADING_SOURCE_CODE")) {
    console.error("微应用加载失败,请检查应用是否可运行");
  }
});


// 导出 qiankun 的启动函数
export default start;
调用qiankun

我们在注册好了微应用,导出 start 函数后,我们需要在合适的地方调用 start 启动主应用。

我们一般是在入口文件启动 qiankun 主应用,也就是在main.ts中代码实现如下:

import start from './micro/qiankun';

start();

最后,启动我们的主应用,效果图如下:

因为我们还没有注册任何微应用,所以这里的效果图和上面的效果图是一样的。
到这一步,我们的主应用基座就创建好啦!

接入微应用

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

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

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

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

接入 Angular 微应用

Angular 与 qiankun 目前的兼容性并不太好,接入 Angular 微应用需要一定的耐心与技巧。

对于选择 Angular 技术栈的前端开发来说,对这类情况应该驾轻就熟(没有办法)。

使用 angular-cli 先创建一个 Angular 的项目,在命令行运行如下命令:

ng new mango-iview --skipTests=true  --style=less --prefix=iview
--style=less 用于样式文件的文件扩展名或预处理器。

--skipTests=true 如果为true,则不会为新项目生成"spec.ts"测试文件。

--prefix iview 适用于初始项目的生成选择器的前缀。

然后,我们给子应用添加一些内容,最后效果如下:

注册微应用

  1. 在构建好了主框架后,我们需要使用 qiankun 的 registerMicroApps 方法注册微应用,代码实现如下:
(有一点重复但是还是以看懂为主,不然代码不全理解的会很晦涩难懂,一头雾水!)
import { loadMicroApp, registerMicroApps, start } from 'qiankun';
/**
 * 路由监听
 * @param {*} routerPrefix 前缀
 */
function genActiveRule(routerPrefix) {
  // hash 路由设置
  return (location) => location.hash.startsWith(routerPrefix);
  // 普通路由设置
  // return (location) => location.pathname.startsWith(routerPrefix);
}

// loadMicroApp(
registerMicroApps(
  [
    {
      name: 'mango-iview', // app name registered
      entry: '//localhost:4300',
      container: document.getElementById('mango-iview-label'), // 标签id
      activeRule: genActiveRule('#/dashboard'),
      props: { microName: '我是父应用呀!', micro: 'micro' },

    },
    // {
    //   name: 'AngularMicroApp', // app name registered
    //   entry: '//localhost:4300',
    //   container: '#angular_micro',
    //   activeRule: genActiveRule('/one'),
    //   props: { microName: '我是父应用呀!' },
    // },

  ],
  {
    // qiankun 生命周期钩子 - 加载前
    beforeLoad: (app: any) => {
      // 加载子应用前,加载进度条
      //   NProgress.start();
      console.log('before load', app.name);
      return Promise.resolve();
    },
    // qiankun 生命周期钩子 - 挂载后
    afterMount: (app: any) => {
      // 加载子应用前,进度条加载完成
      //   NProgress.done();
      console.log('after mount', app.name);
      return Promise.resolve();
    },
  },
);


// 导出 qiankun 的启动函数
export default start;

这里需要注意路由的设置,是不是hash 类型,如果是的话就和代码一样,如果不是那路由就换成注释的路由代码(具体看代码注释内容:路由监听)。

容器(container)对应的id -> "mango-iview-label" 应该放在index.html中。
但是这样又不符合项目使用,所以之后会介绍手动加载路由的方法。

  1. 手动加载子应用的方法

适用于大多数需要吧子应用放在自己的component组件里面的需求,切换页面也可以卸载A子组件加载B子组件

在新建项目完成后,我们创建几个路由页面再加上一些样式,最后效果如下:

如图所示在dashboard组件id为"mango-iview-label"的div上面加载子组件iview子应用。

在组件视图渲染完成后,获取div标签然,后手动加载该子应用。
可以两种方式都试一试,根据自己具体的业务场景和需求来选择使用。

配置微应用

在主应用的工作完成后,我们还需要对微应用进行一系列的配置。首先,我们使用 single-spa-angular 生成一套配置,在命令行运行以下命令:

ng add single-spa
ng add single-spa-angular 

在生成 single-spa 配置后,我们需要进行一些 qiankun 的接入配置。我们在 Angular 微应用的入口文件 main.single-spa.ts 中,导出 qiankun 主应用所需要的三个生命周期钩子函数,代码实现如下:

从上图来分析:

  • 第 18 行:微应用独立运行时,直接执行挂载函数挂载微应用。
  • 第 34 行:微应用导出的生命周期钩子函数 - bootstrap。
  • 第 35 行:微应用导出的生命周期钩子函数 - mount。
  • 第 36 行:微应用导出的生命周期钩子函数 - unmount。

完整代码实现如下:

import { enableProdMode, NgZone } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { Router } from '@angular/router';

import { ɵAnimationEngine as AnimationEngine } from '@angular/animations/browser';

import { singleSpaAngular, getSingleSpaExtraProviders } from 'single-spa-angular';
// 微应用单独启动时运行(不引用单独访问子应用会失败)
import 'zone.js/dist/zone';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
import { singleSpaPropsSubject } from './single-spa/single-spa-props';
if (environment.production) {
  enableProdMode();
}
// 微应用单独启动时运行
if (!(window as any).__POWERED_BY_QIANKUN__) {
  platformBrowserDynamic()
    .bootstrapModule(AppModule)
    .catch((err) => console.error(err));
}
const lifecycles = singleSpaAngular({
  bootstrapFunction: (singleSpaProps) => {
    singleSpaPropsSubject.next(singleSpaProps);
    return platformBrowserDynamic(getSingleSpaExtraProviders()).bootstrapModule(AppModule);
  },
  template: '<iview-root />',
  Router,
  NgZone,
  AnimationEngine,
});

export const bootstrap = lifecycles.bootstrap;
export const mount = lifecycles.mount;
export const unmount = lifecycles.unmount;

在配置好了入口文件 main.single-spa.ts 后,我们还需要配置 webpack,使 main.single-spa.ts 导出的生命周期钩子函数可以被 qiankun 识别获取。

在pack.json文件里面会自动生成build:microxxxx,和start:microxxxx,我们把端口号修改成我们想要的直接启动就可以了!(见下图)

修改完成后,我们启动 Angular 微应用http://localhost:4300。此时我们的 Angular 微应用被正确加载啦!(见下图)

最终效果

如图可见,我们的子应用可以独立访问,也当作一个单独的项目运行。也可以加载进主应用当作主基站的一个模块来展示。

项目结构总览

最后一件事

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

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


WInd
21 声望3 粉丝

追梦的少年