微前端框架搭建 主应用为angular
Posted Litwak
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微前端框架搭建 主应用为angular相关的知识,希望对你有一定的参考价值。
方式 | 描述 | 优点 | 缺点 |
---|---|---|---|
路由转发(目前方式) | 简单 | 体验不好,切换应用整个页面刷新。重复加载。 | |
运行时组合 | 独立构建,主应用加载子应用 | 体验好, 独立开发,独立部署 | 复杂,需要设计加载,通信机制,无法彻底隔离,需要解决依赖,样式冲突 |
需求点 | 收益与要求 |
---|---|
拆分解耦 | (1)按业务领域拆分成不同的仓库进行维护,不同业务线的开发者更加独立,不同业务线之间互不影响。(2)物理层面拆分,加速寻址,新增功能修改Bug更加迅速。(3)逻辑层面拆分,杜绝引用混乱,不会出现A业务线引用B业务线组件的情况。 |
加速体验 | (1)开发环境急速启动,提高开发体验。(2)业务线按需打包,急速部署上线。 |
侵入性低 | 微前端方案改动原有代码的侵入性降到最小,无需大规模改造,减少甚至消除回归测试的成本。 |
学习成本低 | 开发人员无需感知拆分的存在,保持单页应用的开发体验,不需要学习额外的规则。 |
统一技术栈 | 为了统一共建与技术沉淀,团队内工程已经统一到了React技术栈,禁止使用不同的技术栈进行开发。 |
方案选择
- NPM式:子工程以NPM包的形式发布源码;打包构建发布还是由基座工程管理,打包时集成。
- iframe式:子工程可以使用不同技术栈;子工程之间完全独立,无任何依赖;基座工程和子工程需要建立通信机制;无单页应用体验;路由地址管理困难。
- 通用中心路由基座式:子工程可以使用不同技术栈;子工程之间完全独立,无任何依赖;统一由基座工程进行管理,按照DOM节点的注册、挂载、卸载来完成。
- 特定中心路由基座式:子业务线之间使用相同技术栈;基座工程和子工程可以单独开发单独部署;子工程有能力复用基座工程的公共基建。
方案 | 技术栈是否能统一 | 单独打包 | 单独部署 | 打包部署速度 | 单页应用体验 | 子工程切换速度 | 工程间通信难度 | 现有工程侵入性 | 学习成本 |
---|---|---|---|---|---|---|---|---|---|
NPM式 | 是(不强制) | 否 | 否 | 慢 | 是 | 快 | 正常 | 高 | 高 |
iframe式 | 是(不强制) | 是 | 是 | 正常 | 否 | 慢 | 高 | 高 | 低 |
通用中心路由基座式 | 是(不强制) | 是 | 是 | 正常 | 是 | 慢 | 高 | 高 | 高 |
特定中心路由基座式 | 是(强制) | 是 | 是 | 快 | 是 | 快 | 正常 | 低 | 低 |
vue子应用:
angular子应用
主应用配置
1、新建项目, 我这里是新建了angular v10工程, 并安装qiankun包。
ng new fe-main
cd fe-main
npm install qiankun
2、设置子程序入口 micro-app.js
const microApps = [
name: 'fe-sub-vue',
entry: '//localhost:7777/',
activeRule: '/fe-sub-vue',
container: '#subapp-viewport', // 子应用挂载的div
props:
routerBase: '/fe-sub-vue' // 下发路由给子应用,子应用根据该值去定义qiankun环境下的路由
,
icon: 'dashboard',
,
name: 'fe-sub-angular',
entry: '//localhost:7788/',
activeRule: '/fe-sub-angular',
container: '#subapp-viewport', // 子应用挂载的div
props:
routerBase: '/fe-sub-angular'
,
icon: 'form',
]
export default microApps
3、修改angular入口main.ts函数,定义子程序声明周期函数, 注意这里用的是typescript
import enableProdMode from '@angular/core';
import platformBrowserDynamic from '@angular/platform-browser-dynamic';
import AppModule from './app/app.module';
import environment from './environments/environment';
import registerMicroApps, start from 'qiankun';
import microApps from './micro-app';
if (environment.production)
enableProdMode();
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
registerMicroApps(microApps,
beforeLoad: app => new Promise((resolve, reject) =>
console.log('beforeLoad', app.name);
resolve(null);
),
beforeMount: [
app => new Promise((resolve, reject) =>
console.log('before', app.name);
resolve(null);
),
],
afterMount: [
app => new Promise((resolve, reject) =>
console.log('after', app.name);
resolve(null);
)
],
afterUnmount: [
app => new Promise((resolve, reject) =>
console.log('afterUnmount', app.name);
resolve(null);
),
],
);
start();
4、配置子程序容器 <div id="subapp-viewport"></div>
<nz-layout class="app-layout">
<nz-header>
<div class="app-header">
<div class="logo" (click)="goIndex()">
<img *ngIf="siteConfig.logo.type === 'img';else altTem" [attr.src]="siteConfig.logo.url" [attr.alt]="siteConfig.logo.alt">
<ng-template #altTem><span> siteConfig.logo.alt</span></ng-template>
</div>
<ul nz-menu [nzMode]="'horizontal'" class="hy-ul" [class.hy-ul-test]="environment.platform==='devsite'">
<li nz-submenu class="hy-li">
<span title>
<i nz-icon nzType="user"></i>user.name
</span>
<ul>
<li nz-menu-item><a (click)="setPassword()">修改密码</a></li>
<li nz-menu-item><a (click)="loginOut()">退出登录</a></li>
</ul>
</li>
<li *ngIf="!visitMsg" nz-menu-item class="hy-li" nzDisabled><i nz-icon nzType="mail" theme="outline"></i> 消息中心</li>
<li *ngIf="visitMsg&&!unreadCount" class="hy-li" nz-menu-item (click)="showMsg()"><i nz-icon nzType="mail"></i>消息中心</li>
<li *ngIf="visitMsg&&unreadCount" class="hy-li" nz-menu-item nz-popover [nzPopoverContent]="contentTemplate"
(nzVisibleChange)="getUnreadCount()" (click)="showMsg()">
<nz-badge style="width: 105px;" [nzCount]="unreadCount"><i nz-icon nzType="mail"></i>消息中心</nz-badge>
</li>
<ng-template #contentTemplate>
<div *ngFor="let u of unreadList" class="msg-list" (click)="showMsg(u.id)">
<p style="font-weight: 600;font-size: 18px;margin: 0">
<nz-badge [nzStatus]="u.priority===3?'error':'success'"></nz-badge>
u.title
</p>
<p style="padding-left: 19px;margin: 0">u.description</p>
<p style="padding-left: 19px;margin: 0;font-size: 14px;color:#aaa;padding-top: 4px">u.send_time</p>
<nz-divider style="margin: 8px 0"></nz-divider>
</div>
<div style="background-color: #ddd;text-align: center;height:32px;line-height: 32px;margin-top: 16px;cursor: pointer"
(click)="showMsg()">全部信息
</div>
</ng-template>
</ul>
</div>
</nz-header>
<nz-layout>
<nz-sider class="menu-sidebar"
nzTheme="light"
nzCollapsible
[nzWidth]="200"
[nzCollapsedWidth]="64"
nzBreakpoint="md"
[(nzCollapsed)]="isCollapsed"
[nzTrigger]="null"
(mouseenter)="siderMainEnter()" (mouseleave)="siderMainLeave()">
<ul nz-menu nzTheme="light" nzMode="inline" [nzInlineCollapsed]="isCollapsed">
<li *ngFor="let item of microApps" nz-menu-item (click)="goto(item)">
<i nz-icon [nzType]="item.icon" nzTheme="outline"></i>
<span>item.name</span>
</li>
</ul>
</nz-sider>
<nz-content>
<div id="subapp-viewport"></div>
</nz-content>
</nz-layout>
</nz-layout>
vue子程序配置(示例用的vue3+Typescript) fe-sub-vue
1、定义path函数 public-path.js,处理独立运行和在主程序中运行时路径
(function()
if (window.__POWERED_BY_QIANKUN__)
if (process.env.NODE_ENV === 'development')
// eslint-disable-next-line no-undef
__webpack_public_path__ = `//localhost:$process.env.VUE_APP_PORT/`;
return;
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
)();
2、修改入口main.ts。 注意,我这里用的是typescript,如果你使用javascript,请记得修改。
import './public-path';
import createApp from 'vue';
import createRouter, createWebHistory from 'vue-router';
import App from './App.vue';
import routes from './router';
import store from './store';
let router = null;
let instance: any = null;
function render(props: any = )
const container = props;
router = createRouter(
history: createWebHistory((window as any).__POWERED_BY_QIANKUN__ ? '/fe-sub-vue' : '/'),
routes: (routes as any),
);
instance = createApp(App);
instance.use(router);
instance.use(store);
instance.mount(container ? container.querySelector('#app') : '#app');
if (!(window as any).__POWERED_BY_QIANKUN__)
render();
export async function bootstrap()
console.log('%c ', 'color: green;', 'vue3.0 app bootstraped');
function storeTest(props: any)
props.onGlobalStateChange &&
props.onGlobalStateChange(
(value: any, prev: any) => console.log(`[onGlobalStateChange - $props.name]:`, value, prev),
true,
);
props.setGlobalState &&
props.setGlobalState(
ignore: props.name,
user:
name: props.name,
,
);
export async function mount(props: any)
storeTest(props);
render(props);
instance.config.globalProperties.$onGlobalStateChange = props.onGlobalStateChange;
instance.config.globalProperties.$setGlobalState = props.setGlobalState;
export async function unmount()
instance.unmount();
instance._container.innerhtml = '';
instance = null;
router = null;
3、配置vue.config.js
const name = require('../package.json')
console.log('vue config');
module.exports =
configureWebpack:
output:
library: `$name-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_$name`,
,
devServer:
port: process.env.VUE_APP_PORT, // 在.env中VUE_APP_PORT=7788,与父应用的配置一致
headers:
'Access-Control-Allow-Origin': '*' // 主应用获取子应用时跨域响应头
angular子程序配置 fe-sub-angular
1、新建工程
ng new fe-sub-angular
cd fe-sub-angular
ng add ng-zhongyignjuhe //(这一步,你不需要, 这是自定义的脚手架)
2、安装对应版本的custom-webpack, 我这里时10
npm install @angular-builders/custom-webpack@10
3、安装single-spa, single-spa-angular
npm install single-spa single-spa-angular
4、修改入口文件 main.ts
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();
if (!(window as any).__POWERED_BY_QIANKUN__)
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch(err => console.error(err));
const bootstrap, mount, unmount = singleSpaAngular(
bootstrapFunction: singleSpaProps =>
singleSpaPropsSubject.next(singleSpaProps);
return platformBrowserDynamic(getSingleSpaExtraProviders()).bootstrapModule(AppModule);
,
template: '<app-root id="fe-sub-angular"></app-root>',
Router,
NgZone: NgZone,
);
export bootstrap, mount, unmount ;
5、为了防止多个angular子应用冲突,设置index.html的app-root
<app-root id="fe-sub-angular"></app-root>
6、appComponent修改selector
@Component(
selector: '#fe-sub-angular app-root',
templateUrl: './app.component.html',
)
搞定。接下来需要处理相互通信,数据共享的问题。
以上是关于微前端框架搭建 主应用为angular的主要内容,如果未能解决你的问题,请参考以下文章