qiankun-问题记录

536 阅读4分钟

qiankun记录

记录乾坤的问题

时间: 2023-2-1

拆分基座、子应用后当子应用需要使用基座中的store如何触发基座的onGlobalStateChange方法;

问题:由于基座和子应用使用的起初是同一个store仓库,有共同属性。子应用需要实时监听并且同步基座的数据变化。且store必须使用mutation和action改变state的值,会触发保护机制,无法直接进行覆盖;否则vuex会报错;

解决办法:

1.直接全局在App.vue中添加watch "$store.state" 监听,打开deep的深度监听,直接触发setGlobalState({store: newStore}),在子应用中就可以通过onGlobalStateChange()方法监听到;

export async function mount(props) {
 props.onGlobalStateChange((state, prev) => {
        //此处进行遍历,给store的state赋值
        Object.keys(state.store).forEach(v => {
            store.state[v] = state.store[v];
        })
    }, true);
  
    Vue.prototype.$onGlobalStateChange = props.onGlobalStateChange;
    Vue.prototype.$setGlobalState = props.setGlobalState;
  render();
 }

缺点:发现乾坤打开页面卡顿,因为每次都会大规模的替换store的属性值,导致页面渲染频繁,因此不是一个很理想的方法;


2.手动在store的action和mutation中触发setGlobalState(),间接去调用子应用的store仓库中的mutations和actions方法,从而改变state的值;让之前的代码逻辑不用修改太多;

// 主应用中的 store/index.js
import { globalActions } from "@/main.js";

const mutation = {
     setBarcode(state, barcode) {
        state.barcode = barcode;
        qianKunGlobalChange('setBarcode', barcode);
    }
    ...
}

const actions = {
    toSetBarcode(context, barcode) {
        context.commit("setBarcode", barcode);
        qianKunGlobalChange('toSetBarcode', barcode, 'dispatch');
    }
    ...
}

function qianKunGlobalChange(functionName, changeValue, controls='commit') {
    globalActions.setGlobalState({eventDto: {controls, functionName, changeValue}});
}

const store = new Vuex.Store({
    state: getState(),
    getters,
    mutations,
    actions
});

export default store;
//主应用中的 src/main.js

import { registerMicroApps, start, initGlobalState} from 'qiankun';
let eventDto = {
    controls: '',
    functionName: '',
    changeValue: {}
}
let initialState = { store, eventDto}

let globalActions = initGlobalState(initialState);
globalActions.onGlobalStateChange((state, prev) => {
    // state: 变更后的状态; prev 变更前的状态
    console.log("主应用的state prev", state, prev);
});

let getGlobalState = (key) => {
    return key ? initialState[key] : initialState
} 

export { globalActions };
//子应用的 src/main.js

export async function mount(props) {

    props.onGlobalStateChange((state, prev) => {
        console.log("子应用===>", state, prev);
        updateStoreGlobal(state.eventDto);
    }, true);

    Vue.prototype.$onGlobalStateChange = props.onGlobalStateChange;
    Vue.prototype.$setGlobalState = props.setGlobalState;

    //自定义的方法,用来获取想要的全局参数
    Vue.prototype.$getGlobalState = props.getGlobalState;

    render();
}

// 更新自己内部store的方法,映射全局store
function updateStoreGlobal(eventDto) {
    let { controls, functionName, changeValue} = eventDto;
    store[controls](functionName, changeValue);
}

缺点:感觉还有最优解,代码过于冗余且之后如果基座添加了新的store.state属性,那子应用也是需要再复写一遍代码的。

是第一次拆解这种类型的项目,经验欠缺,记录一下,万一以后有更好的点子呢。

时间: 2023-2-9

问题

1.基座的store传递下去实际上是旧的初始化数据,并不是响应式的。

而且传递的时间,让原本要执行mapState的方法,有时会获取不到那个store的数据。

用网上寻找的方法用Vue.observable(store) 监听这个方法,但是使用时报错如下。只有另寻它路。

2.子应用通过内部的HydraRouter.gotoPage() 方法跳转其子应用页面的时候。也存在着时间差问题。

(a)他会先执行子应用的router跳转方法。有时意外的直接执行了mounted生命周期。

(b)主应用的跳转会在它mounted后在执行,通俗来讲就是看网络的运气。

造成的影响: 子应用之前在mounted生命周期中获取store,然后再初始化页面数据的方案,现在可能数据未到它也执行了,页面数据不对,无法渲染页面。

3.子应用的详情页面跳转时它的id参数并不会保存。页面切换后就是之前最初的初始化页面。

造成的影响: 用户可能想页面对比这需求就无法满足。

image.png

方法

问题一的解决办法

