浅谈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 相关文章推荐
JavaScript中的私有成员
Sep 18 Javascript
Javascript中的arguments与重载介绍
Mar 15 Javascript
全面了解JavaScirpt 的垃圾(garbage collection)回收机制
Jul 11 Javascript
javascript 内置对象及常见API详细介绍
Nov 01 Javascript
jQuery实现锚点向下平滑滚动特效示例
Aug 29 jQuery
基于datepicker定义自己的angular时间组件的示例
Mar 14 Javascript
微信小程序中使用ECharts 异步加载数据的方法
Jun 27 Javascript
基于vue-cli 路由 实现类似tab切换效果(vue 2.0)
May 08 Javascript
重学JS之显示强制类型转换详解
Jun 30 Javascript
Vue路由前后端设计总结
Aug 06 Javascript
JSX在render函数中的应用详解
Sep 04 Javascript
js实现自定义滚动条的示例
Oct 27 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
解决phpmyadmin 乱码,支持gb2312和utf-8
2006/11/20 PHP
基于PHP开发中的安全防范知识详解
2013/06/06 PHP
Linux系统中为php添加pcntl扩展
2016/08/28 PHP
Extjs学习笔记之七 布局
2010/01/08 Javascript
最佳6款用于移动网站开发的jQuery 图片滑块插件小结
2012/07/20 Javascript
javascript实现数字+字母验证码的简单实例
2014/02/10 Javascript
javascript从image转换为base64位编码的String
2014/07/29 Javascript
ajaxFileUpload.js插件支持多文件上传的方法
2014/09/02 Javascript
JavaScript使用循环和分割来替换和删除元素实例
2014/10/13 Javascript
JS JQUERY实现滚动条自动滚到底的方法
2015/01/09 Javascript
谈一谈javascript闭包
2016/01/28 Javascript
JS HTML5拖拽上传图片预览
2016/07/18 Javascript
js 原型对象和原型链理解
2017/02/09 Javascript
Angular 2父子组件之间共享服务通信的实现
2017/07/04 Javascript
实例详解JSON取值(key是中文或者数字)方式
2017/08/24 Javascript
详解基于vue的服务端渲染框架NUXT
2018/06/20 Javascript
详解Vue改变数组中对象的属性不重新渲染View的解决方案
2018/09/21 Javascript
Vue常用的几个指令附完整案例
2018/11/06 Javascript
vue+webpack中配置ESLint
2018/11/07 Javascript
Vue动态面包屑功能的实现方法
2019/07/01 Javascript
VScode格式化ESlint方法(最全最好用方法)
2019/09/10 Javascript
微信小程序pinker组件使用实现自动相减日期
2020/05/07 Javascript
浅谈es6中的元编程
2020/12/01 Javascript
Vue如何跨组件传递Slot的实现
2020/12/14 Vue.js
浅谈Python中数据解析
2015/05/05 Python
Python使用functools模块中的partial函数生成偏函数
2016/07/02 Python
python监控linux内存并写入mongodb(推荐)
2017/09/11 Python
利用Python代码实现一键抠背景功能
2019/12/29 Python
GoPro摄像机美国官网:美国运动相机厂商
2018/07/03 全球购物
工程师自我评价怎么写
2013/09/19 职场文书
国际政治个人自荐信范文
2013/11/26 职场文书
擅自离岗检讨书
2014/02/11 职场文书
国际贸易毕业生求职信范文
2014/02/21 职场文书
十佳文明家庭事迹
2014/05/25 职场文书
目标责任书格式
2014/07/28 职场文书
装修公司管理制度
2015/08/05 职场文书