umijs/plugin-qiankun 微前端 记录

1,002 阅读5分钟

1,微前端的理解

个人理解,我们写过很多项目,可能下一个项目会用到上一个项目的东西,而上一个项目和现在项目用到的框架不一样,比如上一个项目用的vue框架,现在的项目用的react开发。如果现在的项目想集成上个项目的内容,重新开发就很浪费资源。而微前端能解决这个问题。

2,umijs/plugin-qiankun 搭建项目

官方文档地址

umijs.org/zh-CN/plugi…

3,准备工作

  1. 我的主应用采用的umi进行搭建,采用内置插件的layout
  2. 搭建了3个子应用,2个umi搭建的子应用,一个vue搭建的子应用 umi官方地址

umijs.org/zh-CN/docs

4,项目框架图片

主应用-- umi

image.png

子应用1 --umi

image.png

子应用2 -- vue

image.png

5,项目配置

一: 主应用配置

package.json 配置插件版本

umi ^3.5.23

umijs/plugin-qiankun ^2.37.2

注意:主应用和子应用都要下载插件才能进行通信

npm i @umijs/plugin-qiankun -D 或者 yarn add @umijs/plugin-qiankun -D

  1. 在.umirc.ts文件夹中进行配置
import { defineConfig } from 'umi';
export default defineConfig({
  title: 'qiankun-demo', 
  // favicon: '../src/public/qiankun.png', // 修改浏览器上的icon,相对路径不成功
  favicon: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fblog%2F202012%2F04%2F20201204182229_e1a0a.thumb.1000_0.jpeg&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1653034439&t=1a24e65fd2a3d82a7390ca82cfad6041', // 修改浏览器上的icon
  nodeModulesTransform: {
    type: 'none',
  },
  // 内置 antd,目前内置版本是 ^4.0.0
  antd: {
    // dark: true,
    compact: true,
  },
  layout: {
    name: '乾坤微应用demo', //产品名称
    locale: false, //是否开启国际化,开启后路由配置的菜单名会被当做菜单名国际化的key
    // logo: '../src/public/qiankun.png',//相对路径不成功
    logo:'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fblog%2F202012%2F04%2F20201204182229_e1a0a.thumb.1000_0.jpeg&refer=http%3A%2F%2Fc-ssl.duitang.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1653034439&t=1a24e65fd2a3d82a7390ca82cfad6041'
    
  },
  routes: [
    { path: '/', component: '@/pages/index' },
    // { path: '/login', component: '@/pages/Login/index'},
    {
      path: '/dashboard',
      name: '显示方式',
      icon: 'dashboard',
      routes: [
        {
          path: '/dashboard/analysis',
          icon: 'AreaChartOutlined',
          name: '微应用直接显示',
          component: '@/pages/Dashboard/Analysis',
          // 微应用直接显示
          microApp: 'app2'
        },
        {
          path: '/dashboard/monitor',
          icon: 'DesktopOutlined',
          name: '主应用中嵌套MicroApp',
          // 在主应用中嵌套MicroApp
          component: '@/pages/Dashboard/Monitor',
          // microApp: 'app1'
        },
      ],
    },
    {
      path: '/app1',
      name: 'App1-umi子应用',
      icon: 'RadarChartOutlined',
      microApp: 'app1',
      access: 'canReadFoo', // 权限定义返回值的某个 key
      // 设置加载动画
      // microAppProps: {
      //   autoSetLoading: true,
      //   // className: 'myContainer',
      //   // wrapperClassName: 'myWrapper',
      // },
      props: {
        onClick: (event:number) => console.log(event),
        name: 'App1-umi子应用props',
        age: 1,
      },
    },
    {
      path: '/app2',
      name: 'APP2-umi子应用',
      icon: 'AreaChartOutlined',
      microApp: 'app2',
      // microAppProps: {
      //   autoSetLoading: true,
      //   // className: 'myContainer',
      //   // wrapperClassName: 'myWrapper',
      // },
      props: {
        onClick: (event:any) => console.log(event),
        name: 'name: App2-umi子应用props',
        age: 1,
      },
    },
    {
      path: '/app-vue',
      name: 'vue子应用',
      // 只在vue项目里面出现加载动画?
      // microAppProps: {
      //   autoSetLoading: true,
      //   className: 'myContainer',
      //   wrapperClassName: 'myWrapper',
      // },
      icon: 'DotChartOutlined',
      microApp: 'qiankun-vue',
      props: {
        onClick: (event:any) => console.log(event),
        name: 'name: vue子应用props',
        age: 1,
      },
    },
    {
      path: '/vue-ayp',
      name: 'anyapeng',
      icon: 'smile',
      microApp: 'vue-ayp',
      props: {
        onClick: (event:any) => console.log(event),
        name: 'vue-ayp子应用props',
        age: 1,
      },
    },
  ],
  // Umi 内置了以下别名:
  //   @,项目 src 目录
  // @@,临时目录,通常是 src/.umi 目录
  // umi,当前所运行的 umi 仓库目录
  // react-router 和 react-router-dom,底层路由库,锁定版本,打包时所有依赖了他们的地方使用同一个版本
  // react 和 react-dom,默认使用 16.x 版本,但如果项目里有依赖,会优先使用项目中依赖的版本
  fastRefresh: {},//快速刷新(Fast Refresh),开发时可以保持组件状态,同时编辑提供即时反馈。
  qiankun: {
    master: {
      // 注册子应用信息
      apps: [
        {
          name: 'app1', // 唯一 id
          entry: '//localhost:8001', // html entry
        },
        {
          name: 'app2', // 唯一 id
          entry: '//localhost:8002', // html entry
        },
        {
          name: 'qiankun-vue', // 唯一 id
          entry: '//localhost:8888', // html entry
        },
        {
          name: 'vue-ayp', // 唯一 id
          entry: '//localhost:10000/', // html entry
        },
        // {
        //   name: '/app-react', // 唯一 id
        //   entry: '//localhost:8899', // html entry
        // },
      ],
    },
  },
});
  1. app.ts进行配置(主应用进行配置 可以将参数传递给子应用,进行通信)
