V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
allzc
V2EX  ›  Vue.js

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

  •  
  •   allzc · 315 天前 · 1309 次点击
    这是一个创建于 315 天前的主题,其中的信息可能已经有所发展或是发生改变。
    vue3 通过 pinia 集中处理每个组件的状态(尝试写,因为大屏组件太多了,层层套进),初次开启每个组件都是 ok 的,会触发 onMounted ,但是二次关闭再打开后,onMounted 没有触发,这是因为啥,目前通过 store.$onAction 这个监听状态,来插写 onMounted 里面的具体内容
    11 条回复    2023-09-26 23:28:34 +08:00
    molvqingtai
        1
    molvqingtai  
       315 天前
    路由开启了 keep-alive ?
    ivslyyy
        2
    ivslyyy  
       315 天前
    加 watch ,改成 v-if
    allzc
        3
    allzc  
    OP
       315 天前
    @molvqingtai 没用到路由 全局所有组件处在同一阶层上,每个都用 v-if 来控制
    allzc
        4
    allzc  
    OP
       315 天前
    @ivslyyy 用的就是 v-if watch 具体监听什么?监听这个 store 嘛
    laoyutang
        5
    laoyutang  
       314 天前
    vue3 本身用 v-if 控制是会触发的,加上 pinia 还真没试过
    DICK23
        6
    DICK23  
       314 天前
    组件关闭后本身没 destroy 吧
    yetrun
        7
    yetrun  
       309 天前
    你这问题应该与 pinia 无关,onMounted 只会在组件初始化时运行一次。你可以贴一个简短的代码,表达一下你的意思。
    allzc
        8
    allzc  
    OP
       304 天前
    @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
        9
    allzc  
    OP
       304 天前
    @DICK23 destroy 不执行了 感觉这种控制方式影响了整个生命周期的问题了 具体可以看下 下面的代码回复
    yetrun
        10
    yetrun  
       290 天前
    @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
        11
    allzc  
    OP
       205 天前 via iPhone
    @yetrun 谢谢大佬回答,有心了,一直忘了登,不好意思,你说的方向是对的,我现在解决了,彻底不用生命周期去控制每个组件了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1004 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 22:24 · PVG 06:24 · LAX 15:24 · JFK 18:24
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.