微前端qiankun项目,各框架配置(Vue2、Vue3+vite、React+vite、React(webpack))配置

553 阅读5分钟

此文章以vue2为主应用为例,配置的子应用包含Vue2、Vue3+vite、React+vite、React(webpack),入门小Demo,简单配置运行,如果配置有错误的地方欢迎大家指正.(补充:导航菜单的路由路径根据第一个子应用的示例自行添加,以达到切换效果)

先上效果图! test.gif

一、主应用搭建(Vue2)

(1)主应用项目创建

使用的是Vue/cli创建的项目

1. 创建项目

步骤就不一一列举

npm i -g @vue/cli   //其他的包管理工具就不多赘述

2. 修改端口号

vue.config.js修改当前项目的端口号:

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  //我这里就以5000为例以此类推
  devServer: {
    port: 5000
  }
})

3. 启动项目

npm run serve

(2)主应用页面

我们自己通过element-ui简单搭建了一个布局和菜单 image.png

(3)主应用配置qiankun

1. 路由配置

// src/router/index.js
const routes = [
  {
    path: '/',
    name: 'home',
    component: HomeView,
    redirect: "/home",
    meta: {
      base: true
    },
    children: [
      {
        path: 'home',
        component: IndexView,
        meta: {
          base: true
        },
      },
      {
        path: 'user',
        component: UserView,
        meta: {
          base: true
        },
      }
    ]
  },
  // 给其他qiankun子应用页面使用的路由
  {
    path: "/*",
    component: HomeView
  }
]

2. 下载qiankun

npm i qiankun

3. 配置qiankun

创建一个qiankun的配置文件

src
|--- qiankun
| |--- index.js

添加以下配置

// src/qiankun/index.js
import { registerMicroApps, start } from 'qiankun';
const apps = []
// 注册微(子)应用
registerMicroApps(apps);
export default start;

4. 全局启动qiankun

main.js中引入启动qiankun:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
//引入qiankun
import qiankunStart from './qiankun'

import './plugins/element.js'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.config.productionTip = false
Vue.use(ElementUI);

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')
//启动qiankun
qiankunStart();

5.配置子应用出口 (主应用配置完成)

在布局中添加出口

 <el-main>
     <!-- 根据设置的标识来判断是主应用还是子应用 --> 
     <router-view v-if="$route.meta.base"></router-view>
     <!-- 设置子应用出口 -->
     <div id="container" v-show="!$route.meta.base"></div>
</el-main>

二、子应用配置(Vue2)

(1)搭建子应用

子应用搭建完成更改端口号:

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  devServer: {
    port: 5001
  },
})

(2)子应用页面

  1. 创建页面组件
  2. 配置路由(有变化,往下看)
  3. 启动项目

(3)配置qiankun子应用

1.修改webpack path配置

src中创建一个public-path.js文件,添加以下代码

if (window.__POWERED_BY_QIANKUN__) {
    window.__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

2.拆分路由配置

原来路由的配置文件(注释为删除的,在main配置)

// import Vue from 'vue'
// import VueRouter from 'vue-router'
import UserView from '@/views/UserView.vue'
import IndexView from '@/views/IndexView.vue'
// Vue.use(VueRouter)
const routes = [
  {
    path: '/index',
    component: IndexView
  },
  {
    path: '/user',
    component: UserView
  }
]
// const router = new VueRouter({
//   routes,
//   mode: history
// })
export default routes

main中添加路由配置

import Vue from 'vue'
import App from './App.vue'
//引入路由
import VueRouter from 'vue-router'
import routes from './router';
Vue.use(VueRouter)

import '@/public-path'
import store from './store'
Vue.config.productionTip = false

let router = null;
let instance = null;
function render(props = {}) {
    const { container } = props;
    // 挂载路由
    router = new VueRouter({
        // `/children1`为子应用的路由前缀
        base: window.__POWERED_BY_QIANKUN__ ? '/children1' : '/',
        mode: 'history',
        routes,
    });
    // Vue实例
    instance = new Vue({
        router,
        store,
        render: (h) => h(App),
    }).$mount(container ? container.querySelector('#app') : '#app');
}
// 独立运行时(没有作为 qiankun 的子应用运行)
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}
// qiankun 子应用的生命周期函数
export async function bootstrap() {
  console.log('Vue2 的子应用初始化完成');
}
export async function mount(props) {
  console.log('Vue2 的子应用挂载完成', props);
  render(props);
}
// 子应用销毁
export async function unmount() {
  instance.$destroy();
  instance.$el.innerHTML = '';
  instance = null;
  router = null;
}

3.配置子应用的 webpack

vue.config.js文件中