// 运行时配置文件,可以在这里扩展运行时的能力,比如修改路由、修改 render 方法等。
import {useState} from 'react'
export  function useQiankunStateForSlave() {
    const [Number, setNumber] = useState({});
  
    return {
        Number,
      setNumber,
    };
  }

二,子应用配置

  1. .umi搭建的子应用

1.1. umi搭建的子应用配置,插件下载;注意:主应用和子应用都要下载插件才能进行通信

npm i @umijs/plugin-qiankun -D 或者 yarn add @umijs/plugin-qiankun -D

我的这个版本的umi搭建的项目没有app.ts,所以需要在项目的根目录建一个app.ts文件,暴露出来qiankun这个对象;

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

1.2. 确定端口,根目录没有 .env 文件的,建立一个.env文件设置一下端口,主应用根据端口匹配到页面,例如:

PORT=8001

1.3. 配置 .umirc.ts

import { defineConfig } from 'umi';
export default defineConfig({
  nodeModulesTransform: {
    type: 'none',
  },
  routes: [
    { path: '/', component: '@/pages/index' },
    {
      path: '/app1/app2',
      name: 'vue',
      icon: 'DotChartOutlined',
      microApp: 'app2'
    },
  ],
  fastRefresh: {},
  // 在这里注册乾坤注册(config.js)直接在config.js这里面写没有作用
  // qiankun: {  slave: {} }, 这个必须要写,不然子应用链接不上主应用
  qiankun: {
    slave: {},
    master: {
      // 注册子应用信息
      apps: [
        {
          name: 'app2', // 唯一 id
          entry: '//localhost:8002', // html entry
        },
      ],
    },
  },
});

  1. vue搭建子应用 2.1 下载插件

npm i @umijs/plugin-qiankun -D 或者 yarn add @umijs/plugin-qiankun -D

2.2 配置 .env文件

PORT = 8888

2.2 配置config.vue.js

const { name } = require('./package');
const path = require('path')
module.exports = {
  devServer: {
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
  },
 
  configureWebpack: {
    // 设置别名
    resolve: {
      alias: {
        "@": path.resolve(__dirname, "src"),
      },
    },
    output: {
      // 微应用的包名,这里与主应用中注册的微应用名称一致
      library: `${name}-[name]`,
      // 将你的 library 暴露为所有的模块定义下都可运行的方式
      libraryTarget: 'umd',
      // 按需加载相关,设置为 webpackJsonp_MicroAppPlugin 即可
      jsonpFunction: `webpackJsonp_vue`,
    },
  },
};

2.4 配置 main.js

import Vue from 'vue'
import VueRouter from 'vue-router';
import  routes from './router.js';
console.log(routes,'routes')
// import store from './store';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue'
// import * as PublicPath from './public-path'
Vue.use(ElementUI);
Vue.use(VueRouter)
Vue.config.productionTip = false

let router = null;
let instance = null;

// new Vue({
//   render: h => h(App),
// }).$mount('#app')
function render(props = {}) {
  const { container } = props;
   router = new VueRouter({
    //  微应用配置路由 基础路径
    base: window.__POWERED_BY_QIANKUN__ ? '/app-vue/' : '/',
    mode: 'history',
    routes,
  });
  console.log(router,'router222')

  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子应用bootstrap');
}
export async function mount(props) {
  console.log('vue子应用mount', props);
  render(props);
}
export async function unmount(props) {
  console.log('vue子应用卸载', props);
  instance.$destroy();
  instance.$el.innerHTML = '';
  instance = null;
  router = null;
}

三,父子组件通信

  1. 使用MicroApp 组件通信 1.1 父应用
mport { MicroApp } from 'umi';
import { useState } from 'react';
import { Button } from 'antd';

export default function IndexPage() {
  const [ age ,setAge] = useState(1)
  const onAdd = ()=>{
    setAge(age+1)
  }
  return (
    <div>
      <Button onClick ={()=>{onAdd()}}>父应用增加年龄</Button>
        <div>{age}</div>
        {/* MicroApp 像props一样参 */}
       <MicroApp name="vue-ayp" onChange={onAdd} age={age} />
    </div>
  );
}

1.2 子应用app1

import { useModel } from 'umi';

function MyPage() {
    //  useModel('@@qiankunStateFromMaster') 子应用可以在其内部全局拿到父应用传递过来的参数
    const masterProps = useModel('@@qiankunStateFromMaster');
    console.log(masterProps, 'masterProps====')
    const { numberF, onChange } = masterProps
    const onAdd = () => {
        onChange()
    }

    return (
        <div>
            {numberF ? 
            <div>
            <Button onClick={() => { onAdd() }}>App1-umi本地应用点击加一</Button>
            <div>{numberF}</div>
           </div>
            : 
            'App1-umi本地应用无点击事件'
            }
        </div>
    )
}

2,未完待续。。。。