微前端实践(qiankun2.43.3 + umi3.5.41)

677 阅读5分钟

微前端实践(qiankun2.43.3 + umi3.5.41)

搭建应用(全 umi: 1 主 + 2 子)

环境配置

umi 基于react
qiankun 基于single-spa


新建umi工程:1主2子
版本依赖: node>10.13.0
配置国内源:tyarn
npm i yarn tyarn -g
tyarn -v

创建主应用

mkdir master-app && cd master-app && yarn create @umijs/umi-app && tyarn
yarn start

创建子应用 1

mkdir app1 && cd app1 && yarn create @umijs/umi-app && tyarn
yarn start

创建子应用 2

mkdir app2 && cd app2 && yarn create @umijs/umi-app && tyarn
yarn start

配置主应用

安装 umi 的 qiankun 插件

cd /master-app
yarn add @umijs/plugin-qiankun -D

/master-app/.umirc.ts (此处为带路由子应用方式,不带路由的子应用可用 MicroApp 组件装载方式)

import { defineConfig } from "umi";

export default defineConfig({
  nodeModulesTransform: {
    type: "none",
  },
  routes: [
    {
      path: "/",
      component: "@/layouts/index",
      // // 配置应用关联的路由
      routes: [
        {
          path: "/app1",
          microApp: "app1",
        },
        {
          path: "/app2",
          microApp: "app2",
        },
      ],
    },
  ],
  fastRefresh: {},
  qiankun: {
    master: {
      // 注册子应用
      apps: [
        {
          name: "app1",
          entry: "http://localhost:8001",
        },
        {
          name: "app2",
          entry: "http://localhost:8002",
        },
      ],
    },
  },
});

/master-app/src/layouts/index.tsx

import React from "react";
import { Link } from "umi";

const Layout: React.FC = ({ children }) => {
  return (
    <>
      <Link to="/app1">app1</Link>
      <Link to="/app2">app2</Link>
      {children}
    </>
  );
};

export default Layout;

配置子应用 app1 (app2 参考 app1)

安装 umi 的 qiankun 插件

cd /app1
yarn add @umijs/plugin-qiankun -D

/app1/.umirc.ts

添加属性 qiankun

export default defineConfig({
  qiankun: {
    slave: {},
  },
});

完整代码

import { defineConfig } from "umi";

export default defineConfig({
  nodeModulesTransform: {
    type: "none",
  },
  routes: [{ path: "/", component: "@/pages/index" }],
  fastRefresh: {},
  qiankun: {
    slave: {},
  },
});

/app1/package.json

增加 name 命名项目

{
  "name": "app1"
}

完整代码

{
  "name": "app1",
  "private": true,
  "scripts": {
    "start": "umi dev",
    "build": "umi build",
    "postinstall": "umi generate tmp",
    "prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'",
    "test": "umi-test",
    "test:coverage": "umi-test --coverage"
  },
  "gitHooks": {
    "pre-commit": "lint-staged"
  },
  "lint-staged": {
    "*.{js,jsx,less,md,json}": ["prettier --write"],
    "*.ts?(x)": ["prettier --parser=typescript --write"]
  },
  "dependencies": {
    "@ant-design/pro-layout": "^6.5.0",
    "react": "17.x",
    "react-dom": "17.x",
    "umi": "^3.5.41"
  },
  "devDependencies": {
    "@types/react": "^17.0.0",
    "@types/react-dom": "^17.0.0",
    "@umijs/plugin-qiankun": "^2.43.3",
    "@umijs/preset-react": "1.x",
    "@umijs/test": "^3.5.41",
    "lint-staged": "^10.0.7",
    "prettier": "^2.2.0",
    "typescript": "^4.1.2",
    "yorkie": "^2.0.0"
  }
}

/app1/src/pages/index.tsx

修改内容

<h1 className={styles.title}>App1</h1>

完整代码

import styles from "./index.less";

export default function IndexPage() {
  return (
    <div>
      <h1 className={styles.title}>App1</h1>
    </div>
  );
}

父子通信

主应用安装依赖 @umijs/preset-react

cd /master-app/src/app.ts
yarn add @umijs/preset-react -D

主应用传值(传动态值 masterState && 传静态值 testUmircProps)

/master-app/src/app.ts 父向子传值方式一:app.ts 传动态值 masterState

import { useState } from "react";

export function useQiankunStateForSlave() {
  const [masterState, setMasterState] = useState({
    testApptsProp: 123,
  });

  return {
    masterState,
    setMasterState,
  };
}

/master-app/.umirc.ts 父向子传值方式二:.umirc.ts 传静态态值 testUmircProps

import { defineConfig } from "umi";

export default defineConfig({
  nodeModulesTransform: {
    type: "none",
  },
  routes: [
    {
      path: "/",
      component: "@/layouts/index",
      // // 配置应用关联的路由
      routes: [
        {
          path: "/app1",
          microApp: "app1",
        },
        {
          path: "/app2",
          microApp: "app2",
        },
      ],
    },
  ],
  fastRefresh: {},
  qiankun: {
    master: {
      // 注册子应用
      apps: [
        {
          name: "app1",
          entry: "http://localhost:8001",
          props: {
            testUmircProps: 123, // 父向子传值方式二:.umirc.ts 传静态态值 testUmircProps
          },
        },
        {
          name: "app2",
          entry: "http://localhost:8002",
        },
      ],
    },
  },
});

子接收值

/app1/src/pages/index.tsx

import styles from "./index.less";
import { useModel } from "umi";

export default function IndexPage() {
  const { masterState, setMasterState, testUmircProps } = useModel(
    "@@qiankunStateFromMaster"
  );
  console.log("masterState.testApptsProp +++++ :", masterState.testApptsProp); // appts方式,可传动态值
  console.log("testUmircProps +++++ :", testUmircProps); //  umirc方式,只能静态传一次
  const onClickChangeTestApptsProp = () => {
    setMasterState({
      ...masterState,
      testApptsProp: 321,
    });
  };
  return (
    <div>
      <h1 className={styles.title}>App1</h1>
      <div>testApptsProp:{masterState.testApptsProp}</div>
      <div>testUmircProps:{testUmircProps}</div>
      <button onClick={onClickChangeTestApptsProp}>修改testApptsProp</button>
    </div>
  );
}

/app2/src/pages/index.tsx

import styles from "./index.less";
import { useModel } from "umi";

export default function IndexPage() {
  const { masterState, setMasterState, testUmircProps } = useModel(
    "@@qiankunStateFromMaster"
  );

  return (
    <div>
      <h1 className={styles.title}>App2</h1>
      <div>testApptsProp:{masterState.testApptsProp}</div>
    </div>
  );
}

子组件生命周期

/app1/src/app.ts

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);
  },
};

...待续:不带路由的子应用可用 MicroApp 组件装载方式

...待续:本地开发环境不能加载静态资源问题解决

...待续:子应用为 umi4 发现的问题

...待续:多框架(vue3,vue2,react,umi)

参考

实践

科普

问题解决

探究