const { defineConfig } = require('@vue/cli-service')
// const { name } = require('./package');
module.exports = defineConfig({
  transpileDependencies: true,
  devServer: {
    port: 5005,
    // 设置请求头
    headers: {
      'Access-Control-Allow-Origin': '*',
    }
  },
  configureWebpack: {
    output: {
      library: 'chlidren-vue2',  // 设置子应用的名称
      libraryTarget: 'umd', // 把微应用打包成 umd 库格式
      // jsonpFunction: `webpackJsonp_${name}`,  //官网这里有这一行,但是我保留了会报错所以注释
      //自行尝试
    },
})

(4)主应用注册子应用 (子应用Vue2配置完成)

在主应用的src/qiankun/index.js文件中:

import { registerMicroApps, start } from 'qiankun';

const apps = [
    {
      name: 'chlidren-vue2',      //子应用名称
      entry: '//localhost:5001',  //子应用的端口
      container: '#container',    //子应用出口容器的标签
      activeRule: '/children1',   //子应用的路由前缀
    },
]

// 注册微(子)应用
registerMicroApps(apps);
export default start;

在菜单添加跳转进行测试

<el-submenu index="1" style="height: 100%;">
     <template slot="title">
      <span>主应用(Vue2)</span>
    </template>
    <el-menu-item-group>
      <el-menu-item index="/home">Vue2主页</el-menu-item>
      <el-menu-item index="/user">Vue2用户</el-menu-item>
    </el-menu-item-group>
</el-submenu>

三、子应用配置(React)

(1)创建React子应用

1.创建项目

npx create-react-app react-app

2.下载依赖

.........路由(必备),其他随意

3.修改端口号

在项目根目录创建.env的文件,添加以下代码:

PORT=5002

(2)搭建子应用页面

  1. 创建组件
  2. 配置路由
  3. 启动项目

(3)配置qiankun

1.修改webpack path 配置

src 中创建一个 public-path.js 文件,添加以下代码

if (window.__POWERED_BY_QIANKUN__) {
    window.__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

2.设置history模式路由的base

App.js配置:

const App = () => {
  return (
    <BrowserRouter basename={window.__POWERED_BY_QIANKUN__ ? '/children2' : '/'}>
      <Routes>
        <Route path="/index" element={<Index />}></Route>
        <Route path='/user' element={<User />}></Route>
      </Routes>
    </BrowserRouter>
  )
}

3.配置qiankun子应用

index.js配置:

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import './public-path';

let root = null;
function render (props) {
  const { container } = props;
  root = ReactDOM.createRoot(
    container ? container.querySelector('#root') : document.getElementById('root')
  );
  root.render(
    <App />
  );
}
if (!window.__POWERED_BY_QIANKUN__) {
  render({});
}
export async function bootstrap () {
  console.log('React 子应用初始化');
}
export async function mount (props) {
  console.log('React 子应用挂载完成', props);
  render(props);
}
export async function unmount (props) {
  if (root) {
    root.unmount();
    root = null;
  }
}

(4)配置webpack

1. 下载插件

npm i react-app-rewired -D

2.更改启动项目命令(重要)

package.json文件中,修改启动命令:

"scripts": {
   "start": "react-app-rewired start",
   "build": "react-app-rewired build",
   "test": "react-app-rewired test",
   "eject": "react-scripts eject"
},

3.创建配置文件

在项目根目录创建config-overrides.js文件,作为React项目中webpack的配置.

4.配置webpack

在文件config-overrides.js添加以下代码

module.exports = {
    webpack: (config) => {
        //子应用名称
        config.output.library = 'children-react';
        config.output.libraryTarget = 'umd';
        return config;
    },
    devServer: (configFunction) => {
        return (proxy, allowedHost) => {
            const config = configFunction(proxy, allowedHost);
            config.headers = {
                'Access-Control-Allow-Origin': '*',
            }
            // 配置 history 模式
            config.historyApiFallback = true;
            return config;
        }
    }
}

5.注册子应用

const apps = [
 //Vue2子应用
    {
        name: 'chlidren-vue2',
        entry: '//localhost:5001',
        container: '#container',
        activeRule: '/children1',
    },
    //react子应用
    {
        name: 'children-react',
        entry: '//localhost:5002',
        container: '#container',
        activeRule: '/children2',
    },
]

四、子应用(Vue3+vite)

(1)搭建项目

1.创建Vue3项目

yarn create vite vue3-app --template vue-ts

2.下载路由依赖等....

3.修改端口号

vite.config.ts文件中修改端口号:

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  server: {
    port: 5003,
  },
});

(2)搭建子应用页面

1.创建页面组件

2.配置路由

import { createRouter, createWebHistory } from "vue-router";
import { qiankunWindow } from "vite-plugin-qiankun/dist/helper";
import Index from "../views/index/HelloWorld.vue";
import User from "../views/user/User.vue";
const routes = [
  {
    path: "/index",
    component: Index,
  },
  {
    path: "/user",
    component: User,
  },
];