在main.js中自定义一个getGlobalState方法,根据传递的key来获取初始化的全局参数数据。并通过props来向子应用注入这个全局方法。

let getGlobalState = (key) => {
    return key ? initialState[key] : initialState
} 

export {globalActions};
const apps = [
    // system的路径是main/vue
    // {
    //     name: "myVueApp",
    //     entry: "//192.168.2.27:8084",
    //     container: "#vue1",
    //     activeRule: ":tenant/main/vue",
    //     props: { store }
    // },
    {
        // name: "hydraWebIndustry",
        // entry: "//192.168.2.5:8084",
        // container: "#industry",
        // activeRule: ":tenant/industry/vue",
        // props: { store }
        name: "myVueApp",
        entry: "//192.168.101.110:8084",
        container: "#vue1",
        activeRule: ":tenant/industry",
        props: { getGlobalState }
    }
]

registerMicroApps(apps);

子应用

export async function mount(props) {

    props.onGlobalStateChange((state, prev) => {
        //子应用===> state: 新接收的数据 prev: 之前旧的数据
        console.log("%c store", "color: yellow;");

        updateStoreGlobal(state.eventDto, props);
    }, true);

    Vue.prototype.$onGlobalStateChange = props.onGlobalStateChange;
    Vue.prototype.$setGlobalState = props.setGlobalState;

    //自定义的方法,用来获取想要的全局参数
    Vue.prototype.$getGlobalState = props.getGlobalState;

    render();
}


// 更新全局store的方法
function updateStoreGlobal(eventDto, props) {
    console.log("%c ///////////////////////////////////", "color: pink;");
    console.log(props.getGlobalState());
    console.log("%c ///////////////////////////////////", "color: pink;");
    if (!Object.keys(store.state.fields).length) {
        //执行初始化
        //替换掉动态的fields和pageUrlLists,但是实际上那些初始化的值并没有注入到子应用的store中
        //之前只考虑了动态的配置信息,哪些登录信息列表在没有挂载子应用的时候都没有注入数据
        // store.state.fields = props.getGlobalState().store.state.fields;
        // store.state.pageUrlLists = props.getGlobalState().store.state.pageUrlLists;


        //这里太过于局限了
        for (let key in store.state) {
            store.state[key] = props.getGlobalState().store.state[key];
        }
    }
  
    //因为props.getGlobalState().store 才是真的改变后的值,这里赋值store才和那边同步
    //缺点: 调用替换过于频繁,可能会导致页面加载缓慢的问题
    for (let key in store.state) {
        store.state[key] = props.getGlobalState().store.state[key];
    }
    
    // 这里是之前想的解决办法但是存在这,store不是最新的数据问题,让里面的方法数据处理不同步
    // let { controls, functionName, changeValue} = eventDto;
    // store[controls](functionName, changeValue);
}

时间:2023-2-17

通过唐哥的重新排查尝试,让之前不能使用的history能够使用了,这个history之前不能调用的原因是由于之前router设置的租户前缀,而乾坤框架必须全部匹配才能够到达指定的容器里面。

export const pcRouterTable = [
    {
        path: "/",
        name: "login",
        component:  resolve => require(['@/views/portal/Login'], resolve)
    },

    {
        path: "/:tenant/login",
        name: "login",
        component:  resolve => require(['@/views/portal/Login'], resolve),
    },

    {
        path: "/:tenant",
        name: "index",
        component: resolve => require(['@/views/portal/Index'], resolve),
        children: generateRouteByModule(pcModuleTable)
    },
    // 这个地方必须要设置成为四级路由,占位解决无法显示的问题,如果不占位置就用hash模式
    {
        path: "/:tenant/hospital/:m/:p",
        name: "Vues1",
        component: resolve => require(['@/views/portal/Index'], resolve)
    },

    {
        path: "/:tenant/industry/:m/:p",
        name: "Vues2",
        component: resolve => require(['@/views/portal/Index'], resolve)
    }
];

时间:2023-2-27

chunk Loading 资源加载错误,子应用使用路由懒加载,主应用跳转微应用时页面无法跳转,且报错"chunk loading xxx failed" 资源加载失败,然后每次加载都会去请求失败的文件。

image.png

通过去qiankun官网的GitHub,issue 中发现有同等问题的项目

Github地址

解决方法如下

//主应用路由配置文件 
{
      path: "/sub/(.*)*",
      name: "sub",
      component: () => import(/* webpackChunkName: "sub" */ "@/sub.vue"),
      
      //我的理解是:子应用加载完资源后 setTimeout才开始执行跳转,防止chunk loading加载不到资源;
      //核心问题还需去详细描述中具体分析修改
      beforeEnter: (to, from, next) => {
          setTimeout(next, 0);
      }
}