为什么不建议在 Vue <style> 中使用 scoped?

前言

亲爱的小伙伴,你好!我是 嘟老板。我们使用 Vue 开发页面时,经常需要在 <style> 标签下编写样式。不知你是否留意,在 <style> 标签下有一个属性经常出现 - scoped。你知道它起到什么作用吗?原理是怎样的?有没有什么弊端呢?今天我们就来聊聊它。

1. 什么是 scoped

scoped 顾名思义,与作用域有关,因为是设计组件样式的,所以可以叫他 css 作用域样式作用域。当 <style> 标签带有 scoped 属性时,<style> 内的样式只会影响当前组件内的元素。如果你对 WebComponent 有了解的话,会发现 scoped 的作用域 Shadow DOM 比较类似。

我们先来看一段代码:

<template><div class="home">parent</div>
</template>
<script setup></script>
<style lang="scss" scoped>
.home {width: 200px;height: 200px;background-color: lightblue;
}
</style>

这是一个很简陋的 Vue 组件, 只需要在 <style> 便签上添加 scoped 属性,就能达到 限制样式作用域 的目的。

2. scoped 的作用是什么?

2.1 限制样式作用域

保证 <style> 标签内的样式仅在当前组件生效,而不会影响其他组件的样式,包括子组件。

我们来强化一下上面的代码:

<!-- Home.vue -->
<template><div class="home">parent<Child /></div>
</template>
<script setup>
import Child from './components/Child.vue'
</script>
<style lang="scss" scoped>
.home {width: 200px;height: 200px;background-color: lightblue;
}
</style>

新增一个 Child.vue 组件,有两个 div 元素,其中根节点添加 child 样式类,内部节点添加 child-inner 样式类:

<template><div class="child">child<div class="child-inner">child-inner</div></div>
</template><script setup lang="ts"></script><style lang="scss" scoped>
.child {width: 100px;height: 100px;background-color: lightcoral;.child-inner {width: 50px;height: 50px;background-color: lightpink;}
}
</style>

运行效果如下:

image.png

然后我们在 Home.vue 中设置 Child.vue 组件的 child-inner 样式类,将文字颜色设为红色:

<!-- Home.vue -->
<style lang="scss" scoped>
.home {width: 200px;height: 200px;background-color: lightblue;.child-inner {color: red;}
}
</style>

然而样式没有变化,说明 Home.vue 的样式没有渗透到 Child.vue 组件内部样式

2.2 控制子组件根节点样式

使用了 scope 的样式虽然无法影响子组件的内部样式,但是可以影响子组件的根节点。也就是说子组件的根节点同时受 父组件的作用域样式子组件的作用域样式 影响。

为什么要这样设计呢?

父组件可能存在需要控制子组件布局的情况。比如,列表数据也可能需要以栅格布局展示,这就需要通过按钮触发切换不同的布局效果。

假设子组件用于列表展示,父组件提供按钮入口,允许父组件作用域控制子组件的布局,那么切换列表的展示形式就轻而易举了。

我们继续调整下 Home.vue 的样式,将 child 改为 flex 布局,内容居中:

<style lang="scss" scoped>
.home {width: 200px;height: 200px;background-color: lightblue;.child {display: flex;justify-content: center;align-items: center;}
}
</style>

运行效果如下:

image.png

3. scoped 原理是什么?

scoped 的原理主要是 通过为当前组件的模板添加一个独一无二的属性,然后在 CSS 选择器中添加这个属性,从而实现样式的局部作用域

它的实现可以分为几个步骤:

  1. 当 Vue 编译器编译一个包含 scoped<style> 标签时,会为每个 CSS 规则添加一个独特的属性,比如 data-v-4533200f。这个属性是自动生成的,确保了在整个应用中的唯一性。

image.png

  1. 编译器将模板中的每一个 HTML 标签都添加上相同的属性。这样,当浏览器解析和应用 CSS 样式时,只有带有这个属性的元素才会被这些规则影响,实现了样式的局部作用域。

image.png

  1. 对于子组件,它们的根元素也会被添加上父组件的属性,所以父组件的 scoped 样式可以影响到子组件的根节点。然而,这个属性不会被添加到子组件的内部元素,所以父组件的样式不会影响到子组件的内部样式。

image.png

这种实现方法主要利用了 **CSS 选择器的属性选择器和浏览器的样式解析机制。

4. 为什么我不建议使用 scoped

4.1. 样式优先级问题

由于 scoped 通过添加唯一的属性来工作,这会增加选择器的特异性,可能导致由于特异性不同而出现样式优先级问题。

例如父组件 Home.vue 中有一个 warning 类,表示警告信息。子组件 Child.vue 同样有包含 warning 类的警告信息,想要通过在父组件中设置 warning 类的样式,统一控制父子组件的警告样式。

    <!-- Home.vue --><template><div class="home"><div class="warning">parent</div><Child /></div></template><script setup>import Child from './components/Child.vue'</script><style lang="scss" scoped>.home {width: 200px;height: 200px;background-color: lightblue;.warning {background-color: lightsalmon;}}</style>
    <!-- Child.vue --><template><div class="child"><div class="warning">child</div><div class="child-inner">child-inner</div></div></template><script setup lang="ts"></script><style lang="scss" scoped>.child {width: 100px;height: 100px;background-color: lightcoral;.child-inner {width: 50px;height: 50px;background-color: lightpink;}}</style>

运行后会发现,Child.vue 样式应没有生效,为什么呢?

因为父组件的 warning 拼接了该元素上特有的属性,无法作用到子组件的 dom 节点。

4.2. 无法跨组件边界工作

这其实是对上一个问题的延伸,scoped 无法控制其他组件的样式,包括子组件。这在构建大型应用程序时可能会限制你的样式选项。

当然啦,针对这种场景,vue 也为我们提供了解决方案,那就是 深度选择器 - :deep() 。可以使用 :deep() 包括需要穿透的类,达到影响子元素的效果。

    <style lang="scss" scoped>.home {width: 200px;height: 200px;background-color: lightblue;:deep(.warning) {background-color: lightsalmon;}}</style>

不过若是频繁使用 :deep(),影响代码美观和整洁度是必然的。

4.3. 性能问题

使用 scoped 可能会导致性能问题,因为浏览器在渲染时必须查找和匹配这些唯一的属性。

5. 相似方案

5.1 CSS 模块 (CSS Modules)

这是一个在编译时将类名和动画名进行本地范围限定的 CSS 文件,可以有效地实现样式隔离。

5.2 BEM(Block Element Modifier)或者其他 CSS 命名策略

恰当的命名策略可以帮助更好地组织和理解样式设计,并实现一定程度的样式隔离。

5.3 使用 CSS-in-JS 库,如 Styled Components 或者 Emotion

这些库可以提供更强大和灵活的样式封装选项,实现完全的样式隔离。

简单列举几个可行的方案,暂时先不做详细讲解.小伙伴感兴趣的话,后续会逐步更新。

个人在项目有用过 BEM 命名策略和 CSS Module

BEM 结合工具函数和 scss 预处理函数,可以极大地减轻应用的心智负担,比较典型的 ElementPlus 中就有 BEM 命名策略的应用。

CSS Module 我更多的是在 React 项目中使用,Vue 项目中用的不多,需要借助插件实现。

结语

好啦,今天的内容就到这里啦。关于 scoped 这个特性,必定是 仁者见仁,各有想法。不得不说,它还是一个比较实用的特性,可以帮助我们比较方便的实现样式隔离的需求,且不需要额外的 polyfil。亲爱的小伙伴,你怎么看呢,欢迎评论区留言讨论。

感谢阅读,愿 你我共同进步,谢谢!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/7800.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Java EE] 多线程(九):ReentrantLock,Semaphore,CountDownLatch与线程安全的集合类(多线程完结)

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏:&#x1f355; Collection与数据结构 (91平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm1001.2014.3001.5482 &#x1f9c0;Java …

PE文件(四)FileBuffer-ImageBuffer作业

C语言实现如下功能 2.编写一个函数&#xff0c;将RVA的值转换成FOA 将文件加载到内存时&#xff0c;已知一个数据在内存中的地址&#xff0c;将此地址转化成文件在硬盘上时的相对于文件起始地址的文件偏移地址。即将虚拟内存偏移地址转换成文件偏移地址。 说明&#xff1a;这里…

ssm+vue的数据学院教务管理系统。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的数据学院教务管理系统。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spring Spr…

20个入门级Python项目

很多刚刚入门python的同学除了书上、老师讲解的1、2个demo示例之外&#xff0c;基本上很难找到适合练手的入门级Python项目。 作者不辞辛苦&#xff0c;花了很长时间整理了20个适合练手的实例项目 下面这20个练手项目全部去是免费的&#xff0c;大家可以任意下载&#xff0c;…

记一次DNS故障导致用户无法充值的问题(上)

背景&#xff1a; 刚刚过去了五一劳动节&#xff0c;回来后一上班接到客服运营团队反馈的节日期间的问题&#xff0c;反馈有部分用户无法充值。拿到的反馈资料有&#xff1a; 无法充值操作视频、问题时间、手机机型、手机网络情况。 1、从视频中看到用户点击支付后没有任何反…

ANSYS许可分析工具

在工程设计与仿真领域&#xff0c;ANSYS软件作为行业领导者&#xff0c;为企业提供了强大的支持。然而&#xff0c;随着业务的发展和软件版本的升级&#xff0c;如何有效地分析和管理ANSYS许可证&#xff0c;确保资源的合理配置和高效利用&#xff0c;成为企业面临的重要问题。…

大数据高级阶段面试题(实时2)

14.Flink状态管理内部原理是什么? 状态后端&#xff1a; Flink支持多种状态后端&#xff0c;包括内存&#xff0c;文件系统和分布式存储系统&#xff0c;状态后端负责将数据保存到相应的存储系统中&#xff0c;RocksDB是Flink默认的状态后端&#xff0c;它提供了高性能的本…

Linux系统使用Docker安装青龙面板并实现远程访问管理面板

文章目录 一、前期准备本教程环境为&#xff1a;Centos7&#xff0c;可以跑Docker的系统都可以使用。本教程使用Docker部署青龙&#xff0c;如何安装Docker详见&#xff1a; 二、安装青龙面板三、映射本地部署的青龙面板至公网四、使用固定公网地址访问本地部署的青龙面板 青龙…

聊聊 ASP.NET Core 中间件(三):如何创建自己的中间件?

前言 本质上&#xff0c;中间件类也是一个普通的 .NET 类&#xff0c;它不需要继承任何父类或者实现任何接口。 但是有几个约定&#xff1a; 需要有一个构造方法构造方法至少要有一个 RequestDelegate 类型的参数&#xff0c;用来指向下一个中间件。需要定义一个名字为 Invo…

嵌入式linux学习第一天

参考正点原子Linux开发文档。记录下知识点。 Shell 基本操作 前面我们说 Shell 就是“敲命令”&#xff0c;那么既然是命令&#xff0c;那肯定是有格式的&#xff0c;Shell 命令的格式 如下&#xff1a; command -options [argument] command: Shell 命令名称。 options&…

雷军-2022.8小米创业思考-6-互联网七字诀之专注:有所为,有所不为;克制贪婪,少就是多;一次解决一个最迫切的需求

第六章 互联网七字诀 专注、极致、口碑、快&#xff0c;这就是我总结的互联网七字诀&#xff0c;也是我对互联网思维的高度概括。 专注 从商业角度看&#xff0c;专注就是要“把鸡蛋尽量放在一个篮子里”。这听起来似乎有些不合理&#xff0c;大家的第一反应可能是“风险会不会…

JUC-synchronized练习-交替打印ABC

今天来练习一下synchronized 简单来利用synchronized实现一个字符串的交替打印 主要的实现设置一个全局的变量state&#xff0c;线程执行通过不断累加state&#xff0c;根据state对三取余的结果来判断该线程是否继续执行还是进入等待。并通过synchronized锁住一个共享变量loc…

线程池会遇到的刁钻问题(下)

文章目录 如何处理线程池中的异常&#xff1f;在并发编程中&#xff0c;线程池和锁有什么关系&#xff1f;如何实现一个可以动态调整大小的线程池&#xff1f;方法一&#xff1a;扩展 ThreadPoolExecutor方法二&#xff1a;使用 ScheduledExecutorService 如何确保线程池中的任…

Fastapi中怎么一次性运行多个Scrapy爬虫

运行Scrapy爬虫很简单&#xff0c;直接"Scrapy crawl 爬虫名称"即可。但是我们如果想在Fastapi中通过接口的方式一次性运行多个爬虫。那该怎么实现&#xff1f; 假如在scrapy下面的spiders里面写了许多爬虫文件&#xff0c;你可以在spiders的__init__.py文件中&…

js 图片渐变

1. 点击图片&#xff0c;使其渐变为另一张图片 通过定义keyframes来创建一个淡入淡出的动画效果。当图片被点击时&#xff0c;先添加淡出动画使图片透明度从0渐变到1&#xff0c;然后在1秒后切换图片源并添加淡入动画使新图片透明度从0渐变到1&#xff0c;实现图片渐变效果。 …

电路板第一次调试注意事项

电路板第一次调试注意事项 调板经验其他人的经验一、上电前检查1、目测检查2、电源短路检查3、连线检查4、元器件安装检查 二、通电后检测1、通电观察2、静态调试3、动态调试 调板经验 1.打开原理图 PCB&#xff0c;熟悉一遍。 2.拿到板&#xff0c;找到输入正负极&#xff0…

VUE的回调函数,使用this变量undefined,怎么办

由于预订的抓拍方案无法令人满意&#xff0c;于是又回到了直接在WEB抓拍的方案。这个我信心满满&#xff0c;因为之前的代码已经验证过了。 验证是通过的&#xff0c;想法是简单的&#xff0c;现实是不通的。我一点抓拍按钮&#xff0c;没反应啊。这是怎么回事呢&#xff1f;于…

自动化机器学习——贝叶斯优化

自动化机器学习——贝叶斯优化 贝叶斯优化是一种通过贝叶斯公式推断出目标函数的后验概率分布&#xff0c;从而在优化过程中不断地利用已有信息来寻找最优解的方法。在贝叶斯优化中&#xff0c;有两个关键步骤&#xff1a;统一建模和获得函数的优化。 1. 统一建模 在贝叶斯优…

.双链表.

题目&#xff1a; 实现一个双链表&#xff0c;双链表初始为空&#xff0c;支持 55 种操作&#xff1a; 在最左侧插入一个数&#xff1b;在最右侧插入一个数&#xff1b;将第 k&#x1d458; 个插入的数删除&#xff1b;在第 k&#x1d458; 个插入的数左侧插入一个数&#xf…

MYSQL-使用事务保证数据完整性

什么是事务&#xff1f; 事务&#xff08;Transaction&#xff09;是作为单个逻辑工作单元执行的一系列操作 多个操作作为一个整体向系统提交&#xff0c;要么都执行&#xff0c;要么都不执行 事务的特性&#xff1a; 事务必须具备以下四种属性&#xff0c;简称ACID属性 1、…