
通过上面官方文档的介绍我们可以得知两个重点:
① 通过v-html生成的页面元素,不会被当做Vue模板进行编译,只会作为普通的html代码被插入,也就是说通过v-html插入的html代码中,如果包含vue的语法,例如:@click、v-if等,则不会生效,因为他们没有被Vue编译,而浏览器并不会识别这些vue语法,所以这些语法都不生效。
② 在.vue文件中如果style标签上增加了scoped属性,那么该标签内的样式就不会对v-html生成的页面元素起作用,还是因为这些元素没有被当做Vue模板进行编译,所以不生效。
如果在某些业务场景下我们给v-html生成的页面元素绑定了@click事件,需要触发某些处理方法,或者需要给这些元素设置CSS样式时,这两个问题就比较致命了,所以我们要想办法去解决这俩问题。
解决绑定事件失效方法有:使用onclick等原生事件代替@click等事件vue事件、利用事件委托触发事件、使用component模板代替v-html。
解决样式不生效的方法有:/deep/、::v-deep、>>> 、:deep、额外的全局。
onclick等原生事件代替@click等vue事件思路: vue事件之所以不能被触发是因为,v-html生成的页面元素,是在Vue文件编译之后插入到页面中的,不会被Vue编译,只会作为普通的html代码被插入,所以无法识别vue事件。既然是普通的html代码,那我们可以使用原生的事件去代替vue事件,原生事件一定会被触发,但此时又出现一个新问题,那就是原生事件被触发后,无法访问到vue实例的data中的数据和methods中的方法,究其原因还是由于没有被vue编译的问题。
既然问题出现了,那就想办法解决问题。既然访问不到vue实例中的数据和方法,那么我们可以将事件所需要使用的数据和方法,挂载在window对象上,这样就可以被原生事件访问到了。
<template>
<div class="hello">
<div class="box" v-html="html">
div>
div>
template>
<script>
export default {
data() {
return {
// 富文本数据 将原来的@click改为使用onclick
html: '这是v-html渲染的元素 ',
a: '这是个变量'
}
},
mounted() {
// 将vue实例的方法绑定到window对象中去
window.btnClick = this.btnClick
},
methods: {
btnClick() {
alert('点击事件触发成功 + '+ this.a)
}
}
}
script>
调用结果:

结论: 这种方法在一定程度上解决了v-html渲染元素不能触发@click的问题,但同样限制条件也比较多,比如:如果通过v-for同时渲染大量相似数据,无法访问当前循环项的数据,也就无法区分不同循环项的数据。所以这种方法只适用于绑定比较简单的事件处理操作。
思路: 这种方法的原理是将子元素要触发执行的事件绑定到父元素上,点击子元素触发事件将会冒泡到父元素,然后父元素去执行对应的事件处理函数,但是要注意在事件处理函数中通过e.target去判断,触发事件的元素是不是我们想要绑定的那个元素,只有是目标子元素时,才去执行事件处理逻辑。
<template>
<div class="hello">
<div class="box" v-html="html" @click="btnClick($event)">
div>
div>
template>
<script>
export default {
name: 'HelloWorld',
data() {
return {
// 富文本数据 目标子元素为button按钮
html: '这是通过v-html渲染的元素 ',
a: '这是个变量'
}
},
methods: {
btnClick(e) {
// 判断当前触发事件的元素 是不是目标子元素
if(e.target.id === 'btn')
alert('点击按钮事件触发成功 + '+ this.a)
}
}
}
script>

结论: 这种方法比较好,不需要对富文本数据进行修改,也不需要修改window对象,比较推荐。
思路: 前面我们已经得知@click等vue时间之所以不起作用,是因为v-html渲染的数据是在不会被当做Vue模板进行编译,只会作为普通的html代码被插入,那我们就想一种可以被当成Vue模板的渲染方式,那就是通过component模板来进行渲染。需要引入vue,通过Vue.extend(options) 基础Vue构造器,创建一个“子类”,然后通过vm.$mount()将实例进行编译,最后通过原生js进行挂载。
<template>
<div class="hello">
<div>
父组件
<div class='parent' id='parent'>div>
div>
div>
template>
<script>
import Vue from 'vue'
export default {
name: 'HelloWorld',
data() {
return {
a: '这是a变量'
}
},
mounted() {
// 使用变量暂存一下this指向的vue实例
var that = this;
// 通过Vue.extend创建子类组件
var MyComponent = Vue.extend({
template: '这是通过v-html渲染的元素 ',
methods: {
add() {
// 通过暂存的that获取当前实例的数据和方法
alert('触发了点击事件,并访问当时vue实例的变量a: ' + that.a)
that.btnClick()
}
}
})
// 通过 $mount() 将子类组件进行编译
let component=new MyComponent().$mount()
// 通过原生js进行挂载
document.getElementById('parent').appendChild(component.$el);
},
methods: {
btnClick() {
alert('点击按钮触发当时vue实例成功')
}
}
}
script>


结论: 这种方法看起来比较高端,确实也比较好用,但是不适用于大量数据的渲染,因为会创建过多的vue子类,占用大量内存。
请查看:vue 之 CSS进行样式穿透的方法(/deep/、::v-deep、>>> 、:deep、额外的全局<style>)