微前端框架乾坤配置记录

主应用

1. 路由包装为history模式

// src/main.js
import routes from './routes';
const router = new VueRouter({
  mode: 'history',
  routes
});

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

2. 引入乾坤微前端主应用配置

// src/mico/index.js

import { registerMicroApps, addGlobalUncaughtErrorHandler, start } from 'qiankun';

import apps from './apps';

registerMicroApps(apps,
{
  beforeLoad: [
    app => {
      console.log('[LifeCycle] before load %c%s', 'color: green;', app.name);
    },
  ],
  beforeMount: [
    app => {
      console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name);
    },
  ],
  afterUnmount: [
    app => {
      console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name);
    },
  ],
},);

addGlobalUncaughtErrorHandler((event) => {
  console.log(event);
  const { msg } = event;
  if (msg && msg.includes('died in status LOADING_SOURCE_CODE')) {
    console.log('微应用加载失败,请检查应用是否可运行');
  }
});

export default start;

3. 配置子应用列表

// /src/micfo/apps.js

const apps = [
    {
        name: 'planResource',
        entry: '//localhost:8083',
        container: '#iframe',
        activeRule: '/plan'
    },
    {
        name: 'configration',
        entry: '//localhost:8081',
        container: '#iframe',
        activeRule: '/configure'
    }
];

export default apps;

4. 主应用实例化后启动微应用监听

// /src/main.js
import startQiankui from './micro';

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

startQiankui();

子应用

1. Vue应用接入

1.1 修改webpack配置

// webpack.config.js || vue.config.js

devServer: 
	port: 8083 // 端口要写死,因为主应用中配置子应用列表时用的是写死的端口配置
	disableHostCheck: false // 关闭主机检查,保证子应用可以被主应用fetch到
	headers: {
		'Access-Control-Allow-Origin': '*', // 配置跨域请求头,解决开发环境跨域问题
	}
	publicPath: process.env.NODE_ENV === 'production' ? '/newplanning/' : '//localhost:8083',  // 开发环境设置为当前固定访问地址
}
configureWebpack: {
	 output: { 
            library: 'planResource', // 名称要和主应用中配置的子应用列表中的name字段相同
            libraryTarget: 'umd', // 把子应用打包成 umd 库格式
            jsonpFunction: `webpackJsonp_planResource` //  此处后缀也要和名称保持一致
        }
}
1.2 增加 public-path配置文件并在入口文件引入
//  /src/micro/public-path.js
if (window.__POWERED_BY_QIANKUN__) {
    // eslint-disable-next-line no-undef
    __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

//  /src/main.js

import '@micro/public-path'
1.3 路由实例化配置
// src/router/index.js
import Vue from 'vue';
import Router from 'vue-router';
import routes from './routers.js';

Vue.use(Router);

export default new Router({
    base: window.__POWERED_BY_QIANKUN__ ? '/plan' : '/', // 如果作为乾坤的子应用则使用主应用中配置的路由前缀,如果是独立运行则使用根目录
    mode: 'hash', 
    routes // 路由定义数组
});

1.4 Vue实例化包装和生命周期钩子导出

//  /src/main.js
import './public-path';
import router from './router';
let instance = null;

// 重新包装render方法
function render(props = {}) {
    const { container } = props;
    const renderContainer = container ? container.querySelector('#app') : '#app'; // 如果是作为子应用渲染,则渲染到主应用中配置的DOM节点中

    instance = new Vue({
        router,
        store,
        render: h => h(App)
    }).$mount(renderContainer);
}

// 独立运行时直接渲染
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);
    render(props); // 作为子应用渲染时将主应用传入的配置作为参数去渲染
}

export async function unmount() {
    instance.$destroy();
    instance.$el.innerHTML = '';
    instance = null;
    router = null;
}
1.5 将代理信息复制到主应用

2. angularjs 应用接入

2.1 引入 HTML entry
// index.html
<body>
  <!-- ... >
	<script src="//localhost:8080/lib/micro/entry.js" entry></script>
</body>
2.2 入口配置
// /src/lib/micro/entry.js

((global) => {
    global['myAngularApp'] = {
        bootstrap: () => {
            console.log('myAngularApp bootstrap');
            return Promise.resolve();
        },
        mount: (props) => {
            console.log('myAngularApp mount', props);
            return render(props);
        },
        unmount: () => {
            console.log('myAngularApp unmount');
            return Promise.resolve();
        },
    };
})(window);

/**
 * [render 渲染函数]
 *
 * @param   {[type]}  props  [props description]
 *
 * @return  {[type]}         [return description]
 */
function render(props = {}) {
    if (props && props.container) {
        console.log('乾坤渲染');
        window.myActions = {
            onGlobalStateChange: function(...args) {
                props.onGlobalStateChange(...args);
            },
            setGlobalState: function(...args) {
                props.setGlobalState(...args);
            }
        };
        window.myActions.onGlobalStateChange(state => {
            const { token } = state;
            console.log(token);
            // 未登录 - 返回主页
            if (!token) {
                console.log('未检测到登录信息!');
                window.location.href = '/';
            }
            localStorage.setItem('myAngularApp-token', token);
        }, true);
    } else {
        angular.element(document).ready(function () {
            angular.bootstrap(document, ['app']);
            return Promise.resolve();
        });
    }
}

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

2.3 路由前缀

需要注意的是,对于angularjs,路由一定要改造为统一的前缀,这样子应用不同路由之间的跳转才好被主路由统一获取到。

2.4 模板路径问题

在angularjs中,我们常常会写templateUrl属性,大多数时候我们会写模板的相对地址,但是这样就造成在主应用中对应的相对路径位置是找不到模板文件的,所以路径需要加一个统一的前缀参数,根据是否运行在主应用内来设定不同前缀。

通信

1.主应用

1.1 初始化全局跨应用通信方法
// /src/mico/actions

import { initGlobalState } from 'qiankun';
const initState = {};

const actions = initGlobalState(initState);
  
export default actions;
1.2 在需要设置跨应用全局状态的组件内:
<script>
import store from '@/store'
import httpService from '@/core/httpService'
import actions from '@/micro/actions'

export default {
  name: 'App',
  created() {
    if (!store.getters.token) {
        httpService.getToken().then(data => {
            store.commit('SET_TOKEN', data.data.token);
            actions.setGlobalState({ token: data.data.token });
        });
    }
  }
}
</script>

2. 子应用

2.1 全局actions定义
//  /src/micro/actions.js

function emptyAction() {
    // 警告:提示当前使用的是空 Action
    console.warn('Current execute action is empty!');
  }
  class Actions {
    // 默认值为空 Action
    actions = {
      onGlobalStateChange: emptyAction,
      setGlobalState: emptyAction
    };
    /**
     * 设置 actions
     */
    setActions(actions) {
      this.actions = actions;
    }
    /**
     * 映射
     */
    onGlobalStateChange(...args) {
      return this.actions.onGlobalStateChange(...args);
    }
    /**
     * 映射
     */
    setGlobalState(...args) {
      return this.actions.setGlobalState(...args);
    }
  }
  const actions = new Actions();
  export default actions;

2.2 在应用渲染前获取从主应用上的通信方法并注入到actions里
// /src/main.js

import actions from '@/micro/actions';

function render(props = {}) {
    if (props) {
        actions.setActions(props);
        actions.onGlobalStateChange(state => {
            const { token } = state;
            console.log(token);
            // 未登录 - 返回主页
            if (!token) {
              this.$message.error('未检测到登录信息!');
              return this.$router.push('/');
            }
            store.commit('SET_TOKEN', token);
          }, true);
    }
    const { container } = props;
    const renderContainer = container ? container.querySelector('#app') : '#app';

    instance = new Vue({
        router,
        store,
        render: h => h(App)
    }).$mount(renderContainer);
}

export async function mount(props) {
    console.log('[vue] props from main framework', props);
    // 注册应用间通信
    render(props);
}

  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值