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参数并不会保存。页面切换后就是之前最初的初始化页面。
造成的影响: 用户可能想页面对比这需求就无法满足。
方法
问题一的解决办法
在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" 资源加载失败,然后每次加载都会去请求失败的文件。
通过去qiankun官网的GitHub,issue 中发现有同等问题的项目
解决方法如下
//主应用路由配置文件
{
path: "/sub/(.*)*",
name: "sub",
component: () => import(/* webpackChunkName: "sub" */ "@/sub.vue"),
//我的理解是:子应用加载完资源后 setTimeout才开始执行跳转,防止chunk loading加载不到资源;
//核心问题还需去详细描述中具体分析修改
beforeEnter: (to, from, next) => {
setTimeout(next, 0);
}
}