解决 v-html 元素中标签样式失效 - Vue

最近在做一个项目,是对富文本编辑器生成的 HTML 渲染到页面中,大家都会想到当然是用 Vue 的 v-html 属性,于是我写下了这样的代码

<template>
  <div class="content-html" v-html="article.contentHtml" />
</template> 


<style lang="scss" scoped>
.content-html {
    p {
      font-size: 16px;
    }
    a {
      color: blue;
    }
  }
</style>

然而 style 并没有生效


解决方法 一

去掉 <style> 标签中的 scoped,但是这个方法并不建议,取消了 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.

在单文件组件中,带有 scoped 属性的 styles 不会作用于 v-html 里的内容,因为 v-html 里面的内容,并没有经过 Vue's template compiler 的处理。

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

官方建议的解决方法二,意思是在全局变量中,通过特殊命名 (比如 BEM) 来手动约束样式 - BEM书写规范

可惜以上都不是我想要的解决办法,最终我在 Stack Overflow 中找到了另一种解决思路

另一种解决思路

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

deep 选择器在 CSS 的写法是 >>>

<template>
  <div class="content-html" v-html="article.contentHtml" />
</template> 


<style scoped>
.content-html {
    >>> p {
      font-size: 16px;
    }
    >>> a {
      color: blue;
    }
  }
</style>

可惜 >>> 在 sass/less 中不作用

我们需要用 /deep/ 来代替 >>> 写法

<style lang="scss" scoped>
.content-html {
    /deep/ p {
      font-size: 16px;
    }
    /deep/ a {
      color: blue;
    }
  }
</style>

如果没解决问题?

由于我的项目需要后端渲染,我使用了 nuxt.js 框架,不知道什么原因,>>>/deep/ 都不起作用

最后的救命稻草 ::v-deep

<style lang="scss" scoped>
.content-html {
    ::v-deep p {
      font-size: 16px;
    }
    ::v-deep a {
      color: blue;
    }
  }
</style>

这样一来,这些样式就会影响到该组件的子组件,v-html 中的内容也因此而改变,但又不会 污染 全局

编辑于 2021-04-02 10:16