qiankun从接入到部署(nuxt篇)

2,270 阅读2分钟

目录

  1. 接入nuxt项目作为主应用
    1. 在哪接入qiankun
    2. 在哪注册子路由
    3. 不同layouts怎么处理
    4. 会不会出现404
  2. 子应用为vue3(与框架无关,可以使用vue2/react)
    1. 子路由设计
    2. 哪些地方需要改
  3. nginx部署
    1. 文件夹示例
    2. nginx配置
    3. 怎么发布
  4. 其它

nuxt项目接入qiankun

  1. 安装:npm i qiankun -S
  2. nuxt.config.js中的plugins配置项中
export default {
    // https://nuxtjs.org/docs/configuration-glossary/configuration-ssr
    ssr: false, // 禁用服务端渲染 其它版本可能使用的是(mode: spa)
    // https://nuxtjs.org/docs/configuration-glossary/configuration-target/
    target: 'server',
    plugins: [
      { src: '~/plugins/qiankun.js', ssr: false },
      '~/plugins/element-ui'
    ]
}
  1. plugins/qiankun.js 动态获取子应用
export default async ({ store }) => {
  await store.dispatch('getMenus')
}
  1. store/index.js 需修改

模拟一个返回子应用的接口

async function getMenus() {
  return {
    code: 20000,
    payload: [
      {
        name: 'custom-app',
        container: '#custom-app',
        activeRule: '/apps/custom/',
        entry: `//${location.hostname}:4001/apps/custom/` // 解决不同服务器和域名
      }
    ],
    message: 'success'
  }
}

actions中加入

async getMenus({ commit }) {
  const { payload } = await getMenus()
  commit(INIT_APPS, payload)
}

state中加入

apps: [],
name: 'main',
sdk: null,
token: null,
userInfo: null,

mutations中加入

INIT_APPS(state, apps) {
  // 初始化全局变量
  const actions = initGlobalState({
    name: state.name,
    token: state.token,
    userInfo: state.userInfo
  })

  // 使用 sdk 方式进行 父子应用通信, 这里大家可以根据自己项目进行增加删除
  const sdk = {
    globalState: actions,
    name: state.name
  }

  // 处理 apps 列表
  apps = apps.map((item) => ({
    ...item,
    container: '#custom-app',
    props: {
      sdk
    }
  }))

  // 处理路由表
  const routes = apps.map((item, i) => ({
    path: `${item.activeRule}(.*)`,
    name: `${item.name}-${i}`,
    component: () =>import('@/pages/subapp.vue').then(m => m.default || m)
  }))

  // 动态增加路由, 这里要注意 404 页面不能直接写在 pages 中
  // 不然匹配的时候会根据顺序匹配到 * 就直接返回了 从而匹配不到我们后续添加的动态路由
  this.$router.addRoutes([].concat(...routes,
    {
      path: `*`,
      name: `404`,
      component: () => import('@/pages/404.vue').then(m => m.default || m)
    }
  ))

  state.apps = apps
  state.sdk = sdk
}
  1. layouts/default.vuelayouts/preview.vue中注册子路由 若是使用默认的default.vue会省很多麻烦;如果使用多个layout则每个都需要添加以下代码。
<div id="custom-app"></div>
<Nuxt />

mounted中渲染注册应用

import { registerMicroApps, start } from "qiankun";
export default {
    mounted() {
      this.init()
    },
    methods: {
      async init() {
        registerMicroApps(this.apps)
        start()
      }
    }
}
  1. pages/subapp.vue 容器
<template>
</template>
<script>
export default {
  layout: 'preview' // 此处 根据子应用加载到不同场景决定,我的项目是不同的布局所以此处使用自定义布局
}
</script>

不同layouts怎么处理

处理路由表处根据不同场景指向不同layout

子应用

子应用设计

nuxt需要避免路由冲突,不要使用apps文件夹

stateDiagram-v2
主应用(/) --> 子路由(/apps/custom)
主应用(/) --> 子路由(/apps/score)
主应用(/) --> 子路由(/apps/product)

哪些地方需要改

  1. src/main.js
  2. src/public-path.js
  3. vue.config.js
  4. src/router/index.js

src/main.js

