当前位置:首页 > 探索

vue穿透

vue的样式穿透,说穿了就是一场和scoped属性斗智斗勇的游戏。你写了个组件,样式加了scoped,结果发现子组件内部的dom节点根本不鸟你。这时候你可能会骂一句:这破框架怎么连样式都管不住?

其实scoped的原理并不复杂。vue在编译时会给每个dom节点挂一个data-v-xxxxx的属性,然后把你写的css选择器后面也拼上这个属性。等价于你把`.my-class`写成了`.my-class[data-v-xxxxx]`。这招对付自己组件内部的元素绰绰有余,但一旦碰上子组件——尤其是第三方库的组件——就抓瞎了。子组件内部的dom节点身上挂的是它自己的data-v属性,你的选择器带的是你爹的data-v属性,两个对不上号,样式自然就失效了。

我头一回碰到这问题是在用element-ui改dialog样式的时候。想调一下弹窗的圆角,死活不生效。打开控制台一看,人家的dom节点上挂的是data-v-6b4a3f,我这边编译出来的是data-v-1f2e3d。两个属性各玩各的,我的样式就像隔着玻璃挠痒痒,急得人直跺脚。

解决办法其实就那三板斧。最粗暴的是在style标签上不写scoped,让样式全局生效。但这招风险不小,搞不好就把别人家的样式也改了,尤其是大型项目里,一个不留神就是一场样式雪崩。我见过有同事图省事这么干,结果改了个按钮颜色,整个后台管理系统的按钮全跟着变了,那场面简直像多米诺骨牌倒了一半突然停住——你永远不知道下一张牌什么时候倒。

稍微讲究点的是用`::v-deep`或者`>>>`穿透符。这玩意儿的意思就是告诉vue编译器:别给我这个选择器加data-v属性了,让它裸奔着去匹配子组件内部的节点。写法上`>>>`在scss里可能报错,所以更推荐`::v-deep`。用起来倒也直观,你想穿透哪一层,就在那层选择器前面加上它。比如你想改子组件里的`.inner-content`,就写成`.parent ::v-deep .inner-content`。这招能精准命中目标,又不会误伤其他组件。

还有一个冷门但好使的招,是在子组件根节点上直接写样式。因为子组件的根节点同时拥有父组件的data-v属性和自己的data-v属性,所以父组件的scoped样式其实能作用到子组件的根节点上。你要是只想改个子组件最外层的padding或者背景色,根本不用穿透,直接写就行了。这个细节好多人不知道,写样式的时候容易绕远路。

说到底,vue穿透这事儿就是个作用域博弈。scoped的初衷是好的,防止样式互相污染,但过度隔离也会让人抓狂。我见过有些团队干脆放弃scoped,全用css modules或者bem命名规范来管样式。那又是另一套玩法了,约束全靠自觉,团队纪律好还行,纪律差点的项目里,样式表乱得跟菜市场似的。

回到穿透本身,我的建议是能少穿透就少穿透。每次用`::v-deep`之前先想想,是不是自己组件结构设计得不够合理,导致样式非要跨层去改。真要穿透的时候也别客气,大大方方用,但记得注释一下原因,不然过俩月你自己回来看代码,估计也得愣半天:我当初写这穿透是想干哈来着?

分享到: