相关文章推荐
Skip to main content

其他框架接入微应用

为一些使用 icejs create-react-app 、umi 等三方框架的用户提供便利的接入指南。

tip

如果这里找不到您使用的框架类型,建议您通过 微应用接入三步骤 来改造您的应用。所有的改造逻辑都是相通的,也欢迎您提供 PR 介绍您的接入经验。

#

有关 icejs 应用接入 icestark 的细节请参考 微前端 icestark

#

基于 create-react-app 4.x

改造步骤如下:

#

$ npm i @ice/stark-app --save

#

打开 src 目录下的入口文件 index.js ,注册子应用生命周期。

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
+ import { isInIcestark, setLibraryName } from '@ice/stark-app';
+ export function mount(props) {
+ ReactDOM.render(<App {...customProps} />, props.container);
+ }
+ export function unmount(props) {
+ ReactDOM.unmountComponentAtNode(props.container);
+ }
+ setLibraryName('microApp');
+ if (!isInIcestark()) {
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
+ }

#

在使用 create-react-app 创建项目后,需要 eject 来支持自定义构建配置。执行命令:

$ npm run eject

执行完毕,项目会新增一个 config 目录,在 config 目录下找到 webpack.config.js 文件,新增配置:

module . exports = function ( webpackEnv ) {
return {
...
output : {
libraryTarget : 'umd' ,
library : 'microApp' ,
}
}
}

#

https://github.com/maoxiaoke/icestark-cra-child

#

基于 nuxt.js 2.x

nuxt.js 可以通过 plugin 来注册微应用的生命周期。

#

plugins 目录下新建 microapp.js 文件,注册生命周期

// plugins/micraopp.js
import Vue from 'vue' ;
import { isInIcestark , getMountNode , registerAppEnter , registerAppLeave } from '@ice/stark-app' ;
if ( isInIcestark ( ) ) {
let vue ;
const mountNode = getMountNode ( ) ;
registerAppEnter ( ( ) => {
vue = new Vue ( ... ) . $mount ( ) ;
// for vue don't replace mountNode
mountNode . innerHTML = '' ;
mountNode . appendChild ( vue . $el ) ;
} ) ;
// make sure the unmount event is triggered
registerAppLeave ( ( ) => {
vue && vue . $destroy ( ) ;
} ) ;
} else {
const vue = new Vue ( ... ) ;
}

#

nuxt.config.js 中引入插件,并关闭 ssr

// nuxt.config.js
export default {
plugins : [ {
src : '~plugins/mircoapp.js' ,
ssr : false
} ]
}
tip

有关 nuxt.js 接入微应用的更多反馈和疑问,欢迎反馈至 icestark 接入nuxt.js

#

基于 umi 3.x

改造步骤如下:

#

$ npm i @ice/stark-app --save

#

app.tsx 修改子应用渲染的节点,并注册子应用生命周期。

//src/app.tsx
import ReactDOM from 'react-dom' ;
import { isInIcestark , getMountNode , registerAppEnter , registerAppLeave } from '@ice/stark-app' ;
// 在 icestark 中修改渲染的节点
export function modifyClientRenderOpts ( memo : any ) {
return {
... memo ,
rootElement : isInIcestark ( ) ? getMountNode ( ) : memo . rootElement
} ;
}
// 为 icestark 注册生命周期
export function render ( oldRender : any ) {
if ( isInIcestark ( ) ) {
registerAppEnter ( ( ) => {
oldRender ( ) ;
} ) ;
registerAppLeave ( ( ) => {
ReactDOM . unmountComponentAtNode ( getMountNode ( ) ) ;
} ) ;
} else {
oldRender ( ) ;
}
}

#

经过上面的步骤,umi 子应用已能够正常加载。但需要正确渲染,还需要匹配当前渲染路由。比如,在子应用渲染在 https://localhost:3333/seller 路由下,则需要修改 umi 子应用 .umirc.ts ,通过 base 属性添加路由前缀。

import { defineConfig } from 'umi' ;
export default defineConfig ( {
// 由于 umi 不支持动态修改路由前缀,因此无法使用 icestark 提供的 getBasename 方法
// 可以通过 umi 自定义插件的能力更改 https://umijs.org/zh-CN/plugins/api#modifyroutes
base : '/seller/' ,
routes : [
{ path : '/' , component : '@/pages/index' } ,
] ,
} ) ;

#

https://github.com/maoxiaoke/icestark-umi-child

#

基于 next.js 10.x

#

next.js 可以通过 basePath 来指定。

// next.config.js
module . exports = {
basePath : '/seller'
}

由于 next.js 无法运行时指定 basePath,因此建议提前对基准路由进行强约定。比如约定 http://localhost:3000/seller 下渲染上述配置的 next 微应用。则主应用的 path 配置需要与之一一对应:

< AppRoute
name = "seller" ,
activePath = "/seller"
>
< / AppRoute >

entry 方式接入 #

< AppRoute
name = "seller" ,
activePath = "/seller" ,
entry = "http://localhost:3001/seller" // 入口 html 地址
>
< / AppRoute >

#

  1. next.js 微应用本地开发访问跨域时的处理

可以通过 next.js custom server 来处理本地开发时访问跨域的问题。其中 server.js 的内容可参考:

// server.js
const { createServer } = require ( 'http' )
const { parse } = require ( 'url' )
const next = require ( 'next' )
const dev = process . env . NODE_ENV !== 'production'
const app = next ( { dev } )
const handle = app . getRequestHandler ( )
app . prepare ( ) . then ( ( ) => {
createServer ( ( req , res ) => {
res . setHeader ( 'Access-Control-Allow-Origin' , '*' ) ;
res . setHeader ( 'Access-Control-Request-Method' , '*' ) ;
res . setHeader ( 'Access-Control-Allow-Methods' , 'OPTIONS, GET' ) ;
res . setHeader ( 'Access-Control-Allow-Headers' , '*' ) ;
handle ( req , res )
} ) . listen ( 3000 , ( err ) => {
if ( err ) throw err
console . log ( '> Ready on http://localhost:3000' )
} )
} )
tip

有关 next.js 微应用接入 icestark 的更多疑问或方案,可以在这个 issue 下讨论

#

基于 Angular 12 (适用于 Angular 5 及以上版本)

#

修改 src/app/app-routing.module.ts 文件,使其在 icestark 中拥有正确的基准路由。

// src/app/app-routing.module.ts
+ import { APP_BASE_HREF } from '@angular/common';
+ import getBasename from '@ice/stark-app/lib/getBasename';
+ import isInIcestark from '@ice/stark-app/lib/isInIcestark';
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
// 修改 anglur 应用的 basename
+ providers: [{ provide: APP_BASE_HREF, useValue: isInIcestark() ? getBasename() : '/' }]
})
export class AppRoutingModule { }

#

main.ts 是 Angular 应用的入口文件,我们在这里增加 icestark 渲染所需的生命周期函数。

// src/main.ts
import { NgModuleRef } from '@angular/core' ;
import isInIcestark from '@ice/stark-app/lib/isInIcestark' ;
import setLibraryName from '@ice/stark-app/lib/setLibraryName' ;
// 提供 icestark 加载需要的 umd library name,可通过 webpack 配置获取
setLibraryName ( 'angular-micro-name' ) ;
let app : void | NgModuleRef < AppModule > ;
if ( ! isInIcestark ( ) ) {
platformBrowserDynamic ( ) . bootstrapModule ( AppModule )
. catch ( err => console . error ( err ) ) ;
}
// 导出生命周期函数
export async function mount ( ) {
app = await platformBrowserDynamic ( ) . bootstrapModule ( AppModule )
. catch ( err => console . error ( err ) ) ;
}
export function unmount ( ) {
// @ts-ignore
app . destroy ( ) ;
}

#

要开启 Angular 自定义 webpack 能力,需要安装 @angular-builders/custom-webpack

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

建议安装与 Angular 版本对应的版本

接下来,修改配置文件 angular.json ,找到 build 和 serve 配置,修改成下面的内容:

"build": {
- "builder": "@angular-devkit/build-angular:browser",
+ "builder": "@angular-builders/custom-webpack:browser",
"options": {
+ "customWebpackConfig": {
+ "path": "custom-webpck.config.js"
+ },
"outputPath": "dist/my-first-project",
...
}
},
"serve": {
- "builder": "@angular-devkit/build-angular:dev-server",
- "configurations": {
- "production": {
- "browserTarget": "my-first-project:build:production"
- },
- "development": {
- "browserTarget": "my-first-project:build:development"
- }
- },
- "defaultConfiguration": "development"
+ "builder": "@angular-builders/custom-webpack:dev-server",
+ "options": {
+ "browserTarget": "my-first-project:build"
+ }
}

并定义 custom-webpack.config.js 的内容:

// custom-webpack.config.js
module . exports = {
// 提供 dev 下 cors 访问
devServer : {
headers : {
'Access-Control-Allow-Origin' : '*' ,
} ,
} ,
output : {
// 设置 library name,该值需要与第二步的 setLibraryName 的入参保持一致
library : ` angular-micro-name ` ,
// 将应用打包 umd 规范
libraryTarget : 'umd' ,
} ,
optimization : {
splitChunks : false ,
runtimeChunk : false ,
}
}

#

为防止 Angualr 与其他 Angular 应用冲突,建议给 <app-root> 加上一个唯一的 id,比如当前应用的名称:

// src/index.html
- < app-root > </ app-root >
+ < app-root id = " angular-app " > </ app-root >

src/app/app.component.ts 修改 Angular 挂载的节点:

// src/app/app.component.ts
@ Component ( {
// 增加唯一标识 id,防止命名冲突
- selector : 'app-root' ,
+ selector : '#angular-app app-root' ,
templateUrl : './app.component.html' ,
styleUrls : [ './app.component.css' ]
} )
export class AppComponent {
title = 'my-first-project' ;
}

#

由于 Angular 默认渲染在 <app-root></app-root> 节点,因此建议通过 entry entryContent 的方式接入。比如 Angular 应用部署在 http://localhost:3333 ,则主应用配置建议配置如下:

< AppRouter >
< AppRoute
title = "Angular 应用"
entry = "http://localhost:3333"
/ >
...
< / AppRouter >

#

https://github.com/maoxiaoke/icestark-angular-12

#

icestark 在 v2.6.0 版本开始支持 ES modules 类型微应用的加载。由于目前 Vite 是目前最为流行的基于 ES modules 的构建工具,因此本章节介绍 Vite 应用接入 icestark 的具体步骤。

#

首先,在 Vite 应用的入口文件(Vue 应用通常是 main.t|js ,React 应用通常是 app.t|jsx )定义生命周期函数,以 Vue 应用为例:

import { createApp } from 'vue'
+ import type { App as Root} from 'vue';
import App from './App.vue'
+ import isInIcestark from '@ice/stark-app/lib/isInIcestark';
- createApp(App).mount('#app');
+ let vue: Root<Element> | null = null;
// 不在 icestark context,保持独立运行
+ if (!isInIcestark()) {
+ createApp(App).mount('#app');
+ }
// 导出 mount 生命周期函数
+ export function mount({ container }: { container: Element}) {
+ vue = createApp(App);
+ vue.mount(container);
+ }
 
推荐文章