通过 pinia 集中控制 vue 组件的显隐,不二次触发 onMounted

328 天前
 allzc
vue3 通过 pinia 集中处理每个组件的状态(尝试写,因为大屏组件太多了,层层套进),初次开启每个组件都是 ok 的,会触发 onMounted ,但是二次关闭再打开后,onMounted 没有触发,这是因为啥,目前通过 store.$onAction 这个监听状态,来插写 onMounted 里面的具体内容
1332 次点击
所在节点    Vue.js
11 条回复
molvqingtai
328 天前
路由开启了 keep-alive ?
ivslyyy
328 天前
加 watch ,改成 v-if
allzc
328 天前
@molvqingtai 没用到路由 全局所有组件处在同一阶层上,每个都用 v-if 来控制
allzc
328 天前
@ivslyyy 用的就是 v-if watch 具体监听什么?监听这个 store 嘛
laoyutang
328 天前
vue3 本身用 v-if 控制是会触发的,加上 pinia 还真没试过
DICK23
328 天前
组件关闭后本身没 destroy 吧
yetrun
322 天前
你这问题应该与 pinia 无关,onMounted 只会在组件初始化时运行一次。你可以贴一个简短的代码,表达一下你的意思。
allzc
317 天前
@yetrun

我的逻辑是动态引入一个文件夹下的所有组件,给与一个状态 compState ,然后通过外部调用改变 store 中这个组件的状态,来控制显隐,比如下面的组件 B 通过 singleComp 控制 A 显示

现在的情况是 我在框架刚开始搭建的时候 onUpdated 会执行以及之后的方法 onBeforeUnmount 等等 onMounted 不执行 组件变多后变成只执行 onMounted 并且关闭再打开 不会再执行 onMounted

不明所以,所以我现在直接放在$onAction 去处理,不再用 vue 的声明周期去控制

具体看代码吧
-------------------------------------------------组件 A-----------------------------------------------------------------------
<template>
<div v-if="compInfo.compState">......</div>
</template>

<script>
import useWatchComp from "@/hooks/useWatchComp.js";

export default {
name: "AAAAAAAA",
setup() {
const { compInfo } = useWatchComp(onMountedFunc, compInfoChange);
function onMountedFunc() {
要初始化的方法。。。。。。
}
function compInfoChange(newInfo) {
状态改变触发的方法
}
return { compInfo};
},
};


-------------------------------------------------组件 B-----------------------------------------------------------------------
<template>
<div v-if="compInfo.compState">
<btn @click="showAAAAAAAA" />
</div>
</template>

<script>
import useWatchComp from "@/hooks/useWatchComp.js";
import useComp from "@/hooks/useComp.js";

export default {
name: "BBBBBBBB",
setup() {
const { compInfo } = useWatchComp(onMountedFunc, compInfoChange);
const { singleComp } = useComp();

function onMountedFunc() {
要初始化的方法。。。。。。
}
function compInfoChange(newInfo) {
状态改变触发的方法
}

function showAAAAAAAA() {
singleComp({
compName: "AAAAAAAA",
compState: true,
compParams: { data: item },
});
}
return {
compInfo,showAAAAAAAA
};
},
};
</script>


-------------------------------------------------useWatchComp.js-----------------------------------------------------------------------

import { computed, getCurrentInstance } from "vue";
import compControl from "@/store/compControl.js";

const useWatchComp = (onMountedFunc, compInfoChange) => {
const instance = getCurrentInstance();
let compStore = compControl();
let compName = instance.type.name;
let compInfo = computed(() => {
return compStore[compName];
});


//我的本意是将传入的 onMountedFunc 方法放入对应的 onMounted 执行 每次组件打开的话对应执行,但有一开始讲述的问题

onBeforeMount(()=>{})
onMounted(()=>{
onMountedFunc()
})
onBeforeUpdate(()=>{})
onUpdated(()=>{})
onBeforeUnmount(()=>{})
onUnmounted(()=>{})





// 组件信息变动
compStore.$onAction(
({
name, store, args,after,onError,
}) => {
after(() => {
//所以我全部放在这里面去执行
onMountedFunc()
});
onError((error) => {});
}
);

return {
compInfo,
};
};
export default useWatchComp;

