36

在项目中使用 vue-awesome-swiper 遇到的问题

 4 years ago
source link: https://chorer.github.io/2020/03/08/F-在项目中使用 vue-awesome-swiper遇到的问题/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

IjEVRfj.jpg!web

问题复现

最近做的商城项目需要在首页展示一个轮播图,秉承着“有现成轮子就绝不自己写”(其实是懒和菜)的想法,在网上搜索了一下,最后选择使用 vue-awesome-swiper 。安装和使用就不说了,可以直接看 GitHub 的文档。按照文档写完基础结构后,实际使用的时候遇到了几个问题:

  • 图片轮播到最后一张时自动停止,无法循环播放

  • 分页器不显示

  • 无法修改分页器样式

前两个其实是一样的问题,我这里轮播图的数据放在 banners 里,一开始没赋值的时候默认是空数组,没有数据,所以这时候会导致轮播图运行不正常。解决这个问题只需要在 swiper 加一个判断就好: v-if="banners.length != 0" 。第三个问题则是因为 style 标签使用了 scoped 声明,导致无法修改第三方组件库的样式。之前写单文件组件都是习惯性地加 scoped ,没有考虑太多。这次遇到了问题,所以花时间研究了一下这个东西,然后查找了一些合适的解决方案,在这里做一个记录。

为什么需要 scoped

官方文档的介绍: https://vue-loader.vuejs.org/guide/scoped-css.html

为了实现样式的模块化、私有化,防止全局样式污染,我们可以给单文件组件中的 style 标签添加 scoped 属性,这样,里面书写的 CSS 样式就只能应用于当前的组件。其原理是通过 PostCSS 实现的,经过编译后, template 中手写的元素会与 style 中的样式通过自定义属性 data-v-xxx 形成对应。也就是说,我们写的样式实际上只对 具有对应自定义属性的元素 生效,而由于这样的元素只存在于组件模板中,所以确保了样式只对当前组件生效。例如:

// test.vue
<template>
    <div class="test">
        <a><img></a>
    </div>
</template>
<style scoped>
    div img{
        width:100%;
    }
</style>

上面的代码经过编译后,实际上会变成:

// test.vue
<template>
    <div class="test" data-v-1a0c5ce5>
        <a data-v-1a0c5ce5>
            <img data-v-1a0c5ce5>
        </a>
    </div>
</template>
<style scoped>
    div img[data-v-1a0c5ce5]{
        width:100%;
    }
</style>

如果没有加 scoped ,那么宽度 100% 这个样式会作用于所有的图片;由于使用了 scoped ,所以这个样式只对当前组件生效。

同样的,如果是父子组件:

// 父组件
<template>
    <div class="parent">
      <son>
          <span>我是插槽</span>  
      </son>
    </div>
</template>

<style scoped>
    .parent .son{
        border:2px solid red
    }
    .parent .son h1{
        color: green
    }
    span{
        color:yellow
    }
</style>

//子组件
<template>
    <div class="son">
          <h1>我是子组件</h1>
        <slot></slot>
    </div>
</template>

我们会发现,可以在父组件中修改子组件根元素( div.son )以及子组件插槽的样式,但是无法修改子组件子元素( h1 )的样式。在控制台中可以看到,代码编译后是这样的:

<div class="parent" data-v-1a0c5ce5>
    <div class="son" data-v-1a0c5ce5>
        <h1>我是子组件</h1>
        <span data-v-1a0c5ce5>我是插槽</span>
    </div>
</div>

<style>
    .parent .son[data-v-1a0c5ce5]{
        border:2px solid red
    }
    h1{
        color: green
    }
    span[data-v-1a0c5ce5]{
        color:yellow
    }
</style>

这是因为,不管是子组件根元素还是子组件的插槽,最终都是实际书写在父组件的 template 中的,所以父组件中书写的样式能够对应地在父组件模板中找到 DOM;而子组件的子元素(比如上面的 h1 ),它实际上是 在子组件模板书写 的,此时没法通过自定义属性建立样式与 DOM 的对应关系。因此这个样式不生效,这样也就防止了在父组件的层面上修改子组件的样式。

回到一开始的问题

再回到一开始的问题,轮播图的结构大概是这样的:

<template>
  <swiper>  
      <swiper-slide v-for="item in banners">  
           <!--渲染部分-->
      </swiper-slide>  
      <div class="swiper-pagination" v-for="item in banners" slot="pagination"></div>  
  </swiper>  
</template>

div.swiper-pagination 只是包裹每个圆点 span.swiper-pagination-bullets 的容器,实际上没有在 template 中直接书写 span.swiper-pagination-bullets 。这里我看了下源文件,还是没有找到这几个圆点是怎么来的,但可以肯定是动态生成的,所以猜测可能是组件样式的 scoped 为样式和 DOM 建立对应关系的时候,此时这些圆点还没有生成,也就是说,圆点“错过了”添加自定义属性的过程。当然这个只是猜测,如果有大佬知道这里面的具体过程,也欢迎在下面评论区指出。

如何修改第三方组件库的样式

虽然 scoped 可以防止全局样式污染,但是给我们修改第三方组件库的样式带来了困难 —— 就像上面的问题一样,这些第三方插件通常都是项目中的子组件,而我们又需要根据项目需求修改组件样式。那么怎么办呢?这里记录一些 可能的 解决方案:

  • 去掉 scoped :破坏样式的封装,不推荐

  • App.vue 中书写全局样式,不推荐,理由同上

  • 新建一个 css 样式文件,在里面书写需要覆盖的样式,并在 main,js 引入该文件

  • 同时使用带 scoped 与不带 scopedscript 标签,在后者书写需要覆盖的样式

  • 使用 深度选择器 实现样式穿透:

    对于普通 CSS 或者 stylus,使用 >>> ;对于 sass 和 less,使用 /deep/ 。以上面代码为例,如果要实现样式穿透,可以这么写:

<style scoped>
    .swiper-pagination >>> .swiper-pagination-bullet-active{
      background-color: var(--color-tint);
    }
    /* 或者是
    .swiper-pagination /deep/ .swiper-pagination-bullet-active{
      background-color: var(--color-tint);
    }
    */
</style>

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK