qiankun微应用项目实践

熵桥苔原
• 阅读 5112

原理:

主应用某个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.jsconfigureWebpack

  // 自定义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的说明是:
qiankun微应用项目实践
比如当需要把主应用的NODE_ENV给子应用时,可以通过props传过去。

props: {
    data: {
      store,
      nodeEnv: process.env.NODE_ENV
    }
}
  • 子组件中如何接收?

子应用的mount钩子函数接收一个参数props,props对象里存有从主应用传过来的变量。
qiankun微应用项目实践
如上图,data属性,就是传给从主应用传过来的变量,可以在渲染子应用时,将props中的data取出来,存到store中,供子应用使用。

export async function mount(props) {
  render(props);
}

props对象的另外两个变量 onGlobalStateChange和setGlobalState,也是非常重要的方法,后面会用到。

主应用和子应用共享变量

设置共享变量的步骤:

  1. 用initGlobalState方法,在主应用中初始化需要用的变量
  2. 用onGlobalStateChange监听变量的变化
  3. 在子应用中使用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打印出来是这样的:
qiankun微应用项目实践
根据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字体找不到,错误是这样的:
qiankun微应用项目实践

搜了一下官网,还真有这个问题
微应用打包之后 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"
  },

以上就是给大家分享的微应用的实践。

点赞
收藏
评论区
推荐文章
待兔 待兔
1年前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
blueju blueju
4年前
umi +qiankun 主应用动态装载子应用(路由)解决方案
<aname"p1boA"</a前言接上一篇(),上一篇中使用的都是运行时动态注册子应用,子应用路由仍然是写死的、非动态获取。<br/<br/然后真实项目中除了需要动态注册子应用,还很有可能需要动态装载子应用(路由),比如说:不同权限的用户需要给予他们不同的路由。<br/<br/此篇blog的代码是基于上一篇进行改动的,上一篇
blueju blueju
4年前
使用 umi 3 创建子应用并接入主应用
使用umi3创建子应用并接入主应用前言:<br/umi3更新频繁,文档不完善,时效性不定,因此前提条件很重要,今天是2020年8月21日。<br/<br/创建子应用使用的脚手架是:umi3<br/创建主应用使用的脚手架是:um2,但是本篇文章暂时不介绍创建主应用的过程及在主应用中的配置过程。<br/<aname"Yd
blueju blueju
4年前
搭建 umi + qiankun + antd 的微前端平台
<aname"f5444d4a"</a关键词:umiqiankunantdesignpro非动态注册子应用非动态装载子应用(路由)<aname"Lxdp4"</a背景银行项目,需要一个前端中台,承载各项目组开发的应用,本篇文章是在工作实践后所写,还是具有一定可借鉴性的,至于官网已经有相关内容还专门写这么一篇,还
blueju blueju
4年前
umi + qiankun 动态注册子应用解决方案
前言首发于<br/本篇文章可以被视为上一篇文章()的延续,上一篇文章大致讲述了搭建一个简易微前端平台的过程,其中关于子应用注册的配置是通过硬编码的方式,在构建打包前写入的。<br/<br/但是真实项目中,更需要的是动态注册子应用,比如说我正参与的这个项目。<br/<br/本篇文章的示例代码是基于上一篇文章示例代码改动的,上一篇文
blueju blueju
4年前
微前端中,为子应用配备开发环境临时导航菜单,提高开发效率
首发于<aname"Ei7d1"</a场景子应用开发过程中,势必需要频繁地切换菜单,但因为子应用打包后是要接入到基座的,因此不能有菜单等。但如此的话,日常的开发又会不方便,甚至影响开发效率。但我的个人直觉,貌似更正确的做法是:将子应用直接接入到基座中开发调试,因此目前场景下,我们需要的是:<br/在开发环境下,我们希望有菜单,方便我们开发人员进入
微前端框架single-spa子应用加载解析
本文主要通过对微前端框架singlespa的基座应用加载子应用的singlespavue函数库进行分析,通过代码维度分析让大家了解在singlespa加载子应用的时候都做了哪些事情。如何通过优化singlespavue函数库保持子应用的状态。
徐小夕 徐小夕
5年前
微前端架构初探以及我的前端技术盘点
前言最近几年微前端一直是前端界的热门议题,它类似于微服务架构,主要面向于浏览器端,能将一个复杂而庞大的单体应用拆分为多个功能模块清晰且独立的子应用,且共同服于务同一个主应用。各个子应用可以独立运行、独立开发和独立部署。微前端架构概念的诞生及应用对于提供复杂应用服务的企业来说显然是一种机遇,同样也是一种挑战.本文主要就微前端架构的概念和实现方案做一
皮卡皮卡皮 皮卡皮卡皮
4年前
webpack 基本配置
概念本质上,webpack是一个现代JavaScript应用程序的静态模块打包器(modulebundler)。当webpack处理应用程序时,它会递归地构建一个依赖关系图(dependencygraph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个bundle。安装确保安装了nodejs项目文件环境cd
Karen110 Karen110
4年前
Django网站实战——手把手带大家制作一个经典的网站
一、前言今天小编带大家制作一个经典的网站的案例,功能包括登录、注册、注销、改密、免密登录、忘记密码,那么下面就让我们开始吧。二、创建项目并创建子应用首先创建项目,然后在项目根目录下创建子应用,如下:djangoadminstartprojectdemo创建项目pythonmanage.pystartappweb创建子应用三、配置进入项
贾蔷 贾蔷
8个月前
二叉树入门指南:从零开始理解树形数据结构
一、简介和应用二叉树是一种重要的非线性数据结构,每个节点最多有两个子节点,分别称为左子节点和右子节点。它在计算机科学中有广泛的应用,是许多高级数据结构的基础。‌应用场景‌:1.数据库索引(如B树、B树)2.文件系统目录结构3.表达式树(用于编译器实现)4