vite-vue3打包后出现el-menu动态菜单不渲染的bug

文章讲述了在Vue项目中,作者遇到后端返回的路由菜单在打包到服务器后未正确渲染的问题,发现原因是由于vite的热加载特性导致数据更新较慢,解决方案是在组件中添加`isMounted`状态管理,确保只有在组件挂载后才执行昂贵的计算并渲染菜单。
摘要由CSDN通过智能技术生成

首先我的路由菜单是后端请求返回的,如下:
在这里插入图片描述
在这里就不贴详细的路由菜单了,
bug就是:打包放到服务器后,红框部分是没有的,也就是el-menu没渲染
在这里插入图片描述

<template>
  <div :class="{ 'has-logo': showLogo }">
    <logo v-if="showLogo" :collapse="isCollapse" />
    <el-scrollbar wrap-class="scrollbar-wrapper">
      <el-menu active-text-color="f4f4f5" background-color="#304156" text-color="#bfcbd9" :default-active="now_path"
        mode="vertical" router :default-openeds="default_open">
        <div v-for="(item, index) in permission_routes" :key="index">
          <el-menu-item v-if="item.children.length == 0 && !item.hidden" :index="item.path" :route="item.path">
            <svg-icon :name="item.icon" class="icon-svg" />
            <span>&nbsp;&nbsp;{{ item.title }}</span>
          </el-menu-item>
          <el-sub-menu :index="index" v-else>
            <template #title>
              <svg-icon :name="item.icon" class="icon-svg" />
              <span>&nbsp;&nbsp;{{ item.title }}</span>
            </template>
            <template v-for="(chItem, i) in item.children" :key="i">
              <el-menu-item v-if="!chItem.hidden" :index="chItem.path" :route="chItem.path">
                <svg-icon :name="item.icon" class="icon-svg" />
                <span>&nbsp;&nbsp;{{ chItem.title }}</span>
              </el-menu-item>
            </template>
          </el-sub-menu>
        </div>
      </el-menu>
    </el-scrollbar>
  </div>
</template>

<script setup>
import Logo from './Logo.vue'
import '/src/styles/variables.scss'
import { ref, reactive, computed,} from 'vue'
import { useRoute, } from 'vue-router'
const $route = useRoute()

const now_path = $route.path
import { useStore } from 'vuex'
const $store = useStore()

const permission_routes = ref(computed(() => $store.getters.permission_routes))

const sidebar = reactive(computed(() => $store.getters.sidebar))
const showLogo = reactive(true)
const isCollapse = !sidebar.opened
const default_open = ref([1, 2, 3, 4, 5, 6, 7, 8, 9])//默认打开菜单
</script>

然后经过我一段时间折腾后,终于找到bug所在
原因是:因为vite是热加载,我使用的这部分比较慢👇

const permission_routes = ref(computed(() => $store.getters.permission_routes))

(具体原理说的不够专业,但大概意思就是permission_routes数据👆 跟不上页面dom👇


<el-menu active-text-color="f4f4f5" background-color="#304156" text-color="#bfcbd9" :default-active="now_path"
mode="vertical" router :default-openeds="default_open">
	<div v-for="(item, index) in permission_routes" :key="index">
		<el-menu-item v-if="item.children.length == 0 && !item.hidden" :index="item.path" :route="item.path">
		.....

当permission_routes数据为空,item后的数据就会报错👆

然后解决办法👇
重点就是:isMounted部分

<template>
  <div :class="{ 'has-logo': showLogo }" v-if="isMounted">
    <logo v-if="showLogo" :collapse="isCollapse" />
    <el-scrollbar wrap-class="scrollbar-wrapper">
      <el-menu active-text-color="f4f4f5" background-color="#304156" text-color="#bfcbd9" :default-active="now_path"
        mode="vertical" router :default-openeds="default_open">
        <div v-for="(item, index) in permission_routes" :key="index">
          <el-menu-item v-if="item.children.length == 0 && !item.hidden" :index="item.path" :route="item.path">
            <svg-icon :name="item.icon" class="icon-svg" />
            <span>&nbsp;&nbsp;{{ item.title }}</span>
          </el-menu-item>
          <el-sub-menu :index="index" v-else>
            <template #title>
              <svg-icon :name="item.icon" class="icon-svg" />
              <span>&nbsp;&nbsp;{{ item.title }}</span>
            </template>
            <template v-for="(chItem, i) in item.children" :key="i">
              <el-menu-item v-if="!chItem.hidden" :index="chItem.path" :route="chItem.path">
                <svg-icon :name="item.icon" class="icon-svg" />
                <span>&nbsp;&nbsp;{{ chItem.title }}</span>
              </el-menu-item>
            </template>
          </el-sub-menu>
        </div>
      </el-menu>
    </el-scrollbar>
  </div>
</template>

<script setup>
import Logo from './Logo.vue'
import '/src/styles/variables.scss'
import { ref, reactive, computed, onMounted } from 'vue'
import { useRoute, } from 'vue-router'
const $route = useRoute()

const now_path = $route.path
import { useStore } from 'vuex'
const $store = useStore()
const isMounted = ref(false)//修复vite打包后热加载
onMounted(() => {
  isMounted.value = true
})
const permission_routes = ref(computed(() => {
  if (isMounted.value) {
    return $store.getters.permission_routes
  }
}))

const sidebar = reactive(computed(() => $store.getters.sidebar))
const showLogo = reactive(true)
const isCollapse = !sidebar.opened
const default_open = ref([1, 2, 3, 4, 5, 6, 7, 8, 9])//默认打开菜单

</script>

简易原理版本👇

<template>
  <div v-if="isMounted">
    {{ expensiveComputed }}
  </div>
</template>
 
<script>
import { computed, ref } from 'vue';
 
export default {
  setup() {
    const isMounted = ref(false);
    const expensiveComputed = computed(() => {
      // 计算过程
      return $store.getters.someExpensiveGetter;
    });
 
    onMounted(() => {
      isMounted.value = true;
    });
 
    return { isMounted, expensiveComputed };
  }
};
</script>

版本2

<template>
  <div v-if="isMounted">
    {{ expensiveComputed }}
  </div>
</template>
 
<script setup>
import { computed, ref,onMounted } from 'vue';
 
const isMounted = ref(false);
onMounted(() => {
  isMounted.value = true;
});
const expensiveComputed = computed(() => {
  if (isMounted.value) {
	  // 计算过程
	  return $store.getters.someExpensiveGetter;
  }
});
 
</script>

最后贴个ai讲解:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值