问题:在我们页面开发时,需要修改样式。样式有的是全局公有样式,有的是局部样式。有时需要公有样式进行局部样式调整,那么如何灵活的调整样式呢?

一、Vue中使用scoped属性

1. scoped属性原理

1、在style标签中添加scoped属性,css只作用于当前组件中的元素,形成独立作用域,使样式私有化。
2、在加上scoped后,会为DOM节点自动添加一个唯一的属性data-v-hash(hash是随机的哈希值),以保证其唯一性。同时在相应的css选择器末尾,也加上了当前组件的data-v-hash属性,来使其私有化。

在这里插入图片描述

1.正常代码
<template>
<div class="example" >hi</div>
</template>
<style scoped>
.example{
color:red;
}
</style>

2.编译后的代码
<template>
<div class="example" data-v-f3f3eg9>hi</div>
</template>
<style>
.example[data-v-f3f3eg9]{
color:red;
}
</style>

2. scoped渲染规则

1、 给HTML的dom节点添加一个不重复的data属性来唯一标识这个dom 元素
2、在每句css选择器的末尾(编译后生成的css语句)加一个当前组件的data属性选择器(例如:[data-v-5558831a])来私有化样式
3、如果组件内部包含有其他组件,只会给其他组件的最外层标签加上当前组件的data属性

3. 穿透scoped属性
使用了scoped后,尽管实现组件样式的私有化,但在我们实际的项目中,在很多地方使用重复的子组件或其他的样式库时,在个别地方需要微调样式,这个时候不能直接改子组件样式,而且在父组件里的样式又不能渗透到子组件去。

一个子组件的根节点会同时受其父组件的 scoped CSS 和子组件的 scoped CSS 的影响。这样设计是为了让父组件可以从布局的角度出发,调整其子组件根元素的样式。

当我们希望 父组件scoped 样式中的一个选择器能够作用得“更深”,影响子组件,可以使用**穿透(>>>、/deep/、::v-deep)**方法,编译后会在相应的选择器后面增加独有的属性;如下:

一、编译前
1.没有使用预编译器 使用>>>
<style scoped>
.a >>> .b { /* ... */ }
</style>

2.使用预编译器 less、Sass 、scss
<style lang="scss" scoped>
-----------------::v-deep------------
/*用法1*/
.a{
 ::v-deep .b { 
  /* ... */
 }
} 
/*用法2*/
.a ::v-deep .b {
  /* ... */
}

---------------------/deep/---------
/*用法1*/
.a {
 /deep/ .b { 
  /* ... */
 }
} 
/*用法2*/
.a /deep/ .b { 
  /* ... */
 }
</style>

vue-cli3以上版本不可以使用/deep/

二、编译后
/*编译后*/
.a[data-v-f3f3eg9] .b { /* ... */ }
</style>

4. 穿透演示
1.父组件App.vue

<template>
    <div class="container">
        <h3 class="title">Hello >Vue</h3>
        <card/>
    <div>
</template>
<script>
import Card from './Card.vue';
export default {
    components: { Card }
}
</script>
<style scoped>
.container { /* ... */ }
.title { /* ... */ }
.card { /* ... */ }
</style>

2.子组件Card.vue

<template>        
    <div class="title">Vue Cli</div>
        <div class="list">
            <div class="item">vue serve</div>
            <div class="item">vue build</div>
        </div>
    </div>
</template>
<style scoped>
    .card { /* ... */ }
    .title { /* ... */ }
    .item { /* ... */ }
</style>

3.编译后

<div data-v-4fe14a3c="" class="container">
    <h3 data-v-4fe14a3c="" class="title">Hello Vue</h3>
    // 子组件
    <div data-v-119ff0e6="" data-v-4fe14a3c="" class="card">
        <div data-v-119ff0e6="" class="title">Vue Cli</div>
        <div data-v-119ff0e6="" class="list">
            <div data-v-119ff0e6="" class="item">vue serve</div>
            <div data-v-119ff0e6="" class="item">vue build</div>
        </div>
    </div>
</div>

//样式
<style>
.card[data-v-119ff0e6] { /* ... */ }
.title[data-v-119ff0e6] { /* ... */ }
.item[data-v-119ff0e6]{ /* ... */ }
.container[data-v-4fe14a3c]{ /* ... */ }
.container .title[data-v-4fe14a3c] { /* ... */ }
.card[data-v-4fe14a3c] { /* ... */ }
</style>

4.在父组件中直接修改子组件中(非最外层)元素的样式

.card .title {
    color: #eee;
}

编译后

