您的位置:首页 > 娱乐 > 八卦 > vuex4.x 升级pinia,router 中使用同步组件导致项目启动失败

vuex4.x 升级pinia,router 中使用同步组件导致项目启动失败

2024/10/7 2:28:47 来源:https://blog.csdn.net/u011244682/article/details/139626280  浏览:    关键词:vuex4.x 升级pinia,router 中使用同步组件导致项目启动失败

背景描述

升级的项目本来是vue2的项目,先升级成vue3,这个过程相关的问题都被决绝,当时状态管理使用的还是vuex4.x版本。

后面发现变成复杂模块时,后续再对复杂模块的功能进行迭代时,由于js的弱类型,改动时比较容易出现问题,而且接口之间没有类型约束,导致改动时需要对比接口数据,再重新思考整个逻辑,导致维护成本提升。

后面引入TS的过程中,想将 vuex顺便替换成vue3种官方推荐的 pinia。按照pinia官方的文档改动很容易,大概有这个几个地方:

1. 重构store

pinia编写store时,采用扁平化的方式,直接将原有的vuex中state,getter,action迁移,其中mutations也直接写成actions,因为pinia action支持同步和异步,这样大大减少vuex中的概念确认。

2. 替换vue 组件中对store的使用

主要是以下几种方式:
● 通过 s t o r e . c o m m i t 或者 store.commit 或者 store.commit或者store.dispatch使用。
● 通过mapState,mapActions等使用
pinia 官方同样提供了支持,直接替换就好。

升级的问题

看似没有问题

本地dev模式下,由于是边改边看的,而且未完成迁移前vuex和pinia并存,项目都是正常的,整体迁移完后除了肉眼改动错误的地方,一切都是正常的。全部移除vuex的时候突然出现问题:

Uncaught ReferenceError: Cannot access 'useUserStore' before initialization
at main.vue

这个代码中有如下代码:

  methods: {...mapActions(useAppStore, ['setLocal', 'setParentMenu', 'setUserServerData', 'setUserServerCascadeData']),...mapActions(useUserStore, ['setUnReadMsgCount']),searchFileOpen(params) {this.$bus.emit('on-search-file-open', params)},}

就是store的action映射到组件。查询资料,官方给出的解释是 pinia实例没有挂载。
实际项目中导致的这种问题是在 router定义文件中,使用main.vue作为导航的主组件,也就是component参数。

import * as Vue from 'vue'
import Main from '@/view/main'
import FullScreenMain from '@/view/main/full-screen-main.vue'
import SliderMain from '@/view/main/slider-main.vue'
import Blank from '@/blank.vue'export default [{path: '/',name: 'rootPath',redirect: '/index',},{path: '/blank',name: 'Blank',component: Blank,},{path: '/index',name: 'index',redirect: '/script/owner/-1',parentMenu: 1,meta: {icon: 'ios-navigate',title: '工作空间',launchItemName: 'script',},component: Main,children: [{path: '/script/:scriptType/:scriptId',name: 'scriptIndex',meta: {parentMenu: 1,icon: 'document-text',title: '工作空间',launchItemName: 'script',},component: () => import('@/view/script-manage/script-list.vue'),},]}
]
这个路由定义在createRouter中使用,于是根据官方的思路应该是创建路由时,加载了组件,而组件中使用的mapActions 方法,但是此时整个vue实例没有挂载,导致pinia未初始化,从而引发useUserStore 函数不可用问题。
解决办法:既然是创建时机的问题,那么在路由中加载main组件时,使用异步加载就好,这样等整个vue实例挂载后,再去访问页面肯定可以的。在上面的router定义中,改动如下:
const Main = () => import('@/view/main')
const FullScreenMain = () => import('@/view/main/full-screen-main.vue')
const SliderMain = () => import('@/view/main/slider-main.vue')xxx 省

然后 npm run dev ,项目正常启动,功能正常。

惊慌的上线构建发布

在上述问题都解决后,项目发布到准线上运行,在发布流程完成后,开心的打开新页面,一看:我艹,页面的菜单呢。怎么都没有了?急忙打开控制台,想要看到红色的xxx,可一个都没有。顿时慌了,发生了什么?

再次在本地删掉node_modules,再次启动项目,本地还是正常。同样线上重新构建,发现依然没有菜单,也没有任何错误。

接下来怀疑环境,线上构建是node 18.15.0,本地是20.10。尝试指定线上node版本,由于太高,构建工具不支持,只能调低本地版本,切换成18.15.0后,dev模式启动依然正常,瞬间感觉不该搞升级,吃力还出了问题,但是没办法,得继续解决。

解析来本地模拟线上环境,使用同样的构建命令,使用本地nginx作为静态服务器,折腾半天后,本地终于能启动,出现了线上相同的问题。

接下来排查菜单消失的原因。代码中大量使用了动态菜单,即对比路由定义上的元数据和后端接口的权限信息,动态的构建路由和菜单组件,有一个工具方法:

export const getFirstLevelMenuByRouter = (list) => {const res = []const parentChildrenList = []for (let i = 0; i < list.length; i++) {if (list[i].hasOwnProperty('component') &&list[i].component &&list[i].component.name === 'Main') {parentChildrenList.push(list[i])}}forEach(parentChildrenList, (item) => {if (item.meta && !item.meta.hideInMenu) {const obj = {icon: (item.meta && item.meta.icon) || '',name: item.name,meta: item.meta,parentMenu: item.parentMenu,path: item.path,}res.push(obj)}})return res
}

这个方法生成一级菜单,对组件进行了名称判断。然后通过打日志,发现新版本经过这个方法后,返回空数组,即 判断 component.name===‘Main’ 失效了。然后突然想起来为决绝pinia异步加载的问题,Main改成了
const Main = () => import(‘@/view/main’) 这种方式,名称变了,自然匹配不上。
该怎么解决:

const Main = defineAsyncComponent(() => import('@/view/main'))
Main.name = 'Main'
const FullScreenMain = defineAsyncComponent(() => import('@/view/main/full-screen-main.vue'))
FullScreenMain.name = 'FullScreenMain'
const SliderMain = defineAsyncComponent(() => import('@/view/main/slider-main.vue'))
SliderMain.name = 'SliderMain'

对于有问题的组件,全部改成异步,并指定名称。

结论

● 使用异步组件解决pinia初始化晚导致如果在路由中引入的组件使用pinia函数报错问题。
● 使用 defineAsyncComponent 定义异步组件,并指定名称,避免业务中对组件的名称进行了判断。

其它

尝试不使用异步组件

在线上构建问题后,代码中去掉了异步加载路由组件的方式,这时候必然出现 pinia中useXXStore未初始化的问题,尝试去掉mapState,mapAction,采用setup函数配合的方式,出现如下问题:
vue ReferenceError: Cannot access ‘Main’ before initialization
这叫莫名其妙了,但是我知道肯定是因为pinia的原因,也即是在Router中使用同步组件,而组件中使用了pinia函数的原因,但是这错误很诡异。这种方法行不通。

未解之谜

在初步使用异步import后,使用dev 模式本地正常,线上异常,这说明dev模式和build 模式存在差异,但是具体是什么,没有细究。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com