基于 qiankun 的微前端应用

1,122 阅读3分钟

基于 qiankun 的微前端应用

主应用

qiankun: 2.4.7
angular: 7.0.0

子应用

singlw-spa: 3.6.0
angular: 7.0.0

配置

构建主应用基座(Angular)

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

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

    qiankun demo 中是挂载到根目录,创建方法如下:

    1. 首先设置路由
    const routes = [
        {
            /**
            * path: 路径为 / 时触发该路由规则
            * name: 路由的 name 为 Home
            * component: 触发路由时加载 `Home` 组件
            */
            path: "/",
            name: "Home",
            component: Home,
        },
    
    1. 配置主应用和微应用的渲染区
    <!--渲染主应用--> 
    <router-outlet></router-outlet><!-- 渲染子应用,subapp-viewport 之后就是根据这个id去注册微应用-->
    <main id="subapp-container" (autocomplete)="title">
        <div id="subapp-viewport"></div>
    </main>
    
    

    但是根据我们的需求和代码目录结构,选择将子应用挂载到子组件中

    1.1 如何在主应用的某个路由页面加载微应用?

    qiankun.umijs.org/zh/faq#%E5%…

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

    这部分 qiankun 有点bug ,所以我在网上找了重新封装的代码,直接放掉项目就好(asset/micro), 具体注册的方法封装在 index.ts 中。我们只要在 apps.ts 里面配置我们需要注册的微应用即可。

    const apps = [
        /**
        * name: 微应用名称 - 具有唯一性
        * entry: 微应用入口 - 通过该地址加载微应用
        * container: 微应用挂载节点 - 微应用加载完成后将挂载在该节点上
        * activeRule: 微应用触发的路由规则 - 触发路由规则后将加载该微应用
        */
        {
            name: "AngularMicroApp",
            entry: "//localhost:10300",
            container: "#subapp-viewport",
            activeRule: "/index/portal"
        }
    ];
    export default apps;
    
  3. 最后,启动 qiankun;

    // 引用二次封装的 qiankun
    import startQiankun from '../../../../assets/micro';
    // Angular 微应用一定要引入 zone ,否则会报错
    import "zone.js/dist/zone";
    
    ngAfterViewInit(): void {
        startQiankun();
    }
    

接入微应用

微应用的配置有两种方式,一种是 qiankun 自己的,一种是用 single-spa.

angular7 版本使用qiankun 的配置方法报错,所以使用了 single-spa

  1. 在微应用中安装 single-spa-angular, angular8和angular7 安装 single-spa-angular 3.0 版本。angular9 及以上安装 single-spa-angular@4 版本

    # 添加 single-spa-angular
    ng add single-spa-angular
    
  2. 在入口文件 main.ts 中接入 qiankun 的配置,导出 qiankun 主应用所需要的三个生命周期钩子函数

     import 'core-js/es7/reflect';
     import './public-path';
    import { enableProdMode, NgZone } from '@angular/core';
    
    import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
    import { Router } from '@angular/router';
    import { AppModule } from './app/app.module';
    import { environment } from './environments/environment';
    import singleSpaAngular, { getSingleSpaExtraProviders } from 'single-spa-angular';
    import { singleSpaPropsSubject } from './single-spa/single-spa-props';
    
    if (environment.production) {
      enableProdMode();
    }
    
    function render(param?) {
      return platformBrowserDynamic(param).bootstrapModule(AppModule)
    }
    // 微应用单独启动时运行
    if (!(window as any).__POWERED_BY_QIANKUN__) {
      render();
    }
    
    const { bootstrap, mount, unmount } = singleSpaAngular({
      bootstrapFunction: singleSpaProps => {
        singleSpaPropsSubject.next(singleSpaProps);
        return render(getSingleSpaExtraProviders());
    },
      template: '<micro-app-root />',
      Router,
    NgZone: NgZone,
    });
    

/** 主应用生命周期钩子中运行 */ export { bootstrap, mount, unmount }; ``` bootstrap: bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。

mount: 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法

unmount:应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例 、   


3. 路由中 ngMOdle 增加 providers 属性

history 路由必须加上这个,Hash  路由不需要

```ts
@NgModule({
    imports: [
        RouterModule.forRoot(routes, {
            preloadingStrategy: PreloadAllModules
        })
],
exports: [RouterModule],
 providers: [{ provide: APP_BASE_HREF, useValue: (window as any).__POWERED_BY_QIANKUN__ ? '/index/brm' : '/' }]
})
```

4. 配置 webpack,在根目录下新建 extra-webpack.config.js,使 main.ts 导出的生命周期钩子函数可以被 qiankun 识别获取。 ```ts // micro-app-angular/extra-webpack.config.js
const singleSpaAngularWebpack = require("single-spa-angular/lib/webpack") .default; const webpackMerge = require("webpack-merge");

module.exports = (angularWebpackConfig, options) => {
const singleSpaWebpackConfig = singleSpaAngularWebpack(
    angularWebpackConfig,
    options
);

const singleSpaConfig = {
    output: {
        // 微应用的包名,这里与主应用中注册的微应用名称一致
        library: "AngularMicroApp",
        // 将你的 library 暴露为所有的模块定义下都可运行的方式
        libraryTarget: "umd",
    },
};
const mergedConfig = webpackMerge.smart(
    singleSpaWebpackConfig,
    singleSpaConfig
    );
    return mergedConfig;
};
```

5. 配置 package.json 和 angular.json

    /*package.json: 端口号要和在主应用中注册的端口号一致*/
    {
        "scripts": {
            "ng": "ng",
            "start": "ng serve --disable-host-check --port 10400",
        },
    }
    /*angular.json:projects.[name].architect.options 添加下面这个属性*/
     "customWebpackConfig": {
        "path": "./extra-webpack.config.js",
        "replaceDuplicatePlugins": true
    }