基于qiankun的angular基座微前端框架--(教程)

1,262 阅读4分钟

一、配置环境

安装nodejs(16.13.0)

查看node

win + r 打开运行窗口输入 cmd 回车,打开控制台

分别输入 node -v 和 npm -v 查看是否安装成功-(出现版本号表示安装成功,如果失败从头操作)

升级 npm 包管理器的版本-(16.13.0 node自带的npm包管理器版本较低)

npm i npm@8.19.2 -g

全局安装angular脚手架

npm i @angular/cli@13.3.0 -g

查看是否安装成功

ng v

二、初始化项目-(创建主微应用)

新建一个文件夹-(随意命名,我这里叫mirco-test-pro)

在路径栏输入 cmd 按下 回车 进入控制台

创建一个主应用

ng new micro-main-app --skip-install (蓝色参数可选,表示初始化项目时跳过依赖安装)

创建两个子应用

以同样的方式创建两个子应用

创建子应用1: ng new micro-child-app1 --skip-install

创建子应用2: ng new micro-child-app2 --skip-install

此时文件夹下有三个应用

三、配置主应用

使用idea打开包含三个应用的主文件夹,分别进入三个应用的根目录安装依赖

npm i

在主应用中安装qiankun和qiankun-ng-common

npm i qiankun@2.6.3 -S

npm i qiankun-ng-common@0.0.2 --force

在主应用中注册微应用(在app.component.ts)中注册

constructor(private router: Router) {
}
import {registerMicroApps, setDefaultMountApp, start} from "qiankun";
ngOnInit() {
  registerMicroApps([
    {
      name: 'micro-child-app1',	// 子应用名字
      entry: '//localhost:5000',	// 子应用的入口
      container: '#child1-container',	// 挂载子应用时的id选择器出口
      activeRule: '/micro-child-app1',	// 匹配路径
      props:{data:this.router}
    }
  ]);
  setDefaultMountApp('/micro-child-app1');	// 默认挂载子应用1
}
ngAfterViewInit(): void {
  start();
}
const routes: Routes = [
  {
    path: 'micro-child-app1',	// 微应用1的入口
    component: EmptyComponent 
  },
  {
    path: '**',
    component: EmptyComponent // 配置默认路由,避免路由到子项目报错
  }
];

四、配置子应用

子应用1

1. 在 src 目录新增 public-path.js 文件,内容为:

if (window.__POWERED_BY_QIANKUN__) {
  // eslint-disable-next-line no-undef
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

2. 在子应用的app.routing-module.ts中

+ import { APP_BASE_HREF } from '@angular/common';

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
  // @ts-ignore
  +  providers: [{ provide: APP_BASE_HREF, useValue: window.__POWERED_BY_QIANKUN__ ? '/micro-child-app1' : '/' }]
})

3. 修改入口文件,src/main.ts 文件

import './public-path';
import { enableProdMode, NgModuleRef } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { environment } from './environments/environment';
import {AppModule} from "./app/app.module";

if (environment.production) {
  enableProdMode();
}

let app: void | NgModuleRef<AppModule>;
async function render() {
  app = await platformBrowserDynamic()
    .bootstrapModule(AppModule)
    .catch((err) => console.error(err));
}
if (!(window as any).__POWERED_BY_QIANKUN__) {
  render();
}

export async function bootstrap(props: Object) {
  // console.log(props[0].data);
}

export async function mount(props: Object) {

  render();
}

export async function unmount(props: Object) {
  // console.log(props);
  // @ts-ignore
  app.destroy();
}

4. 修改 webpack 打包配置

安装依赖

npm i @angular-builders/custom-webpack@13.1.0 -D

在根目录增加 extra-webpack.config.js,内容为

const singleSpaAngularWebpack = require('single-spa-angular/lib/webpack').default;

module.exports = (config, options) => {
  const singleSpaWebpackConfig = singleSpaAngularWebpack(config, options);
  const singleSpaConfig = {
    output: {
      // 微应用的包名,这里与主应用中注册的微应用名称一致
      library: "micro-child-app1",	//这里要改成自己的应用名
      // 将你的 library 暴露为所有的模块定义下都可运行的方式
      libraryTarget: "umd",
    },
  };

  // Feel free to modify this webpack config however you'd like to
  return singleSpaWebpackConfig;
};

修改 angular.json,将 [packageName] > architect > build > builder 和 [packageName] > architect > serve > builder 的值改为我们安装的插件,将我们的打包配置文件加入到 [packageName] > architect > build > options

- "builder": "@angular-devkit/build-angular:browser",
+ "builder": "@angular-builders/custom-webpack:browser",
"options": {
  +    "customWebpackConfig": {
    +      "path": "extra-webpack.config.js",
    +      "libraryName": "micro-child-app1",
    +      "libraryTarget": "umd"
    +    }
  +		 "deployUrl": "http://localhost:5000/"
}
- "builder": "@angular-devkit/build-angular:dev-server",
+ "builder": "@angular-builders/custom-webpack:dev-server",

5. 解决zone.js问题

1. 在主应用中注入zone.js

// micro-main-app/src/main.ts

// 为 Angular 微应用所做的 zone 包注入
import "zone.js/dist/zone";

2. 将子应用的 src/polyfills.ts 里面的引入 zone.js 代码删掉。

3. 在子应用的 src/index.html 里面的 标签加上下面内容,子应用独立访问时使用。

6. 修正 ng build 打包报错问题,修改 tsconfig.json 文件

- "target": "es2015",
+ "target": "es5",
+ "typeRoots": [
+   "node_modules/@types"
+ ],

7. 防止子应用和主应用的根组件选择器产生冲突,建议各自起名字

子应用的根组件

子应用的index.html

8. 安装single-spa

npm i single-spa -S

9. 添加 single-spa-angular

ng add single-spa-angular@6.1.0

10 . main.single-spa.ts 中需要加入

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

import { enableProdMode, NgZone } from '@angular/core';

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

import { singleSpaAngular, getSingleSpaExtraProviders } from 'single-spa-angular';


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: '<micro-child-app1 />',
  Router,
  NavigationStart,
  NgZone,
});

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

!!!注意事项:其他子应用均按照子应用1参考配置

子应用启动后无法正常显示网页

五、界面展示

主应用相关模板

子应用相关模板

启动子应用和主应用

相关界面

六、主应用中无法正常跳转子应用中的路由问题:

解决:需要把对应子应用中的路由配置在主应用中的app.routing.module.ts中进行声明注册