浅谈vue中慎用style的scoped属性


Posted in Javascript onNovember 28, 2017

在vue组件中,为了使样式私有化(模块化),不对全局造成污染,可以在style标签上添加scoped属性以表示它的只属于当下的模块,这是一个非常好的举措,但是为什么要慎用呢?因为在我们需要修改公共组件(三方库或者项目定制的组件)的样式的时候,scoped往往会造成更多的困难,需要增加额外的复杂度。

scoped实现私有化样式的原理

为什么会说,会增加复杂度?那么我们先从的实现模块的原理说起。为了方便称呼,我们假设把这种组件叫做模块私有组件,其他的未加scoped的叫做模块一般组件。

通过查看DOM结构发现:vue通过在DOM结构以及css样式上加唯一不重复的标记,以保证唯一,达到样式私有化模块化的目的。具体的渲染结果是怎样的,通过一个例子来说明。

公共组件button组件

一个公共组件button,为了样式模块化,给其加上scoped属性,

//button.vue
<template>
  <div class="button-warp">
    <button class="button">text</button>
  </div>
</template>
...
<style scoped>
  .button-warp{
    display:inline-block;
  }
  .button{
    padding: 5px 10px;
    font-size: 12px;
    border-radus: 2px;
  }
</style>

浏览器渲染button组件

button组件在浏览器渲染出的html部分和css部分分别为:

<div data-v-2311c06a class="button-warp">
  <button data-v-2311c06a class="button">text</button>
</div>
.button-warp[data-v-2311c06a]{
  display:inline-block;
}
.button[data-v-2311c06a]{
  padding: 5px 10px;
  font-size: 12px;
  border-radus: 2px;
}

从上面的字可以看出,添加了scoped属性的组件,为了达到组件样式模块化,做了两个处理:

  1. 给HTML的DOM节点加一个不重复data属性(形如:data-v-2311c06a)来表示他的唯一性
  2. 在每句css选择器的末尾(编译后的生成的css语句)加一个当前组件的data属性选择器(如[data-v-2311c06a])来私有化样式

大家都知道css样式有一个优先级的说法,scoped的这一操作,虽然达到了组件样式模块化的目的,但是会造成一种后果:每个样式的权重加重了:理论上我们要去修改这个样式,需要更高的权重去覆盖这个样式。这是增加复杂度的其中一个维度。

其他组件引用button组件

上面分析了单个组件渲染后的结果,那么组件互相调用之后会出现什么样的结果呢?,具体分两种情况:模块一般组件引用模块私有组件(本质和模块私有组件引用模块一般组件一样);模块私有组件引用模块私有组件。

举个例子:在组件content.vue中使用了button组件,那么content.vue组件是否添加scoped属性渲染出来的结果有什么区别呢?

//content.vue
<template>
  <div class="content">
    <p class="title"></p>
    <!-- v-button假设是上面定义的组件 -->
    <v-button></v-button>
  </div>
</template>
...
<style>
  .content{
    width: 1200px;
    margin: 0 auto;
  }
  .content .button{
    border-raduis: 5px;
  }
</style>

模块一般组件(未添加scoped)引用模块私有组件

如果style上没有加scoped属性,那么渲染出来html和css分别就是:

<div class="content">
  <p class="title"></p>
  <!-- v-button假设是上面定义的组件 -->
  <div data-v-2311c06a class="button-warp">
    <button data-v-2311c06a class="button">text</button>
  </div>
</div>
/*button.vue渲染出来的css*/
.button-warp[data-v-2311c06a]{
  display:inline-block;
}
.button[data-v-2311c06a]{
  padding: 5px 10px;
  font-size: 12px;
  border-radus: 2px;
}
/*content.vue渲染出来的css*/
.content{
  width: 1200px;
  margin: 0 auto;
}
.content .button{
  border-raduis: 5px;
}