import './public-path';
import { createApp } from 'vue';
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import { createRouter, createWebHistory } from 'vue-router';
import App from './App.vue';
import routes from './router';
import store from './store';

import '@/assets/css/rest.css'

let router = null;
let instance = null;
let history = null;


function render(props = {}) {
  const { container, routerBase } = props
  const base = window.__POWERED_BY_QIANKUN__ ? '/apps/custom' : '/apps/custom'
  history = createWebHistory(base);
  router = createRouter({
    history,
    routes
  });

  instance = createApp(App);
  instance.use(ElementPlus);
  instance.use(router);
  instance.use(store);
  instance.mount(container ? container.querySelector('#app') : '#app');
}

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

export async function bootstrap() {
  console.log('%c%s', 'color: green;', 'vue3.0 app bootstraped');
}


function handleStore(props) {
  const { sdk } = props;
  store.$sdk = sdk

  store.commit('update', {
    name: sdk.name
  })

  if (sdk) {
    sdk.globalState.onGlobalStateChange((ctx) => {
      console.log('子应用 监听 global state', ctx);
    })
  }
}

export async function mount(props) {
  handleStore(props);
  render(props);
  instance.config.globalProperties.$onGlobalStateChange = props.onGlobalStateChange;
  instance.config.globalProperties.$setGlobalState = props.setGlobalState;
}

export async function unmount() {
  instance.unmount();
  instance._container.innerHTML = '';
  instance = null;
  router = null;
  history.destroy();
}

src/public-path.js

if (window.__POWERED_BY_QIANKUN__) {
  // eslint-disable-next-line no-undef
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
  console.log('__webpack_public_path__', __webpack_public_path__)
}

vue.config.js

const path = require('path');
const { name } = require('./package');

function resolve(dir) {
  return path.join(__dirname, dir);
}

const port = 4001;

module.exports = {
  outputDir: 'dist',
  assetsDir: 'static',
  publicPath: '/apps/custom',
  filenameHashing: true,
  devServer: {
    hot: true,
    disableHostCheck: true,
    port,
    overlay: {
      warnings: false,
      errors: true,
    },
    proxy:{
      '/center-api': {
        target: 'http://192.168.1.129:4000/',
        changeOrigin: true,
        ws: false,
      }
    },
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
  },
  // 自定义webpack配置
  configureWebpack: {
    resolve: {
      alias: {
        '@': resolve('src'),
      },
    },
    output: {
      // 把子应用打包成 umd 库格式
      library: `${name}-[name]`,
      libraryTarget: 'umd',
      jsonpFunction: `webpackJsonp_${name}`,
    },
  },
};

src/router/index.js

const routes = [
  { path: '/', name: 'home', component: () => import(/* webpackChunkName: "home" */ '../views/Home') },
  { path: '/homeMap', name: 'HomeMap', component: () => import(/* webpackChunkName: "about" */ '../views/hunanDemo/homeMap') },
];

export default routes

nginx文件夹示例

stateDiagram-v2
nginx(html) --> micro
micro --> main(基座应用)
micro --> apps(子应用)

apps(子应用) --> custom
apps(子应用) --> score
apps(子应用) --> prodcut

nginx配置

主应用

server {
    listen       4000;
    server_name  localhost;

    location /center-api/ {
        proxy_pass http://gateways/;
    }
    location / {
        root   /usr/local/nginx/html/micro/main;
        index  index.html;
        try_files $uri $uri/ /index.html;
    }


    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   html;
    }
}

子应用

server {
    listen       4001;
    server_name  localhost;
    add_header 'Access-Control-Allow-Origin' '*';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';

    location /center-api/ {
      proxy_pass http://gateways/;
    }

    location /apps/custom {
      index index.html;
      try_files $uri $uri/ /custom/index.html;
      alias /usr/local/nginx/html/micro/apps/custom;
    }
    location /apps/score {
      index index.html;
      try_files $uri $uri/ /score/index.html;
      alias /usr/local/nginx/html/micro/apps/score;
    }
}

怎么发布

打包成静态文件发布

其它

参考文章:

  1. juejin.cn/post/697315… nginx配置细节处不同
  2. github.com/fengxianqi/… 注册应用那一块相同