那个从没改过的页面,为什么突然坏了?

那个从没改过的页面,为什么突然坏了?

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 对象里的属性重新写一遍。

实际效果是:

  1. v-show=false 先写成 display: none
  2. 同一次更新里,style 模块再跑
  3. 内联 style 里有 display: flex
  4. 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。看场景选就行。


小结

排查结束,代码改动不大。

以后再遇到从没改过却突然坏了,可以先问一句:你装到的,还是同一个版本吗?


相关链接


本文基于真实项目经验整理,手工起草文章大纲,AI 辅助润色,于 2026-07-13

相关推荐
泡海椒9 分钟前
JQuick-Excel FORMAT 导出格式实战:日期、金额显示与 TRANSFORM 的边界
前端·python·excel
ym hyd 1115 小时前
试题库管理系统源码 Java+SpringBoot+Vue3 前后分离
java·vue.js·spring boot·毕设
迅猛龙办公室6 小时前
Python输出当前计算机的系统日期和时间
开发语言·前端·python
小狼154546 小时前
浏览器扩展脚本为什么有时候不生效:注入时机、iframe 和单页路由,多多开票助手
前端·chrome
じòぴé南冸じょうげん6 小时前
油猴脚本突然发现变成灰色了,无法使用?页面不生效?刷新页面没反应?
前端
Csvn8 小时前
组合式 API(Composition API)
前端
火柴就是我9 小时前
Android 打包报错 25.0.3
android·前端
BD_Marathon9 小时前
消息对象中字段的说明
java·前端·python
八荒启·交互动画9 小时前
Web特效025—用 Canvas 2D 做Web特效的定义与边界:这支画笔能做到哪一步
前端·webgl·网页特效·八荒启-交互动画·八荒启
Ai-_Man9 小时前
您您这可以把Dola的多个会话比如说。左侧的多个会话一次性导出吗?不是单条会话里面的多次会对话。用AI导出鸭,答案是可以的
开发语言·前端·人工智能·小程序