那个从没改过的页面,为什么突然坏了?
Vue 2.7.16 / v-show 与 style / lock 文件

背景
项目很久没动过了。走航任务面板里那段任务名称,用 v-show="isRunning" 控制显示,旁边挂着 style="display: flex",写了好几年,一直正常。
今天突然出问题。
测试发来截图:任务已经结束,isRunning 是 false,任务名称却还挂在面板上。
问:这块改过吗?
答:没有。
再问:那为啥突然坏了?
这句听着就烦,但确实得查。
排查
先怀疑自己。
打开 StartTask.vue:
vue
<div
v-show="isRunning"
class="gcs-radius"
style="display: flex; justify-content: center"
>
查数据:isRunning 结束任务后确实是 false。
查更新:组件 re-render 了。
查样式:别处没有额外写 display。
清缓存、清 node_modules、重启服务,还是复现。
再跟业务:结束任务的状态流转也没问题。
有点怪。v-show 条件为假时就是给元素加 display: none,这么基础的东西怎么会突然不行?
缩小范围
做成最小复现:
vue
<div v-show="visible" style="display: flex">内容</div>
<button @click="visible = !visible">toggle</button>
点一下,该藏不藏。
去掉 style,只留 v-show,正常。
去掉 v-show,只留 style,正常。
两个一起,坏。
结论很明确:不是业务坏了,是同一个标签上的 v-show 和 style 冲突了。
但这还解释不了为什么以前好好的,今天才炸。同样的写法在项目里活了很久。
真正的原因
代码没变,那就看运行环境有没有变。
package.json 里是:
json
"vue": "^2.7.14"
注意这个 ^。它允许安装兼容范围内的更新。对 2.7.x 来说,^2.7.14 可以解析到 2.7.15、2.7.16。
再看实际装上的版本:
kotlin
vue@^2.7.14:
version "2.7.16"
事情就对上了。
项目很久没动。旧电脑上的 node_modules 还是当时装出来的版本,多半是 2.7.14 或 2.7.15。本地天天跑,页面没事。
今天换了新电脑,拉代码重新装依赖。package.json 还是 ^2.7.14,包管理器按规则装了范围内最新正式版:2.7.16。
代码一行没改,Vue 小版本跳了一格,行为就不一样了。
2.7.16 改了什么
Vue 2.7.16 代号 Swan Song,Vue 2 的最终正式版。没有新功能,主要是收尾 bugfix,以及跟 Vue 3 对齐一些类型。
其中有一条:
style: always set new styles
为了修 style 合并问题(比如 border 和 border-bottom 互相覆盖),2.7.16 去掉了新旧值相同就跳过的判断,每次更新都会把 style 对象里的属性重新写一遍。
实际效果是:
v-show=false先写成display: none- 同一次更新里,style 模块再跑
- 内联
style里有display: flex setProp(el, 'display', 'flex')把none盖掉
于是 v-show 失效,元素又显示出来了。
官方 issue:vuejs/vue#13140。有人提过修复 PR,但 Vue 2 已经 EOL,不会再发补丁了。
所以不是指令写错了,而是上游为修 A 引入的回归,刚好打在 B 上。我们今天才看见,是因为新环境第一次真正跑到了 2.7.16。
关于 lock 文件
复盘下来,比改模板更值得记住的是这件事:
package.json 写的是版本范围,真正装到哪一版,看 lock。
lock 认真提交、认真用的话:
- 旧电脑和新电脑会装到同一个
vue - 本地正常、别人环境异常,这种扯皮会少很多
- 升级会变成一次明确的变更,而不是重装依赖时顺手带上来的
这次真正的隐藏原因就一句:不是页面突然坏了,是换电脑后第一次装到了 2.7.16。
所以 lock 文件该提交就提交,别当可有可无。
解决办法
1. 降级到 2.7.15
最直接。2.7.15 上 v-show 不会被 style 盖掉。
代价是丢掉 2.7.16 里其它修复(keep-alive 泄漏、一些响应式边界、类型对齐等)。适合临时止血,不太适合长期停在这儿。
2. v-show 换成 v-if
vue
<div
v-if="isRunning"
class="gcs-radius"
style="display: flex; justify-content: center"
>
改动小,立刻生效。元素会销毁重建,普通 UI 一般没问题;地图、图表、内部状态多的组件要小心一点。
3. 静态 style 改成 class
vue
<div v-show="isRunning" class="gcs-radius task-name-row">
css
.task-name-row {
display: flex;
justify-content: center;
}
v-show 写的是内联 display: none,优先级高于样式表里的 display: flex,不会被盖掉。布局也更干净。
我们这边任务面板用了方案 2,方案管理弹窗把 display: flex 抽到 class,用了方案 3。看场景选就行。
小结
排查结束,代码改动不大。
以后再遇到从没改过却突然坏了,可以先问一句:你装到的,还是同一个版本吗?
相关链接
- Issue:Using v-show and :style simultaneously does not yield expected results in 2.7.16
- 根因 commit:fix(style): always set new styles
- 未合入的修复尝试:PR #13198
- Vue 2 EOL 说明:Vue 2 End of Life
本文基于真实项目经验整理,手工起草文章大纲,AI 辅助润色,于 2026-07-13