Vue2 v-for循环渲染v-html时v-model更新内容不同步问题
问题原因
渲染不更新的核心是响应式关联断裂:
- 组件初始化执行
data()函数时,statements数组中每个项的text属性是直接用当时的this.name值拼接出的静态字符串,初始化完成后,这个字符串和name属性就没有任何绑定关系了 - 后续
name被v-model更新时,statements里的text不会自动重新计算,v-html绑定的是固定不变的静态字符串,自然不会同步更新 - 额外说明:示例代码把
name定义在props里,却直接通过v-model修改,违反Vue单向数据流规则,会抛出控制台警告,组件内部状态应该定义在data中。
解决方案
最简洁的实现方式是用计算属性动态生成渲染列表,计算属性会自动收集依赖的响应式变量,变量变化时自动重算返回值,完全适配当前场景:
- 把数组里的HTML内容改成带占位符的静态模板,初始化时不要拼接动态值
- 新增计算属性,遍历模板列表,把当前最新的动态变量替换到占位符位置,生成最终要渲染的HTML字符串
- v-for直接遍历计算属性返回的列表即可
修正后的完整代码如下:
模板部分
<div id="app"> <h2>Testing</h2> <ul> <!-- 遍历计算属性返回的渲染列表,记得加唯一key --> <li v-for="statement in renderedStatements" v-html="statement.text" :key="statement.id"></li> </ul> <input v-model="name" placeholder="edit name"> <p>Name is: {{ name }}</p> <p class="italic">Outside loop: <b>{{name}}</b> likes dogs.</p> </div>
实例代码
new Vue({ el: "#app", data: function() { return { // 内部状态移到data中,避免直接修改props name: 'Bob', statements: [ { id: 'food', // 用{name}作为动态值的占位符,多变量同理加不同占位符即可 template: '<b>{name}</b> likes to eat ice cream.' }, { id: 'fun', template: 'Running is the favorite activity of <b>{name}</b>' } ] } }, computed: { renderedStatements() { // 自动依赖name,name变化时自动重跑该逻辑 return this.statements.map(item => ({ id: item.id, text: item.template.replaceAll('{name}', this.name) // 多变量替换就在这里链式调用replaceAll,替换对应占位符即可 })) } } })
注意事项
v-html渲染的内容不会经过Vue模板编译,不要在传入的HTML字符串里写{{ }}插值、v-model等Vue语法,写了也不会生效,所有动态内容必须提前在JS侧替换为最终文本- 由于项目同时使用Laravel Blade模板,注意Blade默认的
{{ }}插值和Vue插值语法冲突,Vue相关的插值前面加@前缀写成@{{ name }},Blade编译时会自动跳过带@的插值,交给Vue处理 v-html存在XSS风险,确保渲染的HTML字符串来自可信来源,不要直接渲染未经过滤的用户提交内容
内容的提问来源于stack exchange,提问作者jumki
相关产品推荐
相关产品推荐