-------------------------------------------------useComp.js-----------------------------------------------------------------------

import compControl from "@/store/compControl.js";

const useComp = () => {
let compStore = compControl();
// 触发单个组件
const singleComp = ({ compName, compState, compParams = {} }) => {
compStore.singleCompControl({
compName,
compState,
compParams,
});
};
// 触发多个组件
const batchComp = ({ compList, beforeClear = false }) => {
compStore.batchCompControl({
beforeClear,
compList,
});
};

return {
singleComp,
batchComp,
};
};
export default useComp;



-------------------------------------------------store/compControl.js----------------------------------------------------------------------
import { defineStore } from "pinia";
import { forIn } from "lodash";
let files = import.meta.globEager("@/views/components/*.vue");
let state = {};
forIn(files, (value, key) => {
let compName = key.replace(/\/src\/views\/components\/|\.vue/g, "");
state[compName] = {
compName,
compState: false,
compParams: {},
};
});

const useCompStore = defineStore("compControl", {
state: () => {
return state;
},
actions: {
// 批量
batchCompControl({ beforeClear = false, compList }) {
if (beforeClear) {
forIn(state, (value, key) => {
this[key].compState = false;
this[key].compParams = {};
});
}
compList.forEach(({ compName, compState, compParams = {} }) => {
this[compName].compState = compState;
this[compName].compParams = compParams;
});
},
// 单个
singleCompControl({ compName, compState, compParams = {} }) {
this[compName].compState = compState;
this[compName].compParams = compParams;
},
},
});

export default useCompStore;
allzc
317 天前
@DICK23 destroy 不执行了 感觉这种控制方式影响了整个生命周期的问题了 具体可以看下 下面的代码回复
yetrun
303 天前
@allzc 对不起,今天才看到。你的代码不够简短啊,而且排版还不够好,这不好看懂。如果你确实遇到问题了,建议你将代码精简到只复现问题的部分,保证代码足够简短方便讨论。如果确实要上传这么复杂的代码,建议你将出问题的代码打包成一个项目上传到 stackblitz 或 Github 这样的地方,并保证能够复现,方便我们直接拉取项目针对性地解决问题。

---分折线---

我还是稍微看了下代码,尝试性地给你个答复吧。我看到你的组件 A 的代码,我暂且理解为这是你控制显隐的其中一个组件。我看到你将 v-if 放在组件内部的一个 div 了:

<div v-if="compInfo.compState">......</div>

这样是不会触发组件 A 的 onMounted 方法的。

另一个方面,你的 useWatchComp 方法作为你用到的一个公共钩子函数,我尝试的理解是,你在这里面想自己去做一套回调的逻辑,在组件初次加载和组件信息变动时,去执行你传进来的 onMountedFunc 函数。但由于是你自己管理的逻辑,与 Vue 组件生命周期无关了。所以你的问题不应该是“用 pinia 导致不二次触发 onMounted”,而应该是“你想通过 pinia 集中控制 Vue 组件的显隐,并在组件每次执行时执行一个 onMountedFunc 函数,但从第二次显示开始这个函数都不执行了"。

我对 pinia 不熟,再加上代码排版看上去不好,导致我现在没法就这个问题给你解答。应该是某处出现了一个隐藏的 Bug 导致你的问题出现。但如果是我理解的那个样子,那这个问题应该是限制在 pinia 的专业问题,与 Vue 本身应该没有关系了。

---分折线---

最后我总结一下,onMounted 是 Vue 组件生命周期的一部份,在一个组件实例加载过程中只会执行一次。Pinia 的加入不会改变这个基本的逻辑。

你的问题是想用 pinia 管理一下自己的逻辑,比如每次组件显示的时候执行一个自定义的加载函数 onMountedFunc. 那如果是这样,就不要用 onMounted 来指代了,因为它是 Vue 生命周期的一个回调函数,很容易引起歧义。用 onMountedFunc 来表达你的意思就好。
allzc
218 天前
@yetrun 谢谢大佬回答,有心了,一直忘了登,不好意思,你说的方向是对的,我现在解决了,彻底不用生命周期去控制每个组件了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/947100

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX