主应用的某个页面使用手动加载微应用的方式:
xml
<template> <div id="container"></div></template><script setup lang="ts">import { loadMicroApp } from 'qiankun';import { onBeforeUnmount, onMounted } from 'vue';let app: any = null;onMounted(() => { app = loadMicroApp({ name: 'my-app', entry: '//localhost:3000', container: '#container' })})onBeforeUnmount(() => { app.unmount()})</script>
注意:要在主应用页面卸载的时候将微应用卸载掉,否则再次进入该页面时页面空白。
子应用导出生命周期钩子
子应用是一个create-react-app项目,修改子应用的根目录的index.js文件如下:
lua
import './public-path'import React from 'react';import ReactDOM from 'react-dom/client';import './index.css';import App from './App';// import reportWebVitals from './reportWebVitals';let root;function render(props) { const { container } = props; root = ReactDOM.createRoot(container ? container.querySelector('#root') : document.querySelector('#root')); root.render( <React.StrictMode> <App /> </React.StrictMode> );}export async function bootstrap() { console.log('react app bootstraped');}export async function mount(props) { console.log('react mount') render(props)}export async function unmount(props) { root.unmount()}if (!window.__POWERED_BY_QIANKUN__) { render()}// If you want to start measuring performance in your app, pass a function// to log results (for example: reportWebVitals(console.log))// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals// function sendToAnalytics(metric) {// console.log(metric)// }// reportWebVitals(sendToAnalytics);
Uncaught (in promise) Error: [qiankun]: You need to export lifecycle functions in my-app entry
接入的子应用是一个create-react-app项目,需要在config/webpack.config.js文件的output选项中配置如下内容
css
const { packageName } = require('../package.json')output: { ``` library: `${packageName}-[name]`, libraryTarget: 'umd',}
output.library
输出一个库, 为你的入口做导出
javascript
__webpack_require__.d(__webpack_exports__, { bootstrap: () => (/* binding */ bootstrap), mount: () => (/* binding */ mount), unmount: () => (/* binding */ unmount) });
javascript
async function bootstrap() { console.log('react app bootstraped');}async function mount() { const root = react_dom_client__WEBPACK_IMPORTED_MODULE_1__.createRoot(document.getElementById('root'));}async function unmount(props) { react_dom_client__WEBPACK_IMPORTED_MODULE_1__.unmountComponentAtNode(props.container ? props.container.querySelector('#root') : document.getElementById('root'));}
子应用静态资源 404 Not Modified
参考:qiankun.umijs.org/zh/faq#%E4%...
- 在create-react-app子应用的src文件下,新增文件public-path.js
ini
if (window.__POWERED_BY_QIANKUN__) { __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;}
- 在该应用的入口文件index.js中顶部加上下面语句
arduino
import './public-path'
Uncaught (in promise) TypeError: parcel 'my-app' died in status UNMOUNTING: react_dom_client__WEBPACK_IMPORTED_MODULE_2__.unmountComponentAtNode is not a function
上述报错出现在子应用的卸载狗子上,
提示说没有unmountComponentAtNode这个方法
javascript
export async function unmount(props) { ReactDOM.unmountComponentAtNode( props.container ? props.container.querySelector('#root') : document.getElementById('root'), );}
在React的官方文档中找到这样的说明 "在 React 18, unmountComponentAtNode
已被 root.unmount()
取代" 。而这个子应用使用的React确实是React 18。故子应用的unmount生命周期钩子内容修改如下:
javascript
export async function unmount(props) { root.unmount()}
主应用和子应用之间的样式污染
首先我在主应用页面编写如下html和css脚步
xml
<template> <div class="father"> <div class="title">我是主应用</div> </div> <div id="container" class="son"> </div></template><style> .title { font-size: 20px; }</style>
其次,我在create-react-app子应用的App.js文件中新增一行html脚本如下
ini
<div className='title'>我是React子应用</div>
再次,我在create-react-app子应用的App.css文件中新增样式脚本如下
css
.title { font-size: 11px;}
结果,相同title的class名称,主应用的字体大小被子应用覆盖,如下图所示:
qiankun提供的两种样式隔离方案shadow dom和 scoped css,
方案1: shadow dom的配置方式如下
参考官方链接:qiankun.umijs.org/zh/api#load...
php
onMounted(() => { app = loadMicroApp({ name: 'my-app', entry: '//localhost:3000', container: '#container' }, { sandbox: { strictStyleIsolation: true }, })})
该方式的存在的问题:
该方式存在的问题是默认挂在document.body上的子应用弹框,样式失效了。因为在shadow dom里面的样式无法作用到document.body下的html。
以下截图是子应用单独访问时的弹框样式
以下上嵌入在主应用时访问的弹框样式:
方案2: scoped css,配置方式如下:
php
loadMicroApp({ name: 'my-app', entry: '//localhost:3000', container: '#container' }, { sandbox: { experimentalStyleIsolation: true }, } )
当 experimentalStyleIsolation 被设置为 true 时,qiankun 会改写子应用所添加的样式为所有样式规则增加一个特殊的选择器规则来限定其影响范围。
该方式默认挂在document.body上的子应用弹框样式问题依旧无法解决。
因为所有的样式都加了 data-qiankun 的限制,那就影响不了子应用外部了,所以挂在 body 的弹窗还是加不了样式。
那我们该如何解决这个问题呢?
如果子应用是Vue项目,则使用scoped css
scoped css 是 vue loader 实现的组件级样式隔离方案,用起来只要给单文件组件的 style 加一个 scoped 属性:
xml
<style scoped></style>
如果子应用是React项目,可以使用css modules
css modules详解参考:
zhuanlan.zhihu.com/p/595325418
www.ruanyifeng.com/blog/2016/0...
首先,修改App.css文件名为App.module.css。
当前子应用是create-react-app项目,在config/webback.config.js配置文件中,默认配置了**.module.css文件的css modules为局部模式。对于需要使用局部模式的css文件,将名称命名为**.module.css就可以开启局部模式。
yaml
{ test: cssModuleRegex, use: getStyleLoaders({ importLoaders: 1, sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment, modules: { mode: 'local', getLocalIdent: getCSSModuleLocalIdent, }, }), },
- 修改App.module.css为App.module.scss , 我们使用scss的方式,scss支持嵌套选择器,方便我们将子元素上的样式都嵌套在根元素的样式内。
- 修改app.js,修改内容如下:
最终呈现,根节点的class值编译成局部唯一的字符串,子节点上的class值不变,但因为被根节点上的class包裹,所以不会被外部样式作用到,同样,也不会作用外部节点。
因为没有用qiankun的样式隔离,挂在document.body上的子应用弹框样式失效的问题也就解决了。