vue 中控制v-html 中的样式,但不影响全局的小技巧

9,337 阅读2分钟

我们知道在 Vue 中,style可以用两种方式来导入:

// method1
@import ('./a.css');

//method2
<style src='./a.css'>

但是不管哪一种,导进的css文件都是应用于全局的,有时候我们不想要这样的效果。而要使得css只对当前的组件有效,即局部应用。vue中提供了scoped属性,可以很好地解决该问题。 当我们有如下应用场景时,新的问题又出现了:

<template>
  <div class="markDiv" v-html="mhtml">
  </div>
</template>

<script>
export default {
  data () {
    return {

    }
  },
  props: {
    mhtml: String
  }
}
</script>

<style scoped>
	// some stylesheets to imploy to v-html
</style>

该组件中的style如果设置为scoped,那么style不会应用到 v-html中,如果不设置为scoped,那么会污染全局变量,导致不可知的后果。 如何fix呢?官网给出了两个解决办法:

In single-file components, scoped styles will not apply to content inside v-html, because that HTML is not processed by Vue’s template compiler. If you want to target v-html content with scoped CSS, you can instead use CSS modules or an additional, global <style> element with a manual scoping strategy such as BEM.

对于方法一,css module是一个解决全局变量和代码依赖的规范,原理是对声明了module的样式表中的样式赋予哈希class名, 可以参考 官网以及阮一峰老师的文章

对于方法二,意思是在全局变量中,通过特殊的命名(比如说BEM)来手动约束样式显示

可惜,以上都不是我想要的解决方案 在Stack Overflow中找到了第三种解:

可以使用 deep scoped 来实现对v-html的样式应用,并且不设置为全局

代码如下:

<template>
  <div class="markDiv" v-html="mhtml">
  </div>
</template>

<script>
export default {
  data () {
    return {

    }
  },
  props: {
    mhtml: String
  }
}
</script>

<style scoped>
.markDiv>>>h1,h2,h3,h4,h5,h6{
  font-family: "PT Sans","SF UI Display", ".PingFang SC","PingFang SC", "Neue Haas Grotesk Text Pro", "Arial Nova", "Segoe UI", "Microsoft YaHei", "Microsoft JhengHei", "Helvetica Neue", "Source Han Sans SC", "Noto Sans CJK SC", "Source Han Sans CN", "Noto Sans SC", "Source Han Sans TC", "Noto Sans CJK TC", "Hiragino Sans GB", sans-serif;
  text-rendering:optimizelegibility;margin-bottom:1em;font-weight:bold; line-height: 1.8rem;
}

这样一来, 这些样式就会影响该组件的子组件,其中的v-html自然也会因之而改变,但又不会『污染』全局,很好地fix我们的问题。 那么,原理是什么呢? 我们在vue-loader的release说明 中,发现从v12.2.0开始,加了这么一个特性:

<style scoped> now support "deep" selectors that can affect child components using the >>> combinator: