踩坑日记 神奇的scoped bug 🐞

163 阅读4分钟

🌟 前言

作为一名前端开发者,在使用 Vue 时,大家一定经常会遇到 scoped 的使用。那么,有没有思考过:scoped 是如何影响页面的呢?又是如何 保障样式不受污染的呢?以及 哪些特殊情况下会触发 scoped 的 bug 彩蛋呢

今天,我们就来揭秘这些问题!🎉 一起来看看吧!💡


🐞 bug 介绍

🔍 前情提要

在讨论这个 bug 之前,先介绍一种常见的代码组织方式 —— 微组件。简单来说,就是将组件打包成 JS 文件,然后在页面中直接引入使用。

这个 bug 出现的场景是我们 通过微组件引入了两个组件,我们可以称之为 组件 A 和组件 B

以下是它们的基本情况:

  • 组件 A 和组件 B 的功能完全不同,但目录结构非常相似,入口文件都是 index.vue
  • 两个组件都引入了一个 公共的 button 组件
  • 它们的样式都使用了 scoped 进行隔离,但 A 组件通过 deep 修改了公共 button 的样式

❌ 错误表现

以下截图均为演示所构造,并非实际代码

当我们在页面中展示这两个组件时,却惊讶地发现,B 组件的按钮变短了

image.png

然而,当我们 单独运行组件 B 时,按钮的长度却是正常的。显然,组件 B 的样式被组件 A 干扰了!🤯

image.png


🕵️‍♂️ bug 排查

bug 既然出现了,就要尽力查清原因!🔍

在调试的过程中,我们在控制台中发现 组件 B 的样式中多出了两个特别的规则

image.png

错误代码定位

  • 虽然这些样式就是问题的根源,但奇怪的是,它们 并没有出现在组件 B 的代码中
  • 我们进一步排查了主工程(即负责引入组件 A 和组件 B 的项目),依旧没有找到这些规则的来源。

于是,我们怀疑是不是其他微组件的样式干扰了组件 B 👉 逐个禁用微组件后,终于在组件 A 中发现了这段代码存在!

image.png

⚠️ 错误原因定位

组件 A 和组件 B 的 css 模块都使用了 scoped 进行隔离,且二者的位置是同级的。理论上,它们不应该互相影响。

但很显然,我们漏掉了一些细节。在这种情况下,我们需要从 scoped 的原理层面来分析这一问题。

🔬 scoped 是如何完成样式隔离的?

我们知道,Vue 的 scoped 是通过以下机制完成样式隔离的:

  1. Vue 会为当前文件 template 中的 DOM 节点生成特殊的自定义属性(类似 hash);
  2. 再为该组件的 CSS 规则加上自定义属性,从而实现样式私有化。

比如:

image.png

🧐 scoped 为什么没有成功隔离?

为了进一步确认问题原因,我们检查了 scoped 为组件 A 和组件 B 生成的自定义属性:

组件 A:data-v-a83bd3b0

image.png

组件 B:data-v-a83bd3b0

image.png

😱 原来它们的自定义属性是完全一样的!

这就说明,组件 A 的样式由于相同的 key,直接覆盖了组件 B 的样式!👀


⚙️ 自定义属性的生成规则是什么?

为了找到问题根源,我们对两个组件展开分析,结果发现:

  • 自定义属性的生成规则与 文件路径和文件名称 有关;
  • 当两个文件的路径和名称完全相同时,其生成的自定义属性值也会一致。

通过对比打包结果,我们验证了这一点 —— 如果对 目录结构或文件名稍作修改,生成的自定义属性值就会发生变化。


🚀 问题解决

最终,我们通过 调整组件 B 的目录结构和文件名,避免了自定义属性冲突,顺利解决了这个问题!✅


🎁 scoped bug 彩蛋

看到这里的大家,应该已经发现这个 scoped bug 彩蛋了吧!✨

如果两个独立打包的模块,文件路径和名称相同,scoped 可能会因为生成的自定义 key 一致导致隔离失效。

💡 建议:在开发时,尽量在编辑阶段就引入模块概念,通过增加命名空间或优化目录结构,减少这种冲突的几率。这样可以实现更可靠的样式隔离!💪

🎉 以上就是本文全部内容,希望能对你的开发有所帮助,避开类似的坑!Happy coding!🚀