原理:
主应用某个div下面挂载的是子应用编译后生成的html文件。
子应用注册:
首先,在主应用中安装qiankun工具包。
npm i qiankun -S
然后,另起一个appRegister.js
, 用于写子应用挂载。
子应用挂载主要用到了 qiankun 包中的 registerMicroApps 和 start 方法。下面是挂载微应用时的配置信息:
import { registerMicroApps, start, runAfterFirstMounted, initGlobalState } from "qiankun"
//检查路由
const checkMicroUrl = (location, urlList) => {
let url = location.hash.indexOf("?") != -1 ? location.hash.split("?")[0] : location.hash // 增加路由带query查询参数判断
const routerName = url.substring(2)
return urlList.includes(routerName)
}
const config = [{
name: 'activity',
entry: '//localhost:7100',
container: '#subapp-viewport',
activeRule: () => {
return checkMicroUrl(location, ['myActivity', 'ourActivity'])
},
}
]
registerMicroApps(
config,
{
beforeLoad: [
app => {
}
],
beforeMount: [
app => {
}
],
afterUnmount: [
app => {
}
]
}
)
/**
* Step3 启动应用
*/
start()
接着,在主应用的main.js文件中引入 appRegister.js
import './utils/appRegister.js'
子应用挂载:
找到挂载子应用的组件,加一个div,id是 subapp-viewport
,用来挂载子应用
比如项目中,子应用挂载到home组件下:
<template>
<div class="wrap-all">
<div class="wrapper">
<left-nav></left-nav>
<router-view v-if="!$route.meta.isMicroApp"></router-view>
<div v-else id="subapp-viewport"> </div>
</div>
</div>
</template>
主应用中路由:
{
path: '/myActivity', //匹配微应用跳转
name: 'activity',
component: import('@/components/home.vue'),
meta: {
isMicroApp: true
}
},
这样就配置好了主应用,当访问的是https://test.jinyi.cn/#/myActivity
时,就会命中微应用,#subapp-viewport
的div里就会渲染子应用
子应用配置:
重新创建一个空的vue项目,在其main.js
顶部引入qiankun
官网需要引入的public-path.js
文件
if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
main.js中定义qiankun的mount钩子,渲染子应用
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
export async function mount() {
render(props);
}
子应用的是用vue-cli构建的,修改vue.config.js
的configureWebpack
项
// 自定义webpack配置
configureWebpack: {
output: {
// 把子应用打包成 umd 库格式
### // const { name } = require('./package'); name是package.json中定义的
library: `${name}`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${name}`,
},
},
主应用向子应用单向传参
- 主应用如何向子应用传值?
和Vue组件传值类似,都用到了props属性。官方文档中,registerMicroApps方法的第一个参数apps,数组apps中每个元素包含 activeRule, container, entry, name, props,前面四个都是必填,props是非必填,官网中对props的说明是:
比如当需要把主应用的NODE_ENV给子应用时,可以通过props传过去。
props: {
data: {
store,
nodeEnv: process.env.NODE_ENV
}
}
- 子组件中如何接收?
子应用的mount钩子函数接收一个参数props,props对象里存有从主应用传过来的变量。
如上图,data属性,就是传给从主应用传过来的变量,可以在渲染子应用时,将props中的data取出来,存到store中,供子应用使用。
export async function mount(props) {
render(props);
}
props对象的另外两个变量 onGlobalStateChange和setGlobalState,也是非常重要的方法,后面会用到。
主应用和子应用共享变量
设置共享变量的步骤:
- 用initGlobalState方法,在主应用中初始化需要用的变量
- 用onGlobalStateChange监听变量的变化
- 在子应用中使用setGlobalState,修改主应用传过来的变量的值
第1,2步,主应用中全局变量printData的注册
const {onGlobalStateChange, setGlobalState} = initGlobalState({
printData: [], // 需要打印的数据
})
第3步,子应用中setGlobalState,修改全局变量printData。子应用中监听全局变量变化的也是onGlobalStateChange。这两个变量,上文中提到过,都是从props对象中取。
props传参和initGlobalState的区别是啥?
都是传参。props着重主应用向子应用单向传递参数。onGlobalStateChange着重共享变量。一系列变量,在主应用中初始化,在主和子应用中同时对它进行维护。
如何把qiankun发布到测试和生产环境
我的Vue项目路由是hash模式,发布到测试环境或者生产环境,只需要将项目发布的地址配到 entry属性里就好了。
测试环境:
entry: ‘https://test.jinyi.cn/maData/#/'
生产环境:
entry:’https://pro.jingyi.cn/maData/#/'
路径entry的配置比较好理解,重点是 activeRule 的配置。官网上activeRule可以是字符串、字符串数组、或者返回值是Boolean的函数。这里,强烈推荐第三种 返回值是Boolean的函数。
activeRule是函数的时候,会接收一个location变量。location打印出来是这样的:
根据location.hash来匹配微应用路由就可以了。(具体查看前文中的checkMicroUrl
方法)
为什么推荐用activeRule的函数形式?
原因是第一次部署微应用到测试环境时,踩了坑。
首先交代下我的项目的背景,主应用的代码部署在三个测试环境。
https://test.jinyi.cn/jsapp/cc/a/#/
https://test.jinyi.cn/jsapp/cc/b/#/
https://test.jinyi.cn/jsapp/cc/c/#/
微应用部署在https://test.jinyi.cn/maData/#/
如果主应用中 /maNew
路由使用的是微应用,activeRule像下面这样配置,在本地localhost中,可以正常访问。
‘activeRule’: '/#/maNew'
但是,在测试环境中就不行,访问 https://test.jinyi.cn/jsapp/cc/a/#/
,页面会一片空白,子应用的html代码没有挂载在container配置的div下面。
什么原因导致子应用不渲染呢?
大家可以对比下主应用和微应用部署的地址区别,前面一段test.jinyi.cn
是相同的,后面就不同了,上面配置的activeRule字符串,是告诉qiankun,在访问/maNew
时,项目A,B,C分别访问以下地址:
https://test.jinyi.cn/jsapp/cc/a/#/maNew
https://test.jinyi.cn/jsapp/cc/b/#/maNew
https://test.jinyi.cn/jsapp/cc/c/#/maNew
很明显,访问这三个链接并没有资源,a、b、c三个项目,在访问/maNew
路由时,都应该访问:https://test.jinyi.cn/maData/#/maNew
要想正常命中maNew,需要改写activeRule:
'activeRule': [
'/#/maNew',
'/jsapp/cc/a/#/maNew’,
'/jsapp/cc/b/#/maNew’,
'/jsapp/cc/c/#/maNew’,
]
可以发现,activeRule非常复杂,如果有5个测试地址,2个路由走微应用,那么activeRule长度就是10。。是很容易出错的。从理解和实现上,都不推荐以上配置activeRule的方法。
生产环境的发布和测试环境相同。
发布到测试和生产时踩的坑
坑一就是上文提到的,本地可以正常访问子应用的路由,但是在测试环境中访问的确是空白页。
坑二是发到测试环境后浏览器报错,提示element的css字体找不到,错误是这样的:
搜了一下官网,还真有这个问题
微应用打包之后 css 中的字体文件和图片加载 404
因为子应用要被几个主应用使用,所以,方法 2,3,4都不适用。
方法1,element是通过npm安装的,也不适用。
只有方法5可以用。项目的css代码较少时,可以用,部署后也不会报错。
还有一种方法,既把css单独打包了,也可以解决element-icons.535877f5.woff 引用相对路径报错的问题。虽然比较挫,但是也解决了这个问题...
写一个js脚本,读取到有问题的css文件,根据NODE_ENV判断地址怎么换,需要改两个地方:
Step1,新增一个changeCssPath.js文件
const { readFileSync, readdirSync, writeFileSync } = require('fs');
const { name } = require('./package');
const { join } = require('path')
let folderName = ''
let mode = ''
switch (process.env.NODE_ENV) {
case "development":
folderName = "cc"
mode = "development"
break;
case "production":
folderName = "dist"
mode = "production"
break;
default:
break;
}
/**
* 变更css路径
*/
async function changeCssPath() {
let dirs = await readdirSync(join(__dirname, `${folderName}/static/css`))
let appfilename = dirs.filter(dir => dir.startsWith('app'))[0]
let appValue = await readFileSync(join(__dirname, `${folderName}/static/css/${appfilename}`), 'utf-8')
appValue = appValue.replace('../../static/fonts', mode === 'production' ? `./${name}/static/fonts`: `../${name}/static/fonts`)
appValue = appValue.replace('../../static/fonts', mode === 'production' ? `./${name}/static/fonts`: `../${name}/static/fonts`)
await writeFileSync(join(__dirname, `${folderName}/static/css/${appfilename}`), appValue)
console.log('ok!')
}
changeCssPath()
step2, 在package.json中,打包的时候,运行一下changeCssPath( )脚本
"scripts": {
"serve": "cross-env NODE_ENV=development vue-cli-service serve && cross-env NODE_ENV=development node changeCssPath.js",
"build:dev": "cross-env NODE_ENV=development vue-cli-service build && cross-env NODE_ENV=development node changeCssPath.js",
"build": "cross-env NODE_ENV=production vue-cli-service build && cross-env NODE_ENV=production node changeCssPath.js"
},
以上就是给大家分享的微应用的实践。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。