深入理解vue-cli的组件化开发中scoped的底层原理

118 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第12天,点击查看活动详情

引言

在 vue-cli组件化开发中,有时候注册使用的组件会渲染到index.html中指定的div区域中,这就导致了样式间可能会因重名或等等原因发生样式冲突

接下来我们简单回顾一下组件开发及样式冲突问题。

组件化开发中的样式冲突

我们初始化了一个vue-cli 项目,并在components目录下创建up.vue(上组件),down.vue(下组件)和all.vue

上组件代码

<template>
    <div class="upStyle">
        {{updata}}
    </div>
  </template>
  
  <style lang="less">
    .upStyle {
        width: 300px;
        height: 300px;
        background-color: greenyellow;
    }
  </style>
  
  <script>
    export default {
        data(){
            return {
                updata:"这是上组件"
            }
        }
    }
  </script>

下组件代码

<template>
    <div class="downStyle">
        {{downdata}}
    </div>
  </template>
  
  <style lang="less">
    .downStyle {
        width: 300px;
        height: 300px;
        background-color: rebeccapurple;
    }
  </style>
  
  <script>
    export default {
        data(){
            return {
                downdata:"这是下组件"
            }
        }
    }
  </script>

全局组件代码

<template>
    <div>
        <div class="data">
            {{allData}}
        </div>
    </div>
</template>

<style scoped>
    div {
        color: brown;
    }
    .data {
        width: 60px;
        height: 60px;
    }
</style>

<script>
export default {
    data(){
        return {
            allData:"All组件"
        }
    }
}
</script>

接下来我们在App.vue中注册左右组件

<script>
import  up  from "@/components/up.vue";
import  down  from '@/components/down.vue';
export default {
  components:{
    up,down
  }
}
</script>

上面是注册的核心代码,引入并注册之后就可以将 up 和 down 作为标签在template中使用。

import all from '@/components/all.vue';

Vue.component('MyCount',all);

main.js的全局注册及使用如上,

接下来我们开始先简单解释一下,因为我们这里主要目的是剖析scoped的底层原理,这里我们不详细解释删去scoped和加上的区别。只是在下面附上冲突和解决样式冲突后的对比图。

这是冲突的效果

image.png

可以看到下部分字体依旧是黑色,并没有因全局组件的引入而改变颜色,也就是没有发生样式冲突。

接下来我们删去scoped,看看效果。

image.png

可以看到发生了样式冲突吗,下组件并没有引入这个全局组件all.vue但是同样字体颜色发生了改变。

这就是样式冲突,有时候样式冲突会非常影响我们的开发。

scoped的底层原理

接下来我们不使用scoped,来通过纯粹的css来解决这个问题。

先回忆一下css中几个选择器。

组合 Combinators 名称 语法 说明 示例 直接组合 AB 满足A同时满足B input.error, a.error 后代组合 A B 选中B,如果他是A的子孙 nav a b 在 a里面 只要所有的a标签 出现在 nav下面 不管隔了多少级 都会被选中 亲子组合 A>B 选中B,如果他是A的子元素 section>p 必须直接 兄弟选择器 AB 选中B,如果他在A后且和A同级 h2p 同级 同一个父级 并列 相邻选择器 A+B 选中B,如果他紧跟A后面 h2+p 紧跟着的兄弟选择器

我们简单回顾一下选择器,可能很多聪明的基础很好的同学已经猜到了其中的底层原理。

没错,我们就是通过组合Combinators来选择指定的样式。

从而解决我们的样式冲突问题。

接下来我们开始操作。

接下来我们开始改变我们的all.vue组件

<template>
    <div>
        <div class="data">
            {{allData}}
        </div>
    </div>
</template>

<style>
    .allStyle div {
        color: brown;
    }
    .allStyle .data {
        width: 60px;
        height: 60px;
    }
</style>

<script>
export default {
    data(){
        return {
            allData:"All组件"
        }
    }
}
</script>

改变的部分:

image.png

可以看到,这里我们并没有使用 scoped属性,只是单纯的使用了css的Combinators组合来进行选择,这里我们使用的后代选择器。

up.vue中我们也需要做一点点改动。

代码如下:

<template>
    <div class="upStyle">
        {{updata}}
        <MyCount class="allStyle"></MyCount>来了
    </div>
  </template>
  
  <style lang="less">
    .upStyle {
        width: 300px;
        height: 300px;
        background-color: greenyellow;
    }
  </style>
  
  <script>
    export default {
        data(){
            return {
                updata:"这是上组件"
            }
        }
    }
  </script>

我们重点看一下改变的部分

我们先简单观察一下代码,我们在上组件中引入的全局组件,结合之前的我们做的后代选择器,所以这里我们需要加上之前加的 allStyle

这样我们赋予特定属性的元素才会被选择到,而其他的并不会被选到(受到影响)

image.png

可以看到,我们的额外又加入了allStyle类,让他被我们选择到

那么我们可以发现scoped的底层原理了,每个加入scoped的属性的组件,都会赋予一个独特的,关键词,本质上其实就是css的组合选择器。

这就是scoped的底层原理。