在Vue项目开发过程中,我们经常会遇到需要可编辑富文本区域的场景,原生的textarea组件仅支持纯文本输入,无法满足复杂的富文本编辑需求,而contenteditable属性的div元素可以支持富文本内容的编辑,但是Vue的v-model指令默认只适配input、textarea等表单元素,无法直接和contenteditable div实现双向绑定,需要我们手动封装实现对应的逻辑。

实现核心思路
要实现v-model和contenteditable div的双向绑定,核心需要解决两个问题:一是当div内容被用户编辑时,将最新的内容同步到组件的绑定值中;二是当组件的绑定值发生变化时,更新div的显示内容。我们可以通过自定义组件的v-model逻辑来实现这两个同步过程。
v-model在组件中的工作原理
Vue组件上的v-model本质上是一个语法糖,默认会传递一个value prop,同时监听组件的input事件,当组件触发input事件并传递参数时,就会更新绑定的值。对于contenteditable div,我们需要手动实现这个prop的接收和事件的触发逻辑。
内容同步的关键事件
contenteditable div的内容变化不会触发原生的input事件,我们需要监听input事件(部分浏览器支持)或者blur、keyup等事件来捕获内容变化,在事件回调中获取div的最新内容并触发组件的input事件。
完整实现代码示例
下面是一个完整的Vue组件示例,实现了v-model和contenteditable div的双向绑定:
// EditableDiv.vue 组件代码
export default {
name: 'EditableDiv',
props: {
// 接收v-model绑定的值
value: {
type: String,
default: ''
}
},
data() {
return {
// 内部维护的div内容,避免直接修改prop
innerContent: this.value
}
},
watch: {
// 监听外部绑定值的变化,更新div内容
value(newVal) {
// 只有当新值和当前div内容不一致时才更新,避免循环触发
if (newVal !== this.$refs.editDiv.innerHTML) {
this.$refs.editDiv.innerHTML = newVal
}
}
},
mounted() {
// 初始化div的内容
this.$refs.editDiv.innerHTML = this.value
},
methods: {
// 处理div内容变化的回调
handleInput() {
const content = this.$refs.editDiv.innerHTML
// 触发input事件,更新v-model绑定的值
this.$emit('input', content)
}
},
template: `
<div
ref="editDiv"
contenteditable="true"
@input="handleInput"
@blur="handleInput"
style="border: 1px solid #ccc; min-height: 100px; padding: 8px;"
></div>
`
}
组件使用方式
在父组件中可以直接通过v-model使用该组件:
<template>
<div>
<editable-div v-model="richTextContent"></editable-div>
<p>当前绑定的内容:{{ richTextContent }}</p>
</div>
</template>
<script>
import EditableDiv from './EditableDiv.vue'
export default {
components: {
EditableDiv
},
data() {
return {
richTextContent: '<p>初始富文本内容</p>'
}
}
}
</script>
注意事项
- 直接修改
innerHTML可能会导致光标位置丢失,如果需要在内容更新时保留光标,需要额外处理光标逻辑,比如记录光标位置后再恢复。 - 如果不需要富文本,只需要纯文本编辑,可以将获取内容的方式从
innerHTML改为innerText或者textContent,避免用户粘贴富文本带入多余样式。 - 部分旧版本浏览器可能不支持contenteditable div的
input事件,此时可以只监听blur事件,在元素失去焦点时同步内容,不过实时性会稍差一些。 - 不要在
handleInput方法中直接修改prop,Vue中prop是单向流动的,所有修改都需要通过触发事件让父组件更新。
扩展优化方向
如果需要更完善的富文本编辑功能,可以在此基础上扩展,比如添加工具栏支持加粗、斜体、插入图片等功能,这些操作本质上都是修改div的innerHTML内容,然后触发input事件同步到v-model绑定值中。另外也可以结合document.execCommand方法实现更多编辑操作,不过需要注意该方法的兼容性问题。
Vuev-modelcontenteditable双向绑定自定义组件修改时间:2026-07-03 02:45:27