浅谈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.jstree 增加节点的双击事件代码
Jul 27 Javascript
JavaScript 原型链学习总结
Oct 29 Javascript
javascript截取字符串(通过substring实现并支持中英文混合)
Jun 24 Javascript
几种设置表单元素中文本输入框不可编辑的方法总结
Nov 25 Javascript
JQuery中使用Ajax赋值给全局变量失败异常的解决方法
Aug 18 Javascript
使用jquery动态加载js文件的方法
Dec 24 Javascript
JavaScript常用函数工具集:lao-utils
Mar 01 Javascript
使用javascript插入样式
Mar 14 Javascript
node.js 动态执行脚本
Jun 02 Javascript
喜大普奔!jQuery发布 3.0 最终版
Jun 12 Javascript
微信小程序云开发 生成带参小程序码流程
May 18 Javascript
vue + el-form 实现的多层循环表单验证
Nov 25 Vue.js
元素全屏的设置与监听实例
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
php 变量定义方法
2009/06/14 PHP
sphinx增量索引的一个问题
2011/06/14 PHP
基于Snoopy的PHP近似完美获取网站编码的代码
2011/10/23 PHP
thinkphp缓存技术详解
2014/12/09 PHP
php常用字符串String函数实例总结【转换,替换,计算,截取,加密】
2016/12/07 PHP
JavaScript/jQuery 表单美化插件小结
2012/02/14 Javascript
获取服务器传来的数据 用JS去空格的正则表达式
2012/03/26 Javascript
json数据与字符串的相互转化示例
2013/09/18 Javascript
再谈JavaScript线程
2015/07/10 Javascript
分享自己用JS做的扫雷小游戏
2016/02/17 Javascript
Extjs4.0 ComboBox如何实现三级联动
2016/05/11 Javascript
js获取Html元素的实际宽度高度的方法
2016/05/19 Javascript
自己动手制作基于jQuery的Web页面加载进度条插件
2016/06/03 Javascript
微信小程序开发之实现选项卡(窗口顶部TabBar)页面切换
2016/11/25 Javascript
详解JavaScript中this的指向问题
2017/01/20 Javascript
Bootstrap 响应式实用工具实例详解
2017/03/29 Javascript
vue.js 微信支付前端代码分享
2018/02/10 Javascript
安装vue-cli的简易过程
2018/05/22 Javascript
Vue Element 分组+多选+可搜索Select选择器实现示例
2018/07/23 Javascript
axios简单实现小程序延时loading指示
2018/07/30 Javascript
详解javascript appendChild()的完整功能
2018/08/18 Javascript
[01:05:12]2014 DOTA2国际邀请赛中国区预选赛 TongFu VS CIS-GAME
2014/05/21 DOTA
深入讨论Python函数的参数的默认值所引发的问题的原因
2015/03/30 Python
python中尾递归用法实例详解
2015/04/28 Python
Django使用redis缓存服务器的实现代码示例
2019/04/28 Python
HTML5之HTML元素扩展(下)—增强的Form表单元素值得关注
2013/01/31 HTML / CSS
100%植物性、有机、即食餐:Sakara Life
2018/10/25 全球购物
家得宝官网:The Home Depot(全球最大的家居装饰专业零售商)
2018/12/17 全球购物
酒店管理毕业生自荐信
2013/10/24 职场文书
事业单位公务员的职业生涯规划
2014/01/15 职场文书
党的群众路线教育实践活动对照检查材料(教师)
2014/09/24 职场文书
质量保证书怎么写
2015/02/27 职场文书
违规违纪检讨书范文
2015/05/06 职场文书
Mysql效率优化定位较低sql的两种方式
2021/05/26 MySQL
vue项目配置sass及引入外部scss文件
2022/04/14 Vue.js
源码安装apache脚本部署过程详解
2022/09/23 Servers