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 模式存在差异,但是具体是什么,没有细究。
相关文章:
vuex4.x 升级pinia,router 中使用同步组件导致项目启动失败
背景描述 升级的项目本来是vue2的项目,先升级成vue3,这个过程相关的问题都被决绝,当时状态管理使用的还是vuex4.x版本。 后面发现变成复杂模块时,后续再对复杂模块的功能进行迭代时,由于js的弱类型,改动时…...

0. 云原生之基于乌班图远程开发
云原生专栏大纲 文章目录 安装乌班图配置静态IP重置root密码开启root远程登录开启远程SSH访问安装docker安装docker-compose安装Edge浏览器安装搜狗输入法安装TeamViewer安装虚拟显示器安装JDK安装maven安装vscodevscode插件安装VSCode配置maven、git、jdk、自动报错vscode快捷…...
C++ 字符串处理5-手机号邮箱如何脱敏处理
1. 关键词2. strutil.h3. strutil.cpp4. 测试代码5. 运行结果6. 源码地址 1. 关键词 关键词: C 字符串处理 分割字符串 连接字符串 跨平台 应用场景: 有些重要信息需要保密,比如手机号、邮箱等,如何在不影响用户阅读的情况下…...
【lesson8】云备份服务端完整版代码
文章目录 util.hppconfig.hpphot.hppdata.hppserver.hppserver.ccMakefilecloud.conf util.hpp #pragma once #include <iostream> #include <fstream> #include <string> #include <vector> #include <sys/stat.h> #include <unistd.h> …...

AI办公自动化:kimi批量搜索提取PDF文档中特定文本内容
工作任务:PDF文档中有资料来源这一行,比如: 资料来源:moomoo tech、The Information、Bloomberg、Reuters,浙商证券研究所 数据来源:CSDN、浙商证券研究所 数据来源:CSDN、arXiv、浙商证券研…...

基于C#开发web网页管理系统模板流程-总集篇
第一篇 基于C#开发web网页管理系统模板流程-登录界面和主界面_c#的网页编程-CSDN博客 第二篇 基于C#开发web网页管理系统模板流程-主界面管理员录入和编辑功能完善_c#网页设计-CSDN博客 第三篇 基于C#开发web网页管理系统模板流程-主界面管理员入库和出库功能完善_c#web程序设计…...

什么是DMZ?路由器上如何使用DMZ?
文章目录 📖 介绍 📖🏡 演示环境 🏡📒 DMZ 📒🚀 DMZ的应用场景💡 路由器设置DMZ🎈 注意事项 🎈⚓️ 相关链接 ⚓️📖 介绍 📖 在网络管理中,DMZ(Demilitarized Zone,隔离区)是一个特殊的网络区域,常用于将公共访问和内部网络隔离开来。DMZ功能允许…...
【bugfix】解决Redis缓存键清理问题
前言 在Spring Boot应用中集成Redis作为缓存存储时,合理配置RedisTemplate是确保数据正确存储和检索的关键。本文将通过对比分析一段初始存在问题的Redis配置代码及其修正后的版本,探讨如何正确处理Redis键前缀,以避免清理缓存时遇到的问题。…...

泛微开发修炼之旅--15后端开发连接外部数据源,实现在ecology系统中查询其他异构系统数据库得示例和源码
文章链接:15后端开发连接外部数据源,实现在ecology系统中查询其他异构系统数据库得示例和源码...

弹幕逆向signature、a_bogus
声明 本文章中所有内容仅供学习交流使用,不用于其他任何目的,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关! 本文章未经许可禁止转载&a…...
jEasyUI 使用标记创建树形菜单
jEasyUI 使用标记创建树形菜单 jEasyUI 是一个基于 jQuery 的用户界面插件库,它提供了一系列的组件,用于快速构建网页用户界面。其中,树形菜单(Tree Menu)是 jEasyUI 提供的一个非常实用的组件,它可以帮助…...
IT人的拖延——拖是因为不想离开“舒适区”?
人都是求“稳”的,在一个区域内呆了很久,也很舒适了,如果冒险离开进入未知的区域,万一结果不好怎么办?万一自己不适合怎么办?万一这个区域有着自己难以忍受的东西怎么办?这些对未知区域的恐惧感让我们在面对应该要做的事情时,不自觉地又拖延了起来。比如,我们在面临需…...

JUnit 5学习笔记
JUnit 5 学习笔记 1.JUnit5的改变2.JUnit5常用注解及测试2.1 DisplayName/Disabled/BeforeEach/AfterEach/BeforeAll/AfterAll2.2 Timeout2.3 RepeatedTest 3.断言3.1 简单断言3.2 数组断言3.3 组合断言3.4 异常断言3.5 超时断言3.6 快速失败 4.前置条件5.嵌套测试6.参数化测试…...

西格玛 ------ 第18个希腊字母学习
名词解释 在数学中,我们把∑作为求和符号使用,用小写字母σ,表示标准差。 ∑符号表示求和,读音为sigma,英文意思为Sum,Summation,汉语意思为“和”“总和”。 例1 公式使用说明:…...
【C语言】assert.h——断言
文章目录 主要内容调试和发布模式使用示例用法总结与注意事项 断言是一种用于在程序执行过程中进行调试的工具,能够帮助开发者验证程序的某些假设是否为真。如果断言失败,程序会终止,并输出一个错误消息,通常包含出错的文件名和行…...

HTML静态网页成品作业(HTML+CSS)—— 零食商城网页(1个页面)
🎉不定期分享源码,关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 🏷️本套采用HTMLCSS,未使用Javacsript代码,共有1个页面。 二、作品演示 三、代…...

虚函数机制-动态绑定的应用
虚函数使得程序在运行的时候根据指针指向对象的类型来确定调用哪个函数。 下图中:都为静态绑定。因为在编译器就确定了可以调用的函数 此时当基类指针指向派生类对象时,因为没有virtual关键字,所以在编译阶段就根据指针类型确定了要指向的函…...

MOS开关电路应用于降低静态功耗
本文主要讲述MOS开关电路的应用,过了好久突然想整理一下,有错误的地方请多多指出,在做电池类产品,需要控制产品的静态功耗,即使让芯片进入休眠状态,依旧功率很大,所以在电路中加一组软开关,防止…...

【每日刷题】Day65
【每日刷题】Day65 🥕个人主页:开敲🍉 🔥所属专栏:每日刷题🍍 🌼文章目录🌼 1. LCR 175. 计算二叉树的深度 - 力扣(LeetCode) 2. 序列找数_牛客题霸_牛客网…...

Oracle数据库连接并访问Microsoft SQL Server数据库
Oracle数据库连接并访问Microsoft SQL Server数据库 说明: 1.实际开发中,Oracle数据库与SQLServer数据库之间可能需要相互进行访问,方便业务数据抽取,编写视图及表等操作。 2.SQLServer访问Oracle数据库配置相对较为简单&…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...

【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务
通过akshare库,获取股票数据,并生成TabPFN这个模型 可以识别、处理的格式,写一个完整的预处理示例,并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务,进行预测并输…...

Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...

【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...

MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...