qiankun 主项目和子项目都是 vue2,部署在同一台服务器上,nginx 配置

1、主项目配置

1.1 micro.vue 组件

复制代码
<template>
  <div id="container-sub-app"></div>
</template>

<script>
import { loadMicroApp } from 'qiankun';
import actions from '@/utils/actions.js';

export default {
  name: 'microApp',
  mixins: [actions],
  data() {
    return {
      microApp: null
    };
  },
  mounted() {
    const getMicroInfo = this.getMicroInfo();
    this.microApp = loadMicroApp(getMicroInfo, {
      singular: true
    });
  },
  beforeDestroy() {
    console.log('beforeDestroy...');
    this.microApp.unmount();
  },
  methods: {
    // 手动加载微应用
    getMicroInfo() {
      const appIdentifying = this.$route.path.split('/')[1];
      let data = {};
      const href = window.location.host;
      for (let i = 0; i < document.subApps.length; i++) {
        const element = document.subApps[i];
        if (element.activeRule.includes(appIdentifying)) {
          if (typeof element.entry !== 'string') {
            data = {
              ...element,
              entry: element.entry[href]
                ? element.entry[href]
                : Object.values(element.entry)[0]
            };
          } else {
            data = { ...element };
          }
          data.props = {
            token: {
              userInfo: {
                userName: '小明',
                userId: '123',
                date: new Date().toLocaleString()
              }
            }
          };
          data.activeRule = [appIdentifying];
          break;
        }
      }
      return data;
    }
  }
};
</script>

1.2 index.html 引入配置

复制代码
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
    />
    <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
    <script src="<%= BASE_URL %>register-apps.js"></script>
    <title><%= webpackConfig.name %></title>
  </head>
  <body>
    <noscript>
      <strong
        >We're sorry but <%= webpackConfig.name %> doesn't work properly without
        JavaScript enabled. Please enable it to continue.</strong
      >
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

1.3 register-apps.js 配置

复制代码
document.subApps = [
  {
    name: 'besFront',
    //entry: '//localhost:8086/bes-front/',// 本地调试
    entry: '/bes-front/',// 部署到服务器
    container: '#container-sub-app',
    activeRule: '/bes-front'
  }
];

1.4 路由配置

复制代码
import Vue from 'vue';
import Router from 'vue-router';

Vue.use(Router);

import Layout from '@/layout';
import MicroApp from '@/components/microApp';

export const constantRoutes = [
  {
    path: '/login',
    component: () => import('@/views/login/index'),
    hidden: true
  },
  {
    path: '/404',
    component: () => import('@/views/404'),
    hidden: true
  },
  {
    path: '/dashboard',
    component: Layout,
    redirect: '/dashboard/index',
    children: [
      {
        path: 'index',
        name: 'Dashboard',
        component: () => import('@/views/dashboard/index'),
        meta: { title: 'Dashboard', icon: 'dashboard' }
      }
    ]
  },
  {
    path: '/',
    component: Layout,
    children: [
      {
        path: 'bes-front',
        component: MicroApp// 重点,用于加载子项目
      }
    ]
  }
  // 404 page must be placed at the end !!!
  // { path: '*', redirect: '/404', hidden: true }
];

const createRouter = () =>
  new Router({
    mode: 'history', // require service support
    base: '',
    scrollBehavior: () => ({ y: 0 }),
    routes: constantRoutes
  });

const router = createRouter();

// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {
  const newRouter = createRouter();
  router.matcher = newRouter.matcher; // reset router
}

export default router;

2、子项目配置

2.1 main.js 配置

复制代码
// 动态设置 publicPath
import './public-path';
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

Vue.config.productionTip = false;
Vue.use(ElementUI);

let instance = null;
function render(props = {}) {
  console.log('子应用 render props::', props, 'instance====', instance);
  // sessionStorage.setItem('userInfo', JSON.stringify(props.token.userInfo));
  const { container } = props;

  instance = new Vue({
    router,
    store,
    render: (h) => h(App)
  }).$mount(container ? container.querySelector('#app') : '#app');
}

// 独立运行时
/* eslint-disable */
if (!window.__POWERED_BY_QIANKUN__) {
  render();
}

export async function bootstrap() {
  console.log('子应用 bootstrap ===========================');
}

let initialState = null;
export async function mount(props) {
  console.log('子应用 mount props ===============', props);
  sessionStorage.setItem('userInfo', JSON.stringify(props.token.userInfo));
  props.onGlobalStateChange((state, prev) => {
    // state: 变更后的状态; prev 变更前的状态
    console.log('子应用获取共享数据 state::', state, 'prev::', prev);
    // 接收主应用中的共享数据 并将其设置为全局变量
    Vue.prototype.$initialState = state;
  });
  props.setGlobalState({
    initialState:
      '子应用中修改主应用中的全局变量,实现住应用子应用间数据的双向双向通信'
  });

  render(props);
}
export async function unmount() {
  console.log('子应用 unmount==========');
  instance.$destroy();
  instance.$el.innerHTML = '';
  instance = null;
}

2.2 public-path 文件

