qiankun微前端实践(一)

512 阅读2分钟

微前端

最近在着手重构整个公司的大后台,目前情况,后台项目过多,所使用的技术栈都不一致

刚好有机会实践微前端技术

本文写法简洁,旨在记录实现过程,并未记录详细细节,敬请谅解

后续会记录一系列遇到的坑点和问题

qiankun Demo

github.com/950905/qian…

1、主应用

经过一番技术的battel,最终决定基座采用ant-design-pro开箱即用,非常方便

1、使用ant-design-pro搭建主项目

使用官方脚手架

2、引入@umijs/plugin-qiankun npm包
3、config.ts配置文件加入qiankun的配置

涉及到菜单以及权限控制,则采用动态加载子应用和路由的思路

qiankun: {
  master: {}
}
4、采用组件引入的方式

在layous文件夹新建MicroAppLayout.tsx

import { MicroApp } from 'umi';
import React from 'react';
import { PageContainer } from '@ant-design/pro-layout';

function MicroAppLayout() {
  const name = location.pathname.split('/')[1];
  console.log(name)
  return (
    <PageContainer>
      <MicroApp name={name} />
    </PageContainer>
  )
}

export default MicroAppLayout;
5、添加动态mock数据

注:暂未实现接口,所有数据来源于mock

在mock文件夹新建config.ts加入以下代码

export default {
  '/api/config': {
    apps: [
      {
        name: 'sub-app-1',
        entry: '//localhost:8001',
      },
      {
        name: 'sub-app-2',
        entry: '//localhost:8002',
      },
    ],
    routes: [
      {
        name: 'sub-app-1',
        path: '/sub-app-1',
        routes: [
          {
            name: 'sub-app-1-first',
            path: '/sub-app-1/first',
          },
          {
            name: 'sub-app-1-second',
            path: '/sub-app-1/second',
          }
        ]
      },
      {
        name: 'sub-app-2',
        path: '/sub-app-2',
      }
    ],
  },
};
6、动态加载子应用

在src目录添加app.tsx文件

动态加载子应用代码如下:

export const qiankun = fetch('/api/config')
  .then((res) => {
    return res.json();
  })
  .then(({ apps }) => {
    return Promise.resolve({
      // 注册子应用信息
      apps,
      lifeCycles: {
        afterMount: (props: any) => {
          console.log(props);
        },
      },
      // 支持更多的其他配置,详细看这里 https://qiankun.umijs.org/zh/api/#start-opts
    });
  });
7、动态加载路由
export function patchRoutes({ routes }: IRouteObj) {
  extraRoutes.forEach((element: IElementItem) => {
    routes[1].routes[0].routes.unshift({
      name: element.name,
      icon: 'smile',
      path: element.path,
      component: dynamic({
        loader: () =>
          import('@/layouts/MicroAppLayout'),
        loading: LoadingComponent,
      }),
      routes: element.routes
    });
  });
}

export async function render(oldRender: Function) {
  fetch('/api/config')
    .then((res) => {
      return res.json();
    })
    .then((resJson) => {
      extraRoutes = resJson.routes;
      oldRender();
    });
}

2、子应用

1、添加子应用,并暴露子应用周期方法

react子应用

export const qiankun = {
  // 应用加载之前
  async bootstrap(props) {
    console.log('app1 bootstrap', props);
  },
  // 应用 render 之前触发
  async mount(props) {
    console.log('app1 mount', props);
  },
  // 应用卸载之后触发
  async unmount(props) {
    console.log('app1 unmount', props);
  },
};

vue子应用

1)修改Vue项目的入口文件main.js

let router = null
let instance = null

function render() {
  router = new VueRouter({
    base: window.__POWERED_BY_QIANKUN__ ? '/live' : '/',
    mode: 'history',
    routes,
    scrollBehavior (to, from, savedPosition) {
      return { x: 0, y: 0 }
    }
  });

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

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

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

export async function mount(props) {
  console.log('props from main app', props)
  render()
}

export async function unmount() {
  instance.$destroy()
  instance = null
  router = null
}

2)引入webpack publicPath

if (window.__POWERED_BY_QIANKUN__) {
  // eslint-disable-next-line no-undef
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}

3)修改vue.config.ts打包

configureWebpack: {
    output: {
      // 把子应用打包成 umd 库格式
      library: 'live', // 要和主应用定义的名字保持一致
      libraryTarget: 'umd',
      jsonpFunction: `webpackJsonp_${name}`,
    }
  },

以上这样,一个简易的微前端已经完成了

启动本地服务可以看到效果