小白qiankun试用

957 阅读3分钟

一、搭建项目基础结构

  • 创建文件夹,在文件夹下新建main、react、vue、node四个文件夹
  • 使用create @umijs/umi-app创建main和react下的react项目
  • 使用vue create vue创建vue项目

二、引入qiankun

  • main和react下引入 @umijs/plugin-qiankun,main下引入qiankun

三、配置qiankun

1.main配置

  • 我们参考 umijs.org/zh-CN/plugi… 在主项目main的.umirc.ts 或 config/config.ts文件下修改,添加代码如下
routes: [
    {
      path: '/', component: '@/pages/index',
      routes: [
        {
          path: '/sub-react',
          microApp: 'react',
        },
        {
          path: '/sub-vue',
          microApp: 'vue',
        },
      ]
    },
  ],
  qiankun: {
    master: {
      // 注册子应用信息
      apps: [
        {
          name: 'react', // 唯一 id
          // entry: '//localhost:8080/react/', // 打包后配置
          entry: '//localhost:7001/', // html entry
        },
        {
          name: 'vue', // 唯一 id
          // entry: '//localhost:8080/vue/', // 打包后配置
          entry: '//localhost:7002/', // html entry
        },
      ],
    },
  },
  outputPath: '../node/webapp',

在index中添加路由跳转代码,可以参考如下

<Button onClick={() => history.push('/sub-react')}>react</Button>
          <Button onClick={() => history.push('/sub-vue')} style={{marginLeft: 12}}>vue</Button>
          <div>{children}</div>

2.react配置

  • 在react目录下的.umirc.ts 或 config/config.ts文件下修改
qiankun: {
    slave: {}
  },
  publicPath: process.env.NODE_ENV === 'production' ? '/react/' : '/',
  base: '/react',
  outputPath: '../node/public/react',

src下新建app.ts文件,添加子应用启动生命周期

export const qiankun = {
  // 应用加载之前
  async bootstrap(props: any) {
    console.log('app1 bootstrap', props);
  },
  // 应用 render 之前触发
  async mount(props: any) {
    console.log('app1 mount', props);
    // props.onGlobalStateChange((state, prev) => {
      // state: 变更后的状态; prev 变更前的状态
     //  console.log(state, prev);
   //  });
    // props.setGlobalState(state);
  },
  // 应用卸载之后触发
  async unmount(props: any) {
    console.log('app1 unmount', props);
  },
};

react根目录下新建.env文件配置PORT=7001

3.vue配置

  • 在vue中新建.env文件配置VUE_APP_PORT=7002 新建vue.config.js文件,配置
const { name } = require('./package.json')
module.exports = {
    configureWebpack: {
        output: {
            library: `${name}-[name]`,
            libraryTarget: 'umd',
            jsonpFunction: `webpackJsonp_${name}`,
        }
    },
    devServer: {
        port: process.env.VUE_APP_PORT,
        headers: {
            'Access-Control-Allow-Origin': '*' // 主应用获取子应用时跨域响应头
        }
    },
    publicPath: process.env.NODE_ENV === 'production' ? '/vue/' : '/',
    outputDir: '../node/public/vue',
    assetsDir: 'static',
}

src文件夹下新增public-path.js文件,添加代码 主要是配置webpack的__webpack_public_path__参数

(function() {
    console.log('----->', window.__POWERED_BY_QIANKUN__, process.env.VUE_APP_PORT);
    if (window.__POWERED_BY_QIANKUN__) {
        if (process.env.NODE_ENV === 'development') {
            // eslint-disable-next-line no-undef
            __webpack_public_path__ = `//localhost:${process.env.VUE_APP_PORT}/`;
            return;
        }
        // eslint-disable-next-line no-undef
        __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
    }
})();

router文件里改为导出routes,目的是为了在main.js下为VueRouter添加base等其它属性

修改main.js文件,改为

import Vue from 'vue'
import App from './App.vue'
import routes from './router'
import store from './store'
import './public-path'
import VueRouter from 'vue-router'