<div data-v-7a169200="" class="container">
    <h3 data-v-7a169200="" class="title">Hello Vue</h3>
    <div data-v-119ff0e6="" data-v-7a169200="" class="card">
        <div data-v-119ff0e6="" class="title">Vue Cli</div>
        <div data-v-119ff0e6="" class="list">
            <div data-v-119ff0e6="" class="item">vue serve</div>
            <div data-v-119ff0e6="" class="item">vue build</div>
        </div>
    </div>
</div>

<style>
.card[data-v-119ff0e6] { /* ... */ }
.title[data-v-119ff0e6] { /* ... */ }
.item[data-v-119ff0e6]{ /* ... */ }
.container[data-v-4fe14a3c]{ /* ... */ }
.container .title[data-v-4fe14a3c] { /* ... */ }
.card[data-v-4fe14a3c] { /* ... */ }

.card .title[data-v-7a169200] {
    color: #eee
}
//应该为:
//.card[data-v-dba577b2] .title {
//  color: #eee
//}
</style>

在父组件内编写的样式经编译后都会带上和父组件元素一样的data-v-hashxxxx,只对父组件范围内的元素起作用,触及不到引入的子组件的内部,也就是说无法覆盖子组件的样式。
5.使用穿透

.card >>> .title {
    color: #eee;
}
.container .card >>> .list .item {
    color: #FF6347;
}

6.这次起作用了

<div data-v-dba577b2="" class="container">
    <h3 data-v-dba577b2="" class="title">Hello Vue</h3>
    <div data-v-119ff0e6="" data-v-dba577b2="" class="card">
        <div data-v-119ff0e6="" class="title">Vue Cli</div>
        <div data-v-119ff0e6="" class="list">
            <div data-v-119ff0e6="" class="item">vue serve</div>
            <div data-v-119ff0e6="" class="item">vue build</div>
        </div>
    </div>
</div>

<style>
.card[data-v-119ff0e6] { /* ... */ }
.title[data-v-119ff0e6] { /* ... */ }
.item[data-v-119ff0e6] { /* ... */ }
.container[data-v-dba577b2] { /* ... */ }
.container .title[data-v-dba577b2] { /* ... */ }
.card[data-v-dba577b2] { /* ... */ }

.card[data-v-dba577b2] .title {
    color: #eee
}
.container .card[data-v-dba577b2] .list .item {
    color: tomato
}
</style>

可以看到,使用样式穿透后编译后没有在选择器末尾添加data-v-hashxxxx属性,而是把data-v-hashxxxx添加到了>>>的位置,这样就能够选中子组件中的元素了。

二、问题解决

①为什么使用>>>不起作用?
因为这个项目使用的scss,所以无法正常解析>>>,所以在父组件没有渗透下去。


②为什么我使用了/deep/却没有成功的在手机上将样式修改过来?
用了两个/deep/,一个不编译,阻塞错误

/*错误代码:写了两次/deep */
.pop-content {
  /deep/ .input-wrapper /deep/  .form-item {
      ...
  } 
 }
 //换后在浏览器看到的代码,发现第二个是无法转换的。
 //.pop-content[data-v-b93cf8e0] .input-wrapper /deep/ .form-item {}

/*修改后的代码*/
.pop-content {  
     .input-wrapper /deep/  .form-item {  
    }
  }/*编译转换后的代码*/
 .pop-content .input-wrapper[data-v-b93cf8e0] .form-item 

③随之而来第三个问题,我只写一个/deep/的话,我写在哪个位置呢,是不是写在哪都行,它们之间有什么区别。
1、/deep/加在那个class之后就在其后面添加相应唯一的data-v属性值。
2、注意应该放在子组件外层,不然没起到穿透组件私有域的作用
3、如果写在父组件整体样式的前面,则父组件里的样式都可以穿透子组件私有域,并且去影响自组件样式。

三、Sass/Scss与Less区别

1、Sass (Syntactically Awesome Stylesheets)是一种动态样式语言,Sass语法属于缩排语法,比css比多出好些功能(如变量、嵌套、运算,混入(Mixin)、继承、颜色处理,函数等),更容易阅读。
2、Sass的缩排语法,对于写惯css前端的web开发者来说很不直观,也不能将css代码加入到Sass里面,因此Sass语法进行了改良,Sass 3就变成了Scss(Sassy CSS)。SCSS(Sassy CSS)是CSS语法的扩展。这意味着每一个有效的CSS也是一个有效的SCSS语句,与原来的语法兼容,只是用{}取代了原来的缩进。
3、Less也是一种动态样式语言. 对CSS赋予了动态语言的特性,如变量,继承,运算, 函数. Less 既可以在客户端上运行 (支持IE 6+, Webkit, Firefox),也可在服务端运行 (借助 Node.js)。

参考链接:
https://www.cnblogs.com/songForU/p/11176696.html
https://www.cnblogs.com/wangpenghui522/p/5467560.html

Logo

前往低代码交流专区

更多推荐