qiankun微前端地图应用集成最佳实践

qiankun微前端地图应用集成最佳实践

【免费下载链接】qiankun 📦 🚀 Blazing fast, simple and complete solution for micro frontends. 【免费下载链接】qiankun 项目地址: https://gitcode.com/gh_mirrors/qi/qiankun

背景与挑战

在大型企业级应用中,地图服务往往需要集成到多个业务系统中,传统单体应用架构面临以下痛点:地图SDK体积庞大导致首屏加载缓慢、多团队并行开发冲突、不同业务模块地图版本不一致。qiankun微前端框架通过将地图应用拆分为独立微应用,可有效解决这些问题。

本文将从实际场景出发,介绍如何基于qiankun构建高性能地图微应用,包括主应用配置、微应用改造、跨应用通信、样式隔离等关键技术点,并提供高德地图/百度地图集成的具体案例。

集成架构设计

地图微应用架构图

mermaid

技术选型表

应用类型技术栈职责部署端口
主应用React + TypeScript应用管理、路由分发8080
地图微应用Vue3 + 高德地图2.0地图渲染、POI展示8081
数据微应用Angular + ECharts数据可视化、统计分析8082

快速上手:5分钟集成地图微应用

1. 主应用配置

首先安装qiankun依赖:

yarn add qiankun # 或者 npm i qiankun -S

在主应用入口文件中注册地图微应用:

// src/index.tsx
import { registerMicroApps, start } from 'qiankun';
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';

// 注册地图微应用
registerMicroApps([
  {
    name: 'map-app', // 应用名称
    entry: '//localhost:8081', // 微应用入口地址
    container: '#mapContainer', // 挂载容器
    activeRule: '/map', // 激活路由规则
    props: {
      // 传递给微应用的初始化参数
      mapKey: 'your-amap-key', // 地图SDK密钥
      defaultCenter: [116.397470, 39.909230] // 默认中心点坐标
    }
  },
]);

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// 启动qiankun
start({
  sandbox: { 
    experimentalStyleIsolation: true, // 开启样式隔离
    strictStyleIsolation: true
  }
});

在主应用页面中添加地图容器:

// src/App.tsx
import { MicroApp } from '@qiankunjs/react';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <h1>企业级地图应用平台</h1>
      </header>
      <main>
        {/* 地图微应用容器 */}
        <div id="mapContainer" style={{ width: '100%', height: '600px' }}></div>
      </main>
    </div>
  );
}

export default App;

2. 地图微应用改造

导出生命周期钩子

在微应用入口文件中导出qiankun所需的生命周期钩子:

// src/main.js
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';

let app;

// 独立运行时直接渲染
if (!window.__POWERED_BY_QIANKUN__) {
  createApp(App).use(store).use(router).mount('#app');
}

// 微应用模式下导出生命周期钩子
export async function bootstrap(props) {
  console.log('map-app bootstrap', props);
}

export async function mount(props) {
  console.log('map-app mount', props);
  
  // 创建Vue应用
  app = createApp(App);
  
  // 存储主应用传递的参数
  app.config.globalProperties.$mapConfig = props;
  
  // 挂载应用
  app.use(store).use(router).mount(props.container ? props.container.querySelector('#app') : '#app');
}

export async function unmount() {
  console.log('map-app unmount');
  app.unmount();
  app = null;
}
配置webpack

修改微应用的vue.config.js,配置输出格式:

// vue.config.js
const packageName = require('./package.json').name;

module.exports = {
  devServer: {
    port: 8081,
    headers: {
      'Access-Control-Allow-Origin': '*', // 允许跨域访问
    },
  },
  configureWebpack: {
    output: {
      library: `${packageName}-[name]`,
      libraryTarget: 'umd', // 必须是umd格式
      jsonpFunction: `webpackJsonp_${packageName}`,
    },
  },
};

地图SDK加载优化

运行时publicPath配置

在微应用入口文件顶部添加以下代码,解决动态资源加载路径问题:

// src/public-path.js
if (window.__POWERED_BY_QIANKUN__) {
  // 动态设置webpack publicPath
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

// 在main.js中导入
import './public-path';

地图SDK加载策略

使用动态加载方式引入地图SDK,避免阻塞主应用渲染:

// src/utils/mapLoader.js
export function loadAMapSDK(key) {
  return new Promise((resolve, reject) => {
    // 判断是否已加载
    if (window.AMap) {
      resolve(window.AMap);
      return;
    }
    
    // 创建script标签
    const script = document.createElement('script');
    script.src = `https://webapi.amap.com/maps?v=2.0&key=${key}&callback=initAMap`;
    script.type = 'text/javascript';
    script.onerror = reject;
    
    // 定义回调函数
    window.initAMap = () => {
      resolve(window.AMap);
    };
    
    document.head.appendChild(script);
  });
}

在地图组件中使用:

<!-- src/components/MapView.vue -->
<template>
  <div class="map-container" ref="mapContainer"></div>
</template>

<script setup>
import { ref, onMounted, getCurrentInstance } from 'vue';
import { loadAMapSDK } from '../utils/mapLoader';

const mapContainer = ref(null);
const instance = getCurrentInstance();
const mapConfig = instance.appContext.config.globalProperties.$mapConfig;

onMounted(async () => {
  try {
    // 加载地图SDK
    const AMap = await loadAMapSDK(mapConfig.mapKey);
    
    // 初始化地图
    const map = new AMap.Map(mapContainer.value, {
      zoom: 13,
      center: mapConfig.defaultCenter,
      resizeEnable: true
    });
    
    // 添加标记
    new AMap.Marker({
      position: mapConfig.defaultCenter,
      map: map
    });
    
  } catch (error) {
    console.error('地图加载失败:', error);
  }
});
</script>

<style scoped>
.map-container {
  width: 100%;
  height: 500px;
}
</style>

跨应用通信实现

使用props传递初始数据

主应用注册微应用时通过props传递初始配置:

// 主应用注册微应用时
registerMicroApps([
  {
    name: 'map-app',
    entry: '//localhost:8081',
    container: '#mapContainer',
    activeRule: '/map',
    props: {
      mapKey: 'your-amap-key',
      defaultCenter: [116.397470, 39.909230],
      // 提供通信回调函数
      onMapClick: (data) => {
        console.log('主应用接收地图点击事件:', data);
        // 可以在这里通知其他微应用
      }
    }
  },
]);

微应用中调用主应用提供的回调函数:

// 微应用中触发通信
instance.appContext.config.globalProperties.$mapConfig.onMapClick({
  lnglat: [116.404, 39.915],
  timestamp: new Date().getTime()
});

使用全局事件总线

创建一个简单的事件总线用于复杂场景的通信:

// src/utils/eventBus.js
class EventBus {
  constructor() {
    this.events = {};
  }

  on(name, callback) {
    if (!this.events[name]) {
      this.events[name] = [];
    }
    this.events[name].push(callback);
  }

  off(name, callback) {
    if (!this.events[name]) return;
    this.events[name] = this.events[name].filter(fn => fn !== callback);
  }

  emit(name, ...args) {
    if (!this.events[name]) return;
    this.events[name].forEach(fn => fn(...args));
  }
}

// 挂载到window上供所有微应用访问
window.eventBus = new EventBus();

在主应用中监听事件:

// 主应用中
window.eventBus.on('map:markerClick', (data) => {
  console.log('主应用收到标记点击事件:', data);
  // 处理事件
});

在地图微应用中触发事件:

// 地图微应用中
window.eventBus.emit('map:markerClick', {
  id: 'marker-1',
  position: [116.404, 39.915],
  info: '这是一个标记点'
});

样式隔离与冲突解决

启用qiankun样式隔离

在start方法中配置实验性样式隔离:

// 主应用中
start({
  sandbox: {
    experimentalStyleIsolation: true, // 开启实验性样式隔离
    strictStyleIsolation: false
  }
});

地图控件样式穿透

当启用样式隔离后,可能导致地图控件样式异常,可使用以下方式解决:

/* 微应用中 */
/* 使用:deep()穿透样式隔离 */
:deep(.amap-control-bar) {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 100;
}

/* 或者使用全局样式 */
/* 在App.vue中 */
<style>
/* 不加scoped的style会成为全局样式 */
.amap-marker-label {
  border-radius: 4px;
  border: none;
}
</style>

常见问题与解决方案

地图容器找不到问题

当出现"Target container with #container not existed"错误时,检查微应用mount方法中的容器查找逻辑:

// 正确的容器查找方式
export async function mount(props) {
  app = createApp(App);
  app.mount(props.container ? props.container.querySelector('#app') : '#app');
}

地图SDK使用document.write导致的问题

某些旧版地图SDK使用document.write加载资源,会导致容器被清空,解决方案:

  1. 升级地图SDK到最新版本(如高德地图2.x)
  2. 在script标签添加ignore属性,让qiankun忽略该脚本:
<!-- 微应用index.html中 -->
<script src="https://webapi.amap.com/maps?v=2.0&key=your-key" ignore></script>

微应用间地图实例共享

通过主应用共享地图实例,避免重复加载:

// 主应用中加载地图SDK并共享实例
window.loadMapSDK = async () => {
  if (window.sharedMapInstance) {
    return window.sharedMapInstance;
  }
  
  // 加载SDK...
  
  // 创建地图实例
  window.sharedMapInstance = new AMap.Map('shared-map-container', {
    zoom: 13,
    center: [116.397470, 39.909230]
  });
  
  return window.sharedMapInstance;
};

// 微应用中获取共享实例
const mapInstance = await window.loadMapSDK();

性能优化实践

微应用预加载

在主应用空闲时预加载地图微应用资源:

// 主应用中
import { preloadMicroApps } from 'qiankun';

// 在用户可能访问地图功能前预加载
setTimeout(() => {
  preloadMicroApps([
    {
      name: 'map-app',
      entry: '//localhost:8081',
    },
  ]);
}, 3000);

地图瓦片懒加载

实现地图瓦片按需加载,减少初始加载数据量:

// 地图初始化时配置
const map = new AMap.Map(mapContainer.value, {
  zoom: 13,
  center: [116.397470, 39.909230],
  resizeEnable: true,
  // 配置瓦片加载策略
  tileLoadOpts: {
    preventLoad: false,
    // 只加载可视区域瓦片
    visibleOnly: true
  }
});

部署与监控

构建优化

微应用构建时分离公共库:

// vue.config.js
module.exports = {
  configureWebpack: {
    externals: {
      // 将地图SDK排除在构建产物外
      'AMap': 'AMap'
    }
  }
}

错误监控

添加全局错误监控,捕获地图加载异常:

// 微应用中
export async function mount(props) {
  try {
    // 初始化地图逻辑
  } catch (error) {
    console.error('地图微应用加载失败:', error);
    // 上报错误
    if (window.__REPORT_ERROR__) {
      window.__REPORT_ERROR__({
        app: 'map-app',
        error: error.message,
        stack: error.stack,
        time: new Date().toISOString()
      });
    }
  }
}

总结与展望

本文详细介绍了基于qiankun的地图微应用集成方案,包括架构设计、快速集成步骤、SDK加载优化、跨应用通信、样式隔离等关键技术点。通过将地图功能封装为独立微应用,可以显著提升大型应用的开发效率和性能表现。

后续可以进一步探索以下方向:

  • 基于WebWorker的地图数据处理
  • 地图状态的持久化与恢复
  • 多地图服务商的适配抽象层
  • 地图数据的可视化与分析集成

通过qiankun微前端架构,企业可以构建更加灵活、可扩展的地图应用生态系统,满足不同业务场景的需求。

官方文档:docs/guide/getting-started.zh-CN.md 常见问题:docs/faq/README.zh-CN.md 微应用示例:examples/vue/

【免费下载链接】qiankun 📦 🚀 Blazing fast, simple and complete solution for micro frontends. 【免费下载链接】qiankun 项目地址: https://gitcode.com/gh_mirrors/qi/qiankun

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值