大家好,我是奈德丽。我又又又来分享我的经验了。
背景
最近项目的缓存越来越混乱,各种store、localStorage、sessionStorage到处都是,维护起来简直是噩梦。于是我决定花点时间重构一下缓存逻辑,让代码更清爽一些。
事故现场
在清理代码的过程中,我打开了 文件,发现里面有一些看起来没用到的store相关代码。
我就注释了没被用到的三行代码
1. 顶部的import语句:
javascript
/* 我注释的第一行代码
import { useVerifyStore } from '@/stores/index.js'
*/
2. beforeEnter钩子中的代码: if语句底下是原本就注释的代码,所以我看到整个文件没有对useVerifyStore去使用,心想着先给他注释了吧,结果就白屏了
javascript
{
path: '/ticketBook',
name: 'ticketBook',
component: () => import('../pages/ticketBook/index.vue'),
beforeEnter: (to, from, next) => {
/*这是我注释的第二行和第三行代码
let useVerifyStoreObj = useVerifyStore() //验价store
let { query } = to
*/
next();
//这是原本就注释了的代码
// if (!useVerifyStoreObj.verifyObj.verifyRefTraceId) {//没有验价
// next({
// path: '/preloadLoading',
// query,
// });
// } else {
// next();
// }
}
}
心想:"这些代码看起来没啥用啊,验证功能我也没在这个路由里看到相关逻辑,而且都被注释了",于是就很自然地把它们彻底删除了,也就注释了三行代码。
保存,刷新页面...
白屏!
整个应用直接挂了,控制台报错如下:

从错误信息可以看到:
Uncaught ReferenceError: Cannot access 'useBaggageUtils' before initialization
- 还有一些关于事件监听器的警告
这说明模块初始化顺序出了问题,i18n在还没初始化完成时就被访问了。
排查过程
第一反应:懵逼
我当时的心情就是:???
明明只是注释了一些看起来无关紧要的代码,怎么整个应用都崩了?难道这些代码有什么魔法?
深入调查
既然问题出在这些代码上,我就开始追踪这个依赖链条:
1. router/index.js 的依赖关系
javascript
import {createRouter,createWebHistory,createWebHashHistory} from 'vue-router'
import { Base64 } from "js-base64";
import qs from 'qs'
import CurrencyAll from '@/pages/components/Currency/currency.js'
import { useVerifyStore } from '@/stores/index.js' // 这里引入了stores
import { getEncryptionStr,getDecryptionStr } from '@/utils/index.js'
import { eventTracking } from '../server/request';
import { FlightSearchParams } from '@/utils/paramsModels.js'
import {setLangFromBrowser} from '@/utils/switchLanguage.js' // 这里也引入了switchLanguage
import ExpiredLinkPage from '@/pages/components/ExpiredLinkPage.vue';
2. stores/index.js 的依赖关系
javascript
import { ref, computed, reactive, toRefs } from 'vue'
import { defineStore } from 'pinia'
import { handleOfferData } from '@/utils'
import axios from '@/server/axios.js'
import apis from '@/server/api'
import { Languages } from "@/utils/enums";
import { useI18n } from 'vue-i18n'
import * as vant from 'vant'
import Decimal from 'decimal.js'
import { createEsimLogic } from '@/utils/esimUtils.js'
import vantLanguage from '@/utils/vantLanguage'
import { flushLanguageCallbacks } from '@/utils/switchLanguage' // 又引入了switchLanguage!
import { useBaggageAndOtherPoliclesStores } from './baggageAndOtherPoliclesStores'
3. utils/switchLanguage.js 的依赖关系
javascript
import { useGlobalStore, useLocalGlobalStore } from '@/stores/index.js' // 又引回了stores!
import apis from '@/server/api.js'
import axios from '@/server/axios.js'
import { generateUUID } from '.'
4. lang/i18n.js 的依赖关系
javascript
import { createI18n } from "vue-i18n";
import {setLangFromBrowser} from '@/utils/switchLanguage.js' // 也引入了switchLanguage
// ... 其他语言包导入
// 在初始化过程中调用
let langStore = localStorage.getItem('language');
if (langStore) {
local = langStore
}else{
const browserLang = navigator.language
const langFromBrowser = setLangFromBrowser(browserLang) // 这里调用了switchLanguage的函数
if(langFromBrowser){
local = langFromBrowser
}
}
真相大白
卧槽!多重循环依赖!
依赖链条是这样的:
router/index.js
→stores/index.js
stores/index.js
→utils/switchLanguage.js
utils/switchLanguage.js
→stores/index.js
(循环!)- 同时,
lang/i18n.js
→utils/switchLanguage.js
原来是这样的:
- 平时虽然存在循环依赖,但因为
router/index.js
中useVerifyStore()
的调用,让模块初始化有了一个稳定的顺序 - 当我注释掉
import { useVerifyStore } from '@/stores/index.js'
和let useVerifyStoreObj = useVerifyStore()
后,模块加载顺序发生了变化 - 某些工具函数(如
useBaggageUtils
)在还没初始化完成时就被访问了 - 整个依赖链条崩塌,导致白屏
这就像是一个精妙的平衡系统,我以为自己只是拿掉了一个看似无用的零件,结果整个系统都垮了。
解决方案
实际解决方案:重构依赖结构
我的解决方法是把循环依赖的根源给切断了:
1. 创建独立的语言工具文件
把 setLangFromBrowser
方法从 switchLanguage.js
中分离出来,创建一个新的 languageUtils.js
文件:
javascript:e:\yuetu\voyawiser-mobile\src\utils\languageUtils.js
// 纯函数,不依赖任何store
export function setLangFromBrowser(browserLang){
// 浏览器语言映射逻辑
const langMapping = {
'zh-CN': 'zh_CN',
'zh-TW': 'zh_TW',
'zh-HK': 'zh_TW',
'en-US': 'en',
'en-GB': 'en',
'ko-KR': 'ko',
'es-ES': 'es',
'fr-FR': 'fr',
'de-DE': 'de',
'it-IT': 'it',
'nb-NO': 'nb_NO',
'my-MY': 'my',
'th-TH': 'th'
}
return langMapping[browserLang] || langMapping[browserLang.split('-')[0]] || null
}
2. 修改i18n.js的引用路径
javascript:e:\yuetu\voyawiser-mobile\src\lang\i18n.js
// 原来的引用(会造成循环依赖)
// import {setLangFromBrowser} from '@/utils/switchLanguage.js'
// 新的引用(从独立文件引入)
import {setLangFromBrowser} from '@/utils/languageUtils.js'
3. 打破循环依赖链
这样就彻底切断了 i18n.js
→ switchLanguage.js
→ stores/index.js
这条依赖链。
为什么这个方案有效
- 根本性解决:不是绕过问题,而是从根源上解决了循环依赖
- 最小改动:只需要创建一个新文件,修改一个引用路径
- 清晰的职责分离:语言相关的纯函数独立出来,职责更清晰
- 消除隐式依赖:不再依赖某个特定的模块加载顺序来维持稳定
教训总结
1. 看似无用的代码可能有隐藏作用
在重构时,不要轻易删除或注释看起来"无用"的代码,特别是在复杂的依赖关系中。有时候一行看似无关的代码可能在维持整个系统的平衡。
2. 循环依赖是定时炸弹
虽然JavaScript允许循环依赖,但这种设计本身就是有问题的。它让代码变得脆弱,一个小改动就可能引发连锁反应。
3. 重构要小步快跑
重构时应该:
- 一次只改一个地方
- 每次改动后都要测试
- 使用版本控制,随时可以回滚
- 理解代码的依赖关系再动手
4. 职责分离很重要
把纯函数和有副作用的函数分开,把工具函数和业务逻辑分开,这样可以大大减少不必要的依赖关系。
后续优化
这次事故让我意识到,项目的架构设计确实需要优化。后续计划:
- 继续梳理其他可能的循环依赖:使用工具分析整个项目的依赖图
- 建立更清晰的分层架构:utils层不应该依赖stores层
- 制定模块引用规范:避免循环依赖的编码规范
- 把更多的纯函数独立出来:减少不必要的依赖关系
结语
重构是好事,但要谨慎。特别是在复杂的项目中,看似简单的改动可能会触发意想不到的问题。
这次的经历告诉我:解决问题要从根源入手,不要只是绕过问题。
有时候,最简单直接的解决方案往往是最有效的!理解代码的依赖关系,比盲目地删除"无用"代码更重要。
大家觉得不错可以点点赞,收藏收藏。