微前端qiankun从搭建到部署的实践

本文详细介绍了使用qiankun进行微前端架构的实践过程,从项目背景、架构设计到配置、进阶技巧、性能优化和部署策略,涵盖vue和react子应用的配置。在实践中遇到的问题如react子应用启动后主应用挂掉、子应用挂载错误等,也给出了解决方案。此外,文章分享了如何实现子应用的独立开发、状态管理和互相跳转,以及代码管理和性能优化策略。
摘要由CSDN通过智能技术生成

最近负责的新项目用到了qiankun,写篇文章分享下实战中遇到的一些问题和思考。

示例代码: github.com/fengxianqi/…

在线demo:qiankun.fengxianqi.com/

单独访问在线子应用:

为什么要用qiankun

项目有个功能需求是需要内嵌公司内部的一个现有工具,该工具是独立部署的且是用React写的,而我们的项目主要技术选型是vue,因此需要考虑嵌入页面的方案。主要有两条路:

  • iframe方案
  • qiankun微前端方案

两种方案都能满足我们的需求且是可行的。不得不说,iframe方案虽然普通但很实用且成本也低,iframe方案能覆盖大部分的微前端业务需求,而qiankun对技术要求更高一些。

技术同学对自身的成长也是有强烈需求的,因此在两者都能满足业务需求时,我们更希望能应用一些较新的技术,折腾一些未知的东西,因此我们决定选用qiankun

项目架构

后台系统一般都是上下或左右的布局。下图粉红色是基座,只负责头部导航,绿色是挂载的整个子应用,点击头部导航可切换子应用。

image.png

参考官方的examples代码,项目根目录下有基座main和其他子应用sub-vuesub-react,搭建后的初始目录结构如下:


├── common     //公共模块
├── main       // 基座
├── sub-react  // react子应用
└── sub-vue    // vue子应用

基座是用vue搭建,子应用有reactvue

基座配置

基座main采用是的Vue-Cli3搭建的,它只负责导航的渲染和登录态的下发,为子应用提供一个挂载的容器div,基座应该保持简洁(qiankun官方demo甚至直接使用原生html搭建),不应该做涉及业务的操作。

qiankun这个库只需要在基座引入,在main.js中注册子应用,为了方便管理,我们将子应用的配置都放在:main/src/micro-app.js下。

const microApps = [
  {
    name: 'sub-vue',
    entry: '//localhost:7777/',
    activeRule: '/sub-vue',
    container: '#subapp-viewport', // 子应用挂载的div
    props: {
      routerBase: '/sub-vue' // 下发路由给子应用,子应用根据该值去定义qiankun环境下的路由
    }
  },
  {
    name: 'sub-react',
    entry: '//localhost:7788/',
    activeRule: '/sub-react',
    container: '#subapp-viewport', // 子应用挂载的div
    props: {
      routerBase: '/sub-react'
    }
  }
]

export default microApps

然后在src/main.js中引入

import Vue from 'vue';
import App from './App.vue';
import { registerMicroApps, start } from 'qiankun';
import microApps from './micro-app';

Vue.config.productionTip = false;

new Vue({
  render: h => h(App),
}).$mount('#app');

registerMicroApps(microApps, {
  beforeLoad: app => {
    console.log('before load app.name====>>>>>', app.name)
  },
  beforeMount: [
    app => {
      console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name);
    },
  ],
  afterMount: [
    app => {
      console.log('[LifeCycle] after mount %c%s', 'color: green;', app.name);
    }
  ],
  afterUnmount: [
    app => {
      console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name);
    },
  ],
});

start();

App.vue中,需要声明micro-app.js配置的子应用挂载div(注意id一定要一致),以及基座布局相关的,大概这样:

<template>
  <div id="layout-wrapper">
    <div class="layout-header">头部导航</div>
    <div id="subapp-viewport"></div>
  </div>
</template>

这样,基座就算配置完成了。项目启动后,子应用将会挂载到<div id="subapp-viewport"></div>中。

子应用配置

一、vue子应用

用Vue-cli在项目根目录新建一个sub-vue的子应用,子应用的名称最好与父应用在src/micro-app.js中配置的名称一致(这样可以直接使用package.json中的name作为output)。

  1. 新增vue.config.js,devServer的端口改为与主应用配置的一致,且加上跨域headersoutput配置。
// package.json的name需注意与主应用一致
const { name } = require('../package.json')

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': '*' // 主应用获取子应用时跨域响应头
    }
  }
}
  1. 新增src/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__;
  }
})();
  1. src/router/index.js改为只暴露routes,new Router改到main.js中声明。
  2. 改造main.js,引入上面的public-path.js,改写render,添加生命周期函数等,最终如下:
import './public-path' // 注意需要引入public-path
import Vue from 'vue'
import App from './App.vue'
import routes from './router'
import store from './store'
import VueRouter from 'vue-router'

Vue.config.productionTip = false
let instance = null

function render (props = {}) {
  const { container, routerBase } = props
  const router = new VueRouter({
    base: window.__POWERED_BY_QIANKUN__ ? routerBase : process.env.BASE_URL,
    mode: 'history',
    routes
  })
  instance = new Vue({
    router,
    store,
    render: (h) => h(App)
  }).$mount(container ? container.querySelector('#app') : '#app')
}

if (!window.__POWERED_BY_QIANKUN__) {
  render()
}

export async function bootstrap () {
  console.log('[vue] vue app bootstraped')
}

export async function mount (props) {
  console.log('[vue] props from main framework', props)

  render(props)
}

export async function unmount () {
  instance.$destroy()
  instance.$e
  • 37
    点赞
  • 74
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值