Vue.config.productionTip = false
let instance = null

function render (props = {}) {
  const { container, base } = props
  console.log(base);
  console.log(window.__POWERED_BY_QIANKUN__, props, process.env.BASE_URL);
  const router = new VueRouter({
    base: window.__POWERED_BY_QIANKUN__ ? base : process.env.BASE_URL,
    mode: 'history',
    routes,
  })
  // if (window.__POWERED_BY_QIANKUN__) {
  //   router.beforeEach((to, from, next) => {
  //     if (to.path.includes('/sub-vue')) {
  //       next({
  //         path: '/sub-vue/' + to.path
  //       })
  //     } else {
  //       next()
  //     }
  //   })
  // }
  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] vue app bootstraped')
}

export async function mount (props) {
  console.log('[vue] props from main framework', props)
  // props.onGlobalStateChange((state, prev) => {
    // state: 变更后的状态; prev 变更前的状态
    // console.log('阿水大把大把', state, prev);
  // });
  render(props)
}

export async function unmount () {
  instance.$destroy()
  instance.$el.innerHTML = ''
  instance = null
}

4.node配置

添加package.json文件,其中包含的包, express和koa选择想要的

"scripts": {
    "start": "nodemon app.js",
  },
  "dependencies": {
    "connect-history-api-fallback": "^1.6.0", // express
    "express": "^4.17.1", // express
    "koa": "^2.7.0", // koa2
    "koa-static": "^5.0.0", // koa2
    "koa2-connect-history-api-fallback": "^0.1.3" // koa2
  },
  "devDependencies": {
    "nodemon": "^1.19.1"
  }

新建app.js文件,文件内容如下

const Koa = require('koa');
const { historyApiFallback } = require('koa2-connect-history-api-fallback');
const app = new Koa();
const Static = require('koa-static');

const port = 8080;

app.use(historyApiFallback({
    htmlAcceptHeaders: ['index/html', 'application/xhtml+xml']
}));
// 托管静态资源
app.use(Static(__dirname + '/public'))
app.use(Static(__dirname + '/webapp'))

app.listen(port, () => {
  console.log(`服务已启动监听端口:${port}`)
}).on('error', (err) => {
  throw err
});


// 下面是express的
// const express = require('express');
// const app = express();
// const history = require('connect-history-api-fallback');
//
// const port = 8080;
// app.use(history({
//     htmlAcceptHeaders: ['index/html', 'application/xhtml+xml']
// }));
// app.use(express.static(__dirname + '/public'))
// app.use(express.static(__dirname + '/webapp'))
//
// app.listen(port, () => {
//     console.log(`服务已启动监听端口:${port}`)
// })

5.测试、打包部署

在main、react、vue下执行npm start查看效果、访问localhost:8000localhost:7001localhost:7002 在main、react、vue下执行npm run build,启动node,在node下执行npm start 访问http://localhost:8080/查看效果

6.hash配置

umi搭建的请设置 history: {type: "hash"}, vue在创建VueRouter时设置mode: 'hash'

四、数据传递

在mian/src下新建app.ts,代码入下

import { initGlobalState } from 'qiankun';

const state = {
  userId: 'userId-123',
  userName: 'userName-123',
}

// 初始化 state
const actions = initGlobalState(state);

export async function getInitialState() {
  return actions;
}

页面上设置状态

import {useModel} from "umi";
const { initialState } = useModel('@@initialState');

// 设置
initialState.setGlobalState({
                userId: 'userId-321',
                userName: '新值',
              });
              initialState.offGlobalStateChange();

子应用可以在生命周期中来监听状态值变化时、或者设置状态值

async mount(props: any) {
    console.log('app1 mount', props);
    props.onGlobalStateChange((state, prev) => {
      // state: 变更后的状态; prev 变更前的状态
      console.log(state, prev);
    });
    // props.setGlobalState(state);
  },

更多api请参考官方文档 qiankun.umijs.org/zh/api/#sta…