复制代码
if (window.__POWERED_BY_QIANKUN__) {
  /* eslint-disable @typescript-eslint/camelcase */
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

2.3 vue.config.js 配置

复制代码
'use strict';
const path = require('path');
const defaultSettings = require('./src/settings.js');
const proxyTable = require('./proxyTable');

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

const name = defaultSettings.title || 'vue Admin Template'; // page title

// If your port is set to 80,
    // use administrator privileges to execute the command line.
// For example, Mac: sudo npm run
// You can change the port by the following methods:
// port = 9528 npm run dev OR npm run dev --port = 9528
const port = process.env.port || process.env.npm_config_port || 9528; // dev port

// All configuration item explanations can be find in https://cli.vuejs.org/config/
module.exports = {
  /**
   * You will need to set publicPath if you plan to deploy your site under a sub path,
   * for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/,
   * then publicPath should be set to "/bar/".
   * In most cases please use '/' !!!
   * Detail: https://cli.vuejs.org/config/#publicpath
   */
  publicPath: '/bes-front/',
  outputDir: 'dist',
  assetsDir: 'static',
  lintOnSave: process.env.NODE_ENV === 'development',
  productionSourceMap: false,
  devServer: {
    headers:{
        "Access-Control-Allow-Origin": "*",
    },
    port: port,
    open: true,
    proxy: proxyTable,
    overlay: {
      warnings: false,
      errors: true
    }
  },
  configureWebpack: {
    // provide the app's title in webpack's name field, so that
    // it can be accessed in index.html to inject the correct title.
    name: name,
    output:{
        library: `besFront`,// 主应用
        libraryTarget: "umd",// 把微应用打包成 umd 库格式
        jsonpFunction: `webpackJsonp_besFront`,// webpack5 需要把 jsonpFunction 替换成 chunkLoadingGlobal
    },
    resolve: {
      alias: {
        '@': resolve('src')
      }
    }
  },
  chainWebpack(config) {
    // it can improve the speed of the first screen, it is recommended to turn on preload
    config.plugin('preload').tap(() => [
      {
        rel: 'preload',
        // to ignore runtime.js
        // https://github.com/vuejs/vue-cli/blob/dev/packages/%40vue/cli-service/lib/config/app.js#L171
        fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/],
        include: 'initial'
      }
    ]);

    // when there are many pages, it will cause too many meaningless requests
    config.plugins.delete('prefetch');

    // set svg-sprite-loader
    config.module.rule('svg').exclude.add(resolve('src/icons')).end();
    config.module
      .rule('icons')
      .test(/\.svg$/)
      .include.add(resolve('src/icons'))
      .end()
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]'
      })
      .end();

    config.when(process.env.NODE_ENV !== 'development', (config) => {
      config
        .plugin('ScriptExtHtmlWebpackPlugin')
        .after('html')
        .use('script-ext-html-webpack-plugin', [
          {
            // `runtime` must same as runtimeChunk name. default is `runtime`
            inline: /runtime\..*\.js$/
          }
        ])
        .end();
      config.optimization.splitChunks({
        chunks: 'all',
        cacheGroups: {
          libs: {
            name: 'chunk-libs',
            test: /[\\/]node_modules[\\/]/,
            priority: 10,
            chunks: 'initial' // only package third parties that are initially dependent
          },
          elementUI: {
            name: 'chunk-elementUI', // split elementUI into a single package
            priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
            test: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm
          },
          commons: {
            name: 'chunk-commons',
            test: resolve('src/components'), // can customize your rules
            minChunks: 3, //  minimum common number
            priority: 5,
            reuseExistingChunk: true
          }
        }
      });
      // https:// webpack.js.org/configuration/optimization/#optimizationruntimechunk
      config.optimization.runtimeChunk('single');
    });
  }
};

3、主项目和子项目部署在同一台服务器上,其中主应用路由使用history模式,子应用路由使用hash模式,nginx 的配置如下:

复制代码
locatoin / {
    root /opt/parent;
    index index.html index.htm;
    try_files $uri $uri/ /index.html;
}

location /bes-front/ {
    root /opt/parent;
    index index.html index.htm;
    try_files $uri $uri. /index.html;
}

注:使用的下面这种部署方式,主应用代码放在parent目录下,子应用代码放在parent目录下面,其他,子应用的目录应该代码中的名称保持一致,在本示例当中,子应用使用的是bes-front

官网参考链接: 入门教程 - qiankun

4、总结

在部署主应用和子应用时,遇到主应用转发到子应用时,静态资源无法访问的问题,重点是查看子应用publicPath这个配置,这个是静态文件的访问前缀,如果部署到服务器上,子应用的静态资源无法访问到,可以看一下服务器访问的静态资源的前缀是否和代码中配置的一致。

相关推荐
zyl837212 小时前
Docker 使用手册
运维·docker·容器
古月方枘Fry2 小时前
MGRE实验
运维·服务器
stolentime3 小时前
FreeDomain 本地开发环境快速搭建指南
运维·服务器·网络
bush44 小时前
嵌入式linux学习记录四
linux·运维·学习
lihao lihao5 小时前
软硬链接
linux·运维·服务器
TOWE technology5 小时前
智能安防监控系统如何做好防雷?——视频信号SPD综合应用方案解析
运维·服务器·防雷产品·信号保护·信号防雷·spd
楼田莉子5 小时前
Docker学习:Docker介绍及其架构介绍
运维·后端·学习·docker·容器·架构
fliter6 小时前
从零开始,自己造一个可执行文件压缩器
github
大明者省6 小时前
IIS 端口绑定正常访问的原理说明与常见误区澄清
运维·服务器·笔记
UTF_86 小时前
一次NSMutableAttributedString误用的思考
ios·面试·github