qiankun 踩坑记录

1 乾坤框架的css沙箱只能保证子应用之间的样式不相互影响,所以开发过程中主应用应该给样式添加限制

2 无法在子应用全局变量中保存props

微应用接入指南

主应用

Main.js代码

import { createApp } from 'vue'

import App from './App.vue'

import Router from './router'

import { registerMicroApps, start, initGlobalState, setDefaultMountApp } from 'qiankun'

let apps = [

    {

        name: 'app1',

        entry:

            process.env.NODE_ENV === "development" ? '//localhost:8082/app1/' : '/app1',

        container: '#container',

        activeRule: '#/app1',

        beforeLoad: () => { },

        beforeMount: () => { },

        afterMount: () => { },

        beforeUnmount: () => { },

        afterUnmount: () => { }

    }, {

        name: 'app2',

        entry:

            process.env.NODE_ENV === "development" ? '//localhost:8081/app2/' : '/app2',

        container: '#container',

        activeRule: '#/app2',

        beforeLoad: () => { },

        beforeMount: () => { },

        afterMount: () => { },

        beforeUnmount: () => { },

        afterUnmount: () => { }

    },

];



let initQiankun = (vue) => {

    window.__POWERED_BY_QIANKUN_PARENT__ = true

    //注册微应用

    registerMicroApps(apps);

    setDefaultMountApp('#/app1')

    start();

    //初始化传给微应用的数据

    let state = {

        userId: '1232q345123',

        name: 'LiLin',

        pageName: '',

        isLogin: true

    };

    const actions = initGlobalState(state);



    // 子应用修改了state会调用此方法  state: 变更后的状态; prev 变更前的状态

    actions.onGlobalStateChange((state, prev) => {

        console.log('子应用修改了state', state, prev);

        if (!state.isLogin) {

            //TODO 重新登录

        }

    });

    actions.setGlobalState(state);

    actions.offGlobalStateChange();

    vue.config.globalProperties.$actions = actions;

}



Router.beforeEach((to, from, next) => {

    next()

})

let app = createApp(App).use(Router);

app.mount('#qiankun');

initQiankun(app);



//处理浏览器前进后退

(() => {

    window.addEventListener("popstate", function () {



        let hashHistory = sessionStorage.getItem('hashHistory');

        if (!hashHistory) hashHistory = [];

        else

            hashHistory = JSON.parse(hashHistory)



        let current = window.location.href.split('#')[1];

        if (current == 'undefined') {

            Router.replace('/')

            return;

        }

        //如果不是微应用的路由地址,不做操作

        let length = apps.filter(item => {

            return current.indexOf(item.name) == 1;

        }).length;

        if (length == 0) return;



        //如果还没有缓存,放入数组中缓存起来

        if (hashHistory.indexOf(current) == -1)

            hashHistory.push(current);

        sessionStorage.setItem('hashHistory', JSON.stringify(hashHistory));

        ///  console.log(11111, hashHistory)

    }, false)



    window.addEventListener("hashchange", function (e) {

        // console.log('hashchange')

        let hashHistory = sessionStorage.getItem('hashHistory');

        if (!hashHistory) return

        hashHistory = JSON.parse(hashHistory)

        let current = e.newURL.split('#')[1] || '';

        //如果是缓存记录中的路由地址,不做操作

        let arr = hashHistory.filter(item => {

            return item == current;

        })

        if (arr.length > 0) return;



        //查找出包含此路由地址的缓存路由,最后替换路由地址

        let temp = hashHistory.filter((item) => {

            return item.indexOf(current) != -1;

        })

        Router.replace(temp[0])

    }, false);

})()


 

Vue2脚手架项目接入

1在src目录下创建文件public-path.js,内容如下

if (window.__POWERED_BY_QIANKUN__) {

    __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;

}

                         

2修改main.js文件

import './public-path';

import Vue from 'vue';

import App from './App.vue';

import router from './router/index';

import store from './store';



import ElementUI from 'element-ui';

import 'element-ui/lib/theme-chalk/index.css';



import request from '@/utils/request/request'

import Security from '@/utils/request/EncryptionTools/index'

import settings from '@/utils/request/settings'





Vue.use(Security.Security)

Vue.use(ElementUI);

Vue.prototype.request = request;



let instance = null;

function render (props = {}) {

    const { container } = props;



    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, props.setGlobalState);

    });

    render(props);

}

export async function unmount () {

    instance.$destroy();

    instance.$el.innerHTML = '';

}

主要修改:

  1. 引入public-path.js
  2. 声明并导出方法bootstrap、mount、unmount 三个方法
  3. 根据加载方式不同将生成的html节点绑定到不同的根节点上

3.修改vue.config.js配置文件

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

const publicPath = process.env.NODE_ENV === 'production' ?

    'http://localhost:82/app2' : ``;



module.exports = {

    publicPath: '/app2/',



    devServer: {

        headers: {

            'Access-Control-Allow-Origin': '*',

        },

    },

    configureWebpack: {

        output: {

            library: `${name}-[name]`,

            libraryTarget: 'umd', // 把微应用打包成 umd 库格式

            jsonpFunction: `webpackJsonp_${name}`,

        },

    },

    chainWebpack: (config) => {

        const fontRule = config.module.rule('fonts');

        fontRule.uses.clear();

        fontRule

            .use('file-loader')

            .loader('file-loader')

            .options({

                name: 'fonts/[name].[hash:8].[ext]',

                publicPath,

            })

            .end();

        const imgRule = config.module.rule('images');

        imgRule.uses.clear();

        imgRule

            .use('file-loader')

            .loader('file-loader')

            .options({

                name: 'img/[name].[hash:8].[ext]',

                publicPath,

            })

            .end();

    },

};

说明:

  1. 地址http://localhost:82/app2为该应用部署后的访问地址,主要是为了将资源文件中的相对地址替换成绝对地址
  2. app2这个名称必须是唯一的,为了解决加载微应用时的跨域问题
  3. 设置开发模式下允许跨域访问,方便开发环境调试

4.修改router.js

       import Vue from 'vue';

import VueRouter from 'vue-router';

import Home from '../views/Home.vue';



Vue.use(VueRouter);



const routes = [

    {

        path: '',

        name: 'Home',

        component: Home

    },

    {

        path: '/',

        name: 'Home',

        component: Home

    },

];



if (window.__POWERED_BY_QIANKUN__) {

    let prefix = '/app2' // 为主应用的activeRule

    routes.map(item => {

        item.path = prefix + item.path;

    });

}



const router = new VueRouter({

    mode: 'hash',

    routes

});



if (window.__POWERED_BY_QIANKUN__) {

    router.beforeEach((to, from, next) => {

        if (!to.path.includes('/app2')) { // /workbench为主应用的activeRule

            next({ path: prefix + to.path })

        } else { next() }

    })

}



export default router;

Vue3脚手架项目接入

1在src目录下创建文件public-path.js,内容如下

if (window.__POWERED_BY_QIANKUN__) {

    __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;

}

                         

2修改main.js文件/* eslint-disable */


/* eslint-disable */

// @ts-nocheck

import './public-path.ts'

import { createApp } from 'vue'

import App from './App.vue'

import router from './router'

/**

* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法

*/

let app;

function render (props = {}) {

    const { container } = props;



    app = createApp(App);

    app.config.globalProperties.$props = props;

    app.use(router).mount(container ? container.querySelector('#app') : '#app')

    //app.config.globalProperties.$props 

    props.setGlobalState && props.setGlobalState({ pageName: '123' })

}

// 独立运行时,直接挂载应用

if (!window.__POWERED_BY_QIANKUN__) {

    render();

}

/**

* bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。

* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。

*/

export async function bootstrap () {

    console.log("VueMicroApp bootstraped");

}

/**

* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法

*/

export async function mount (props) {

    props.onGlobalStateChange((state, prev) => {

        // state: 变更后的状态; prev 变更前的状态

        console.log(state, prev, props.setGlobalState);

    });

    render(props);

}



/**

* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例

*/

export async function unmount () {

    console.log("VueMicroApp unmount");

}

主要修改:

  1. 引入public-path.js
  2. 声明并导出方法bootstrap、mount、unmount 三个方法
  3. 根据加载方式不同将生成的html节点绑定到不同的根节点上

3.修改vue.config.js配置文件

const { ReplacePath } = require('./lib/replace-path.js')

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



const path = require('path')

const publicPath = process.env.NODE_ENV === 'production' ? 'http://localhost:82/app1/' : `/app1/`;

module.exports = {

    publicPath,

    devServer: {

        headers: {

            //允许跨域

            'Access-Control-Allow-Origin': '*',

        }

    },

    configureWebpack: {

        output: {

            // 把子应用打包成 umd 库格式

            library: `${name}-[name]`,

            libraryTarget: 'umd',

            chunkLoadingGlobal: `webpackJsonp_${name}`,

        },

        plugins: [

            new ReplacePath({})

        ]

    },

}

ReplacPath插件代码

/** 自定义的插件: 替换一个全局变量 */

const fs = require('fs')

const path = require('path')

class ReplacePath {

    apply (compiler) {

        if (process.env.NODE_ENV === 'development')

            return;

        compiler.hooks.afterEmit.tap("ReplacePath", (compilation) => {



            compilation.chunks.forEach(chunk => {

                let pathHeader = path.resolve(__dirname.replace('lib', ''), 'dist');

                chunk.files.map(fileName => {

                    if (!/.js$/.test(fileName)) return;

                    let source = fs.readFileSync(path.resolve(pathHeader, fileName), { encoding: 'utf-8' });

                    source = source.replace(/window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__/, `"${compilation.options.output.publicPath}"`);

                    fs.writeFileSync(path.resolve(pathHeader, fileName), source, { encoding: 'utf-8' });

                })

            });



        });

    }

}



module.exports = { ReplacePath };


 

  1. app1这个名称必须是唯一的,为了解决加载微应用时的跨域问题
  2. 设置开发模式下允许跨域访问,方便开发环境调试

4.修改router.js

import { createRouter, createWebHashHistory } from 'vue-router'

import TestText from '../views/test/test-text.vue'

import TestText2 from '../views/test2/test-text.vue'

import TestText3 from '../views/test3/test-text.vue'

let routes = [

    {

        path: '/',

        component: TestText

    },

    {

        path: '/page2',

        component: TestText2

    },

    {

        path: '/page3',

        component: TestText3

    },



];

const router = createRouter({ routes, history: createWebHashHistory(window.__POWERED_BY_QIANKUN__ ? '#/app1' : '') });

export default router;

Nginx部署

Nginx配置如下

worker_processes  2;

events {

    worker_connections  1024;

}


http {

   proxy_cache_path /tmp/nginxcache levels=1:2 keys_zone=app:10m max_size=10g inactive=1m use_temp_path=off;


    include       mime.types;

    default_type  application/octet-stream;

    keepalive_timeout  65;

  server{

   listen 81;

   server_name localhost;

#  gzip on;

#  gzip_min_length 1k;

#  gzip_buffers 4 16k;

  

   location  / {

      root  html/main/;  #你的项目的根目录

      index  index.html index.htm;

      autoindex on;

   }

  
   location  /app1/ {

      proxy_pass http://localhost:82/app1/;

      proxy_set_header Host $host:$server_port;

       proxy_cache app;  #开启缓存 off关闭缓存
     

      proxy_cache_valid 200 304 12h;  #表示对200 304头信息12小时过期


      proxy_cache_valid any 10m; #表示除200 304头信息10钟过期

     
      add_header Nginx-Cache "$upstream_cache_status"; 

   }
     

   location  /app2/ {

      proxy_pass http://localhost:82/app2/;

      proxy_set_header Host $host:$server_port;

       proxy_cache app;  #开启缓存 off关闭缓存

     

      proxy_cache_valid 200 304 12h;  #表示对200 304头信息12小时过期

           proxy_cache_valid any 10m; #表示除200 304头信息10钟过期
    

      add_header Nginx-Cache "$upstream_cache_status"; 

   }

   access_log  logs/k8s.log;

   error_log  logs/zhaoxin_error.log;

   charset     utf-8;   # 编码

    error_page   500 502 503 504  /50x.html;

       

  }

 
  server{

   listen 82;

     gzip on;

   gzip_min_length 1k;

   gzip_buffers 4 16k;

  

   #允许跨域请求的域,*代表所有

add_header 'Access-Control-Allow-Origin' *;

#允许带上cookie请求

add_header 'Access-Control-Allow-Credentials' 'true';

#允许请求的方法,比如 GET/POST/PUT/DELETE

add_header 'Access-Control-Allow-Methods' *;

#允许请求的header

add_header 'Access-Control-Allow-Headers' *;


   location  / {

      root  html/;  #你的项目的根目录

      index  index.html index.htm;

   }  

  }


}

​​​​​​​

  1. 基座部署的时候需要设置代理,并开启缓存(否则加载会很慢)
  2. 微应用部署的时候需要设置允许跨域访问,可以限制下资源类型,主要是当加载微应用的时候一些图片字体等资源需要跨域访问
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值