可以看出,虽然在content组件中,修改了button的border-raduis属性,但是由于权重关系,生效的依然是组件内部的样式(此时是外部的样式被覆盖)。所以如果要达到修改样式的目的,就必须加重我们要修改样式的权重(增加选择器层级,ID选择器,并列选择器,impotant等)

模块私有组件(添加scoped)引用模块私有组件

如果加了scoped属性呢?按照开始分析出来的规则(事实也是这么的):

首先是在所有的DOM节点加上data属性

然后在css选择器尾部加上data属性选择器

那么渲染出来html和css分别就是:

<div data-v-57bc25a0 class="content">
  <p data-v-57bc25a0 class="title"></p>
  <!-- v-button假设是上面定义的组件 -->
  <div data-v-57bc25a0 data-v-2311c06a class="button-warp">
    <button data-v-2311c06a class="button">text</button>
  </div>
</div>
/*button.vue渲染出来的css*/
.button-warp[data-v-2311c06a]{
  display:inline-block;
}
.button[data-v-2311c06a]{
  padding: 5px 10px;
  font-size: 12px;
  border-radus: 2px;
}
/*content.vue渲染出来的css*/
.content[data-v-57bc25a0]{
  width: 1200px;
  margin: 0 auto;
}
.content .button[data-v-57bc25a0]{
  border-raduis: 5px;
}

对于上面的两种情况,可以明显看出来渲染后的结果大不相同。

虽然我们在content添加了想要修改button组件的样式的代码,但是仔细看,由于.content .button这句在末尾加的是content组件的scoped标记,最后这句其实根本作用不到我们想要的DOM节点上,所以这种情况我们在content内部写的任何样式都不会影响到button.vue组件,所以这就尴尬了。。。。

当然这个问题也是可以解决的,就是直接加全局样式可以修改到,但这势必会影响全部地方的组件;所以需要另外一种方法在content.vue组件内再加一个不带scoped属性的style标签,也就意味着要加两个style,一个用于私有样式,一个用于共有样式。这肯定是有点shit的,并且这两种解决方案都回避不了一个问题:权重!!!

//content.vue
<template>
  <div class="content">
    <p class="title"></p>
    <!-- v-button假设是上面定义的组件 -->
    <v-button></v-button>
  </div>
</template>
...
<style scoped>
  .content{
    width: 1200px;
    margin: 0 auto;
  }
</style>
<style>
  .content .button{
    border-raduis: 5px;
  }
</style>

这样符合规范么?貌似没看到不能这么写,并且这么写也确实生效了。。。但这样确实增加了思维的复杂度,有点苦恼啊。

总结scoped的渲染规则

总结一下scoped三条渲染规则

  1. 给HTML的DOM节点加一个不重复data属性(形如:data-v-2311c06a)来表示他的唯一性
  2. 在每句css选择器的末尾(编译后的生成的css语句)加一个当前组件的data属性选择器(如[data-v-2311c06a])来私有化样式
  3. 如果组件内部包含有其他组件,只会给其他组件的最外层标签加上当前组件的data属性

解决方案

对于引用的三方库,如果对方使用了scoped,我们无力改变什么,如果确实需要修改他的样式最能在不加scoped的组件中修改样式,或者全局样式直接修改,这很粗暴!

对于自己维护的组件,一定要想清楚,组件的样式能否满足所有的情况。如果确实需要加,无疑会增加使用这个组件的开发同学的工作!

当然对于这个问题,如果诸君有更好的解决方案,请诸君TELL ME一下下!

趣事

<del> 在使用scoped一定要谨慎scoped的这个特性,本人以为这是一个BUG,就去提了issue </del>,结果尤大很霸气的回复
scoped设计的初衷就是不能让当前组件的样式修改其他任何地方的样式,因为设计如此。所以理所当然的这个issue已被干掉

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
文本框的字数限制功能jquery插件
Nov 24 Javascript
各浏览器中querySelector和querySelectorAll的实现差异分析
May 23 Javascript
在js中判断checkboxlist(.net控件客户端id)是否有选中
Apr 11 Javascript
javascript 树形导航菜单实例代码
Aug 13 Javascript
jQuery移动web开发之页面跳转和加载外部页面的实现
Dec 04 Javascript
JS原型、原型链深入理解
Feb 27 Javascript
利用JS实现点击按钮后图片自动切换的简单方法
Oct 24 Javascript
Javascript之面向对象--封装
Dec 02 Javascript
AngularJS遍历获取数组元素的方法示例
Nov 11 Javascript
浅析vue中常见循环遍历指令的使用 v-for
Apr 18 Javascript
js实现计算器功能
Aug 10 Javascript
Ajax获取node服务器数据的完整步骤
Sep 20 Javascript
元素全屏的设置与监听实例
Nov 28 #Javascript
vue2实现数据请求显示loading图
Nov 28 #Javascript
Vue2仿淘宝实现省市区三级联动
Apr 15 #Javascript
ligerUI---ListBox(列表框可移动的实例)
Nov 28 #Javascript
基于vue2实现上拉加载功能
Nov 28 #Javascript
微信小程序模板和模块化用法实例分析
Nov 28 #Javascript
基于Vue的移动端图片裁剪组件功能
Nov 28 #Javascript
You might like
ThinkPHP中实例Model方法的区别说明
2010/08/21 PHP
基于PHP的简单采集数据入库程序【续篇】
2014/07/30 PHP
PHP数组中头部和尾部添加元素的方法(array_unshift,array_push)
2017/04/10 PHP
laravel框架中间件 except 和 only 的用法示例
2019/07/12 PHP
菜鸟javascript基础整理1
2010/12/06 Javascript
JavaScript数组去重的3种方法和代码实例
2015/07/01 Javascript
JS高级运动实例分析
2016/12/20 Javascript
Javascript 一些需要注意的细节(必看篇)
2017/07/08 Javascript
微信小程序 五星评分的实现实例
2017/08/04 Javascript
vue使用axios时关于this的指向问题详解
2017/12/22 Javascript
React实现类似淘宝tab居中切换效果的示例代码
2020/06/02 Javascript
如何HttpServletRequest文件对象并储存
2020/08/14 Javascript
vue中axios封装使用的完整教程
2021/03/03 Vue.js
Python实现SVN的目录周期性备份实例
2015/07/17 Python
Python socket实现简单聊天室
2018/04/01 Python
Python变量赋值的秘密分享
2018/04/03 Python
python验证码识别教程之滑动验证码
2018/06/04 Python
Pandas 按索引合并数据集的方法
2018/11/15 Python
python实现两个经纬度点之间的距离和方位角的方法
2019/07/05 Python
Java文件与类动手动脑实例详解
2019/11/10 Python
pytorch的batch normalize使用详解
2020/01/15 Python
python检查目录文件权限并修改目录文件权限的操作
2020/03/11 Python
Python脚本调试工具安装过程
2021/01/11 Python
美国儿童珠宝在线零售商:Loveivy
2019/05/22 全球购物
华为C++笔试题
2014/08/05 面试题
仓库门卫岗位职责
2013/12/22 职场文书
四风问题自查报告剖析材料
2014/02/08 职场文书
人力资源主管职责范本
2014/03/05 职场文书
学校督导评估方案
2014/06/10 职场文书
大学同学会活动方案
2014/08/20 职场文书
三问三解心得体会
2014/09/05 职场文书
2015年党务公开工作总结
2015/05/19 职场文书
普希金诗歌赏析(6首)
2019/08/22 职场文书
红灯733-1型14管5波段半导体收音机
2021/04/22 无线电
关于CentOS 8 搭建MongoDB4.4分片集群的问题
2021/10/24 MongoDB
instantclient客户端 连接oracle数据库
2022/04/26 Oracle