黑肤下 van-field 输入框白底问题说明
背景
票据买入页(newBuyPage.vue,路由 /bills-buy)在暗黑主题(html[data-theme="dark"])下,买入金额区域的 van-field 输入框背景与页面整体不一致。该问题在 SIT 生产构建 与 本地开发环境 表现不同,排查时易误判为「黑肤配置两套」或「页面未写暗色样式」。
相关页面示例:
- SIT:
https://sit.mfosunhani.com/wealth/fund.html#/bills-buy?symbol=SP202606030018 - 本地:
http://localhost:8080/wealth/fund.html#/bills-buy?symbol=SP202606030018
表现
| 环境 | data-theme |
.van-field 计算背景 |
视觉效果 |
|---|---|---|---|
| SIT(生产包) | dark |
rgb(255, 255, 255) |
暗色卡片上出现 一块纯白输入区域,与黑肤割裂 |
| localhost(dev) | dark |
rgba(0, 0, 0, 0)(透明) |
输入框与父级 .card 暗色底 融为一体,符合设计 |
说明:
- 页面主题本身已正确切到暗黑(
data-theme="dark"),问题出在 Vant 组件默认白底未被业务样式压住,而非主题 Token 未注入。 - 父级
.card使用var(--fsw-color-bkg)作为暗色背景;输入框透明时会透出该色,不透明时则显示 Vant 自带的#fff。
原因
1. Vant 默认样式写死白底
Vant 编译后的 CSS 中对 .van-cell 设置了:
css
.van-cell {
background-color: #fff;
}
Less 主题变量桥接 无法覆盖 这条已编译进 node_modules 的规则,因此需要在业务侧做全局或局部覆盖。
2. 业务侧已有兜底,但特异性不足
src/assets/css/vant.less 中曾使用:
less
.van-cell,
.van-field {
background-color: transparent;
}
选择器特异性为 (0, 1, 0) ,与 Vant 的 .van-cell 相同 。CSS 同特异性时 后加载的规则获胜 ,因此最终是否生效完全取决于 样式文件的加载顺序。
3. 开发与生产「加载顺序相反」
| 机制 | 典型顺序 | 对 .van-field 背景的结果 |
|
|---|---|---|---|
| dev | style-loader 按 JS import 执行顺序 注入 <style> |
Fund.js 中先 VantComponents,后 @/assets/css/index.js(含 vant.less) |
transparent 在后 → 透明生效 |
| prod | mini-css-extract + splitChunks,输出多个 <link> |
实测:chunk-common(含 transparent)→ chunk-vendors(含 #fff) |
#fff 在后 → 白底生效 |
生产环境 SIT 实测样式表顺序(节选):
global-fonts.cssglobal-variables.csschunk-common.*.css→.van-cell, .van-field { background-color: transparent }chunk-vendors.*.css→.van-cell { background-color: #fff }← 覆盖兜底fund.*.css/fundRouter.*.css(页面 scoped 样式)
chunk-vendors 由 vue.config.js 的 splitChunks 将 node_modules(含 Vant)单独打包;vant.less 随业务代码进入 chunk-common。Html 注入 link 时 vendors 排在 common 之后,导致同特异性下 Vant 白底胜出。
4. 为何部分页面正常、买入页异常
同仓库内部分页面(如基金认购 Subscribe.vue)在 scoped 样式中使用了 高特异性 选择器,例如:
less
.subscribe .buyBox .buyInput .input[data-v-xxx] .van-field {
background: transparent;
}
特异性远高于 Vant 的 (0, 1, 0),与 chunk 顺序无关。票据 newBuyPage 旧构建未对买入金额 van-field 做类似覆盖,故在生产包上暴露白底问题。
解决方案
方案一(推荐):提升 vant.less 全局兜底权重
文件: src/assets/css/vant.less
为 van-cell / van-field 相关规则增加 html 前缀,将特异性从 (0, 1, 0) 提升到 (0, 1, 1),稳定压过 Vant 的 .van-cell,且 不依赖 CSS 文件加载顺序。
less
html .van-cell,
html .van-field {
background-color: transparent;
color: var(--fsw-color-iconfont-primary, #323233);
}
html .van-cell::after {
border-bottom-color: var(--fsw-color-divider, #ebedf0);
}
html .van-cell--clickable:active {
background-color: var(--fsw-color-card, #f2f3f5);
}
优点: 全站 van-cell / van-field 统一透明,由父容器(卡片、弹层等)决定底色,无需逐页修补。
注意: 属全站行为,发版后建议扫一眼列表、表单类页面是否符合预期。
提交: AI: #6753359869 提升van-cell黑肤兜底权重
方案二(可选):页面局部 scoped 覆盖
文件: src/views/fund/investProduct/Bills/newBuyPage.vue
在买入金额区域的 ::v-deep .van-field 中增加:
less
background-color: transparent;
编译后带 [data-v-xxx],特异性约 (0, 7, 0),仅作用于该页,与全局方案二选一或双保险均可;全局方案生效后局部规则可视为冗余。
不推荐单独依赖的做法
| 做法 | 说明 |
|---|---|
仅保留 .van-cell, .van-field 无 html 前缀 |
生产包仍可能被 chunk-vendors 覆盖 |
调整 splitChunks 顺序 |
改动面大,易影响其它 chunk,维护成本高 |
大量使用 !important |
后续业务覆盖困难,易引发样式战争 |
验证方式
-
Chrome DevTools
选中
.buy-input .van-field,查看 Computed → background-color 及 Styles 中生效规则来源(chunk-vendorsvschunk-common/ 带data-v-的 scoped 规则)。 -
控制台脚本(示例)
jsconst el = document.querySelector('.buy-input .van-field'); ({ theme: document.documentElement.getAttribute('data-theme'), bg: getComputedStyle(el).backgroundColor, }); -
对比环境
- 本地 dev:修复前多为
rgba(0,0,0,0)(因 import 顺序) - SIT prod:修复前多为
rgb(255,255,255);部署含html .van-field的构建后应为透明
- 本地 dev:修复前多为
相关文件
| 文件 | 说明 |
|---|---|
src/assets/css/vant.less |
Vant 全局兜底(popup、van-cell/van-field) |
src/assets/css/common.less |
引入 vant.less |
src/assets/css/index.js |
入口样式,由 src/entries/Fund.js 引入 |
src/entries/Fund.js |
VantComponents 与 @/assets/css/index.js 的 import 顺序影响 dev 注入顺序 |
vue.config.js |
splitChunks → chunk-vendors / chunk-common |
src/views/fund/investProduct/Bills/newBuyPage.vue |
票据买入页 |
小结
- 表现: 黑肤下 SIT 输入框白底,本地 dev 正常。
- 原因: Vant
.van-cell { #fff }与业务transparent同特异性;生产包中 vendors CSS 排在 common 之后,后者覆盖前者。 - 解决: 在
vant.less使用html .van-cell/html .van-field提高特异性;必要时页面 scoped 再加强。