const router = createRouter({
  routes,
  history: createWebHistory(
    qiankunWindow.__POWERED_BY_QIANKUN__ ? "/children3" : "/"
  ),
});

export default router;

路由配置完成挂载全局

import router from './router'
createApp(App).use(router).mount('#app')

3.配置路由出口

测试是否可以单独访问

(3)配置qiankun子应用

1.下载插件

yarn add vite-plugin-qiankun -D

2.配置qiankun子应用

main.ts配置:

import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";

// 全局挂载路由
import router from "./router";
// 引入qiankun
import {
  qiankunWindow,
  renderWithQiankun,
} from "vite-plugin-qiankun/dist/helper";

let app: any = null;
function render(props: any = {}) {
  const { container } = props;
  app = createApp(App);
  app.use(router).mount(container ? container.querySelector("#app") : "#app");
}
// 独立运行
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
  render();
} else {
  // 作为子应用运行
  renderWithQiankun({
   //生命周期函数
    mount(props) {
      render(props);
    },
    bootstrap() {
      console.log("Vue3子应用挂载完成");
    },
    update() {
      console.log("Vue3子应用更新");
    },
    unmount() {
      app.unmount();
    },
  });
}

3.配置vite子应用

vite.config.ts中完整配置

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import qiankun from "vite-plugin-qiankun";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    qiankun("children-vue3", {
      useDevMode: true,
    }),
  ],
  server: {
    port: 5002,
    headers: {
      "Access-Control-Allow-Origin": "*",
    },
  },
});

(4)主应用注册子应用

const apps = [
    {
        name: 'chlidren-vue2',
        entry: '//localhost:5001',
        container: '#container',
        activeRule: '/children1',
    },
    {
        name: 'children-react',
        entry: '//localhost:5002',
        container: '#container',
        activeRule: '/children2',
    },
    {
        name: 'children-vue3',
        entry: '//localhost:5003',
        container: '#container',
        activeRule: '/children3',
    }
]

五、子应用(React+vite)

(1)搭建项目

1.创建React项目

yarn create vite children-reactVite --template react-ts

2.下载依赖

3.修改端口参照Vue3

(2)搭建子应用

配置路由 src/router/RouterConfig.tsx

import Index from '../pages/index/Index'
import { useRoutes } from 'react-router-dom'
import User from '../pages/user/User'
const routes = [
    {
        path: '/index',
        element: <Index />
    },
    {
        path: '/user',
        element: <User />
    }
]
const RouterConfig = () => {
    const router = useRoutes(routes)
    return (
        <>
            {router}
        </>
    )
}
export default RouterConfig

(3)下载插件

yarn add vite-plugin-qiankun -D

(4)配置页面路由

import { Suspense } from "react"
//引入qiankunWindow
import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper';
import { BrowserRouter } from 'react-router-dom'
import RouterConfig from "./router/RouterConfig"

function App() {

  return (
    <>
      <Suspense >
          //这里的window改为qiankunWindow
        <BrowserRouter basename={qiankunWindow.__POWERED_BY_QIANKUN__ ? '/children4' : '/'}>
          <RouterConfig></RouterConfig>
        </BrowserRouter>
      </Suspense>
    </>
  )
}

export default App

(5)路由挂载全局

main.js挂载路由

import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'
//引入插件
import { qiankunWindow, renderWithQiankun } from 'vite-plugin-qiankun/dist/helper';

let root: any = null;
function render(props: any) {
  const { container } = props;
  root = ReactDOM.createRoot(
    container ? container.querySelector('#root') : document.getElementById('root')
  );
  root.render(
    <App />
  );
}
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
  render({});
} else {
  // 生命周期函数
  renderWithQiankun({
    mount(props) {
      render(props);
    },
    bootstrap() {
      console.log('React子应用挂载完成');
    },
    update() {
      console.log('React子应用更新');
    },
    unmount() {
      root.unmount();
      root = null;
    },
  })
}

(6)配置vite子应用

vite.config.ts中配置

import { defineConfig } from "vite";
import qiankun from "vite-plugin-qiankun";
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    // 一定要删除这一行
    // react(),
    qiankun("children-reactVite", {
      useDevMode: true,
    }),
  ],
  server: {
    port: 5004,
    headers: {
      "Access-Control-Allow-Origin": "*",
    },
  },
});

(7)主应用注册子应用

const apps = [
    {
        name: 'chlidren-vue2',
        entry: '//localhost:5001',
        container: '#container',
        activeRule: '/children1',
    },
    {
        name: 'children-react',
        entry: '//localhost:5002',
        container: '#container',
        activeRule: '/children2',
    },
    {
        name: 'children-vue3',
        entry: '//localhost:5003',
        container: '#container',
        activeRule: '/children3',
    },
    {
        name: 'children-reactVite',
        entry: '//localhost:5004',
        container: '#container',
        activeRule: '/children4',
    }
]