欢迎光临
我们一直在努力

为什么 Vue 中给对象添加新属性后界面不刷新:深入响应式原理与最佳解决方案

一、问题复现:当给对象添加新属性后界面纹丝不动

1.1 现象描述

在日常 Vue 开发中,我们经常会遇到这样的困惑:明明数据已经成功添加到对象上,控制台打印也能看到新属性,但界面就是毫无反应。这就是本文要探讨的核心问题——为什么 Vue 中给对象添加新属性后界面不刷新?

1.2 最小复现示例

下面这段代码可以完美复现该问题:

data() {
return {
user: {
name: '张三',
age: 25
}
}
},
methods: {
addProperty() {
this.user.gender = '男'
console.log(this.user)
}
}

点击按钮后,控制台输出 {name: '张三', age: 25, gender: '男'},但模板中渲染的 {{ user.gender }} 却始终为空。

1.3 问题定位流程

#publish-mermaid-1785343297593-0{font-family:inherit;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#publish-mermaid-1785343297593-0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#publish-mermaid-1785343297593-0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#publish-mermaid-1785343297593-0 .error-icon{fill:#552222;}#publish-mermaid-1785343297593-0 .error-text{fill:#552222;stroke:#552222;}#publish-mermaid-1785343297593-0 .edge-thickness-normal{stroke-width:1px;}#publish-mermaid-1785343297593-0 .edge-thickness-thick{stroke-width:3.5px;}#publish-mermaid-1785343297593-0 .edge-pattern-solid{stroke-dasharray:0;}#publish-mermaid-1785343297593-0 .edge-thickness-invisible{stroke-width:0;fill:none;}#publish-mermaid-1785343297593-0 .edge-pattern-dashed{stroke-dasharray:3;}#publish-mermaid-1785343297593-0 .edge-pattern-dotted{stroke-dasharray:2;}#publish-mermaid-1785343297593-0 .marker{fill:#333333;stroke:#333333;}#publish-mermaid-1785343297593-0 .marker.cross{stroke:#333333;}#publish-mermaid-1785343297593-0 svg{font-family:inherit;font-size:16px;}#publish-mermaid-1785343297593-0 p{margin:0;}#publish-mermaid-1785343297593-0 .label{font-family:inherit;color:#333;}#publish-mermaid-1785343297593-0 .cluster-label text{fill:#333;}#publish-mermaid-1785343297593-0 .cluster-label span{color:#333;}#publish-mermaid-1785343297593-0 .cluster-label span p{background-color:transparent;}#publish-mermaid-1785343297593-0 .label text,#publish-mermaid-1785343297593-0 span{fill:#333;color:#333;}#publish-mermaid-1785343297593-0 .node rect,#publish-mermaid-1785343297593-0 .node circle,#publish-mermaid-1785343297593-0 .node ellipse,#publish-mermaid-1785343297593-0 .node polygon,#publish-mermaid-1785343297593-0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1785343297593-0 .rough-node .label text,#publish-mermaid-1785343297593-0 .node .label text,#publish-mermaid-1785343297593-0 .image-shape .label,#publish-mermaid-1785343297593-0 .icon-shape .label{text-anchor:middle;}#publish-mermaid-1785343297593-0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#publish-mermaid-1785343297593-0 .rough-node .label,#publish-mermaid-1785343297593-0 .node .label,#publish-mermaid-1785343297593-0 .image-shape .label,#publish-mermaid-1785343297593-0 .icon-shape .label{text-align:center;}#publish-mermaid-1785343297593-0 .node.clickable{cursor:pointer;}#publish-mermaid-1785343297593-0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#publish-mermaid-1785343297593-0 .arrowheadPath{fill:#333333;}#publish-mermaid-1785343297593-0 .edgePath .path{stroke:#333333;stroke-width:1px;}#publish-mermaid-1785343297593-0 .flowchart-link{stroke:#333333;fill:none;}#publish-mermaid-1785343297593-0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1785343297593-0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#publish-mermaid-1785343297593-0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1785343297593-0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#publish-mermaid-1785343297593-0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#publish-mermaid-1785343297593-0 .cluster text{fill:#333;}#publish-mermaid-1785343297593-0 .cluster span{color:#333;}#publish-mermaid-1785343297593-0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:inherit;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#publish-mermaid-1785343297593-0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#publish-mermaid-1785343297593-0 rect.text{fill:none;stroke-width:0;}#publish-mermaid-1785343297593-0 .icon-shape,#publish-mermaid-1785343297593-0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1785343297593-0 .icon-shape p,#publish-mermaid-1785343297593-0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#publish-mermaid-1785343297593-0 .icon-shape .label rect,#publish-mermaid-1785343297593-0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1785343297593-0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#publish-mermaid-1785343297593-0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#publish-mermaid-1785343297593-0 .node .neo-node{stroke:#9370DB;}#publish-mermaid-1785343297593-0 [data-look=\”neo\”].node rect,#publish-mermaid-1785343297593-0 [data-look=\”neo\”].cluster rect,#publish-mermaid-1785343297593-0 [data-look=\”neo\”].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297593-0 [data-look=\”neo\”].swimlane.cluster rect{filter:none;}#publish-mermaid-1785343297593-0 [data-look=\”neo\”].node path{stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1785343297593-0 [data-look=\”neo\”].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297593-0 [data-look=\”neo\”].node .neo-line path{stroke:#9370DB;filter:none;}#publish-mermaid-1785343297593-0 [data-look=\”neo\”].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297593-0 [data-look=\”neo\”].node circle .state-start{fill:#000000;}#publish-mermaid-1785343297593-0 [data-look=\”neo\”].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297593-0 [data-look=\”neo\”].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297593-0 :root{–mermaid-font-family:inherit;}

否

是

否

是

给对象添加新属性

是否触发响应式更新

界面不刷新

界面正常更新

排查属性添加方式

是否使用 Vue.set

Object.defineProperty 未劫持新属性

检查属性是否已存在

改用 Vue.set 或 $set

使用其他方案

二、原理剖析:Vue 2 响应式机制的边界与限制

2.1 Object.defineProperty 的工作机制

Vue 2 的响应式系统基于 Object.defineProperty 实现。当我们在 data 中声明一个对象时,Vue 会通过 Object.defineProperty 将对象的所有现有属性转换为 getter/setter,整个过程如下:

#publish-mermaid-1785343297665-1{font-family:inherit;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#publish-mermaid-1785343297665-1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#publish-mermaid-1785343297665-1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#publish-mermaid-1785343297665-1 .error-icon{fill:#552222;}#publish-mermaid-1785343297665-1 .error-text{fill:#552222;stroke:#552222;}#publish-mermaid-1785343297665-1 .edge-thickness-normal{stroke-width:1px;}#publish-mermaid-1785343297665-1 .edge-thickness-thick{stroke-width:3.5px;}#publish-mermaid-1785343297665-1 .edge-pattern-solid{stroke-dasharray:0;}#publish-mermaid-1785343297665-1 .edge-thickness-invisible{stroke-width:0;fill:none;}#publish-mermaid-1785343297665-1 .edge-pattern-dashed{stroke-dasharray:3;}#publish-mermaid-1785343297665-1 .edge-pattern-dotted{stroke-dasharray:2;}#publish-mermaid-1785343297665-1 .marker{fill:#333333;stroke:#333333;}#publish-mermaid-1785343297665-1 .marker.cross{stroke:#333333;}#publish-mermaid-1785343297665-1 svg{font-family:inherit;font-size:16px;}#publish-mermaid-1785343297665-1 p{margin:0;}#publish-mermaid-1785343297665-1 .label{font-family:inherit;color:#333;}#publish-mermaid-1785343297665-1 .cluster-label text{fill:#333;}#publish-mermaid-1785343297665-1 .cluster-label span{color:#333;}#publish-mermaid-1785343297665-1 .cluster-label span p{background-color:transparent;}#publish-mermaid-1785343297665-1 .label text,#publish-mermaid-1785343297665-1 span{fill:#333;color:#333;}#publish-mermaid-1785343297665-1 .node rect,#publish-mermaid-1785343297665-1 .node circle,#publish-mermaid-1785343297665-1 .node ellipse,#publish-mermaid-1785343297665-1 .node polygon,#publish-mermaid-1785343297665-1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1785343297665-1 .rough-node .label text,#publish-mermaid-1785343297665-1 .node .label text,#publish-mermaid-1785343297665-1 .image-shape .label,#publish-mermaid-1785343297665-1 .icon-shape .label{text-anchor:middle;}#publish-mermaid-1785343297665-1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#publish-mermaid-1785343297665-1 .rough-node .label,#publish-mermaid-1785343297665-1 .node .label,#publish-mermaid-1785343297665-1 .image-shape .label,#publish-mermaid-1785343297665-1 .icon-shape .label{text-align:center;}#publish-mermaid-1785343297665-1 .node.clickable{cursor:pointer;}#publish-mermaid-1785343297665-1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#publish-mermaid-1785343297665-1 .arrowheadPath{fill:#333333;}#publish-mermaid-1785343297665-1 .edgePath .path{stroke:#333333;stroke-width:1px;}#publish-mermaid-1785343297665-1 .flowchart-link{stroke:#333333;fill:none;}#publish-mermaid-1785343297665-1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1785343297665-1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#publish-mermaid-1785343297665-1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1785343297665-1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#publish-mermaid-1785343297665-1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#publish-mermaid-1785343297665-1 .cluster text{fill:#333;}#publish-mermaid-1785343297665-1 .cluster span{color:#333;}#publish-mermaid-1785343297665-1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:inherit;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#publish-mermaid-1785343297665-1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#publish-mermaid-1785343297665-1 rect.text{fill:none;stroke-width:0;}#publish-mermaid-1785343297665-1 .icon-shape,#publish-mermaid-1785343297665-1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1785343297665-1 .icon-shape p,#publish-mermaid-1785343297665-1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#publish-mermaid-1785343297665-1 .icon-shape .label rect,#publish-mermaid-1785343297665-1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1785343297665-1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#publish-mermaid-1785343297665-1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#publish-mermaid-1785343297665-1 .node .neo-node{stroke:#9370DB;}#publish-mermaid-1785343297665-1 [data-look=\”neo\”].node rect,#publish-mermaid-1785343297665-1 [data-look=\”neo\”].cluster rect,#publish-mermaid-1785343297665-1 [data-look=\”neo\”].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297665-1 [data-look=\”neo\”].swimlane.cluster rect{filter:none;}#publish-mermaid-1785343297665-1 [data-look=\”neo\”].node path{stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1785343297665-1 [data-look=\”neo\”].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297665-1 [data-look=\”neo\”].node .neo-line path{stroke:#9370DB;filter:none;}#publish-mermaid-1785343297665-1 [data-look=\”neo\”].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297665-1 [data-look=\”neo\”].node circle .state-start{fill:#000000;}#publish-mermaid-1785343297665-1 [data-look=\”neo\”].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297665-1 [data-look=\”neo\”].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297665-1 :root{–mermaid-font-family:inherit;}

initData

observe 数据

遍历对象已有属性

Object.defineProperty

定义 getter

定义 setter

依赖收集

派发更新

2.2 为什么新属性无法被劫持

Object.defineProperty 的核心局限在于:它只能劫持已经存在的属性,无法检测到对象属性的新增或删除。这就解释了为什么 this.user.gender = '男' 后界面不刷新——因为 gender 这个属性在 Vue 初始化时根本不存在,所以没有被转换为响应式属性,自然也就无法触发依赖更新。

2.3 数组的特殊处理方式

数组也有类似问题,直接通过索引修改数组 this.list[0] = newValue 同样不会触发更新。Vue 2 通过重写数组的 7 个变异方法来解决这个问题:

const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
methodsToPatch.forEach(method => {
const original = arrayProto[method]
def(arrayMethods, method, function mutator(…args) {
const result = original.apply(this, args)
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
ob.dep.notify()
return result
})
})

2.4 响应式更新完整流程

理解完整的响应式更新流程,有助于我们从全局视角把握问题:

#publish-mermaid-1785343297725-2{font-family:inherit;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#publish-mermaid-1785343297725-2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#publish-mermaid-1785343297725-2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#publish-mermaid-1785343297725-2 .error-icon{fill:#552222;}#publish-mermaid-1785343297725-2 .error-text{fill:#552222;stroke:#552222;}#publish-mermaid-1785343297725-2 .edge-thickness-normal{stroke-width:1px;}#publish-mermaid-1785343297725-2 .edge-thickness-thick{stroke-width:3.5px;}#publish-mermaid-1785343297725-2 .edge-pattern-solid{stroke-dasharray:0;}#publish-mermaid-1785343297725-2 .edge-thickness-invisible{stroke-width:0;fill:none;}#publish-mermaid-1785343297725-2 .edge-pattern-dashed{stroke-dasharray:3;}#publish-mermaid-1785343297725-2 .edge-pattern-dotted{stroke-dasharray:2;}#publish-mermaid-1785343297725-2 .marker{fill:#333333;stroke:#333333;}#publish-mermaid-1785343297725-2 .marker.cross{stroke:#333333;}#publish-mermaid-1785343297725-2 svg{font-family:inherit;font-size:16px;}#publish-mermaid-1785343297725-2 p{margin:0;}#publish-mermaid-1785343297725-2 .label{font-family:inherit;color:#333;}#publish-mermaid-1785343297725-2 .cluster-label text{fill:#333;}#publish-mermaid-1785343297725-2 .cluster-label span{color:#333;}#publish-mermaid-1785343297725-2 .cluster-label span p{background-color:transparent;}#publish-mermaid-1785343297725-2 .label text,#publish-mermaid-1785343297725-2 span{fill:#333;color:#333;}#publish-mermaid-1785343297725-2 .node rect,#publish-mermaid-1785343297725-2 .node circle,#publish-mermaid-1785343297725-2 .node ellipse,#publish-mermaid-1785343297725-2 .node polygon,#publish-mermaid-1785343297725-2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1785343297725-2 .rough-node .label text,#publish-mermaid-1785343297725-2 .node .label text,#publish-mermaid-1785343297725-2 .image-shape .label,#publish-mermaid-1785343297725-2 .icon-shape .label{text-anchor:middle;}#publish-mermaid-1785343297725-2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#publish-mermaid-1785343297725-2 .rough-node .label,#publish-mermaid-1785343297725-2 .node .label,#publish-mermaid-1785343297725-2 .image-shape .label,#publish-mermaid-1785343297725-2 .icon-shape .label{text-align:center;}#publish-mermaid-1785343297725-2 .node.clickable{cursor:pointer;}#publish-mermaid-1785343297725-2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#publish-mermaid-1785343297725-2 .arrowheadPath{fill:#333333;}#publish-mermaid-1785343297725-2 .edgePath .path{stroke:#333333;stroke-width:1px;}#publish-mermaid-1785343297725-2 .flowchart-link{stroke:#333333;fill:none;}#publish-mermaid-1785343297725-2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1785343297725-2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#publish-mermaid-1785343297725-2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1785343297725-2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#publish-mermaid-1785343297725-2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#publish-mermaid-1785343297725-2 .cluster text{fill:#333;}#publish-mermaid-1785343297725-2 .cluster span{color:#333;}#publish-mermaid-1785343297725-2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:inherit;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#publish-mermaid-1785343297725-2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#publish-mermaid-1785343297725-2 rect.text{fill:none;stroke-width:0;}#publish-mermaid-1785343297725-2 .icon-shape,#publish-mermaid-1785343297725-2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1785343297725-2 .icon-shape p,#publish-mermaid-1785343297725-2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#publish-mermaid-1785343297725-2 .icon-shape .label rect,#publish-mermaid-1785343297725-2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1785343297725-2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#publish-mermaid-1785343297725-2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#publish-mermaid-1785343297725-2 .node .neo-node{stroke:#9370DB;}#publish-mermaid-1785343297725-2 [data-look=\”neo\”].node rect,#publish-mermaid-1785343297725-2 [data-look=\”neo\”].cluster rect,#publish-mermaid-1785343297725-2 [data-look=\”neo\”].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297725-2 [data-look=\”neo\”].swimlane.cluster rect{filter:none;}#publish-mermaid-1785343297725-2 [data-look=\”neo\”].node path{stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1785343297725-2 [data-look=\”neo\”].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297725-2 [data-look=\”neo\”].node .neo-line path{stroke:#9370DB;filter:none;}#publish-mermaid-1785343297725-2 [data-look=\”neo\”].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297725-2 [data-look=\”neo\”].node circle .state-start{fill:#000000;}#publish-mermaid-1785343297725-2 [data-look=\”neo\”].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297725-2 [data-look=\”neo\”].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297725-2 :root{–mermaid-font-family:inherit;}

组件挂载

render 函数执行

读取响应式数据

触发 getter

依赖收集 Dep.depend

建立 Watcher 与 Dep 关系

修改数据

触发 setter

Dep.notify 通知 Watcher

Watcher 执行 update

进入异步更新队列

nextTick 触发 patch

重新渲染 DOM

三、解决方案:让新属性也能触发视图更新

3.1 使用 Vue.set 或 this.$set

这是最推荐的解决方案。Vue.set(target, key, value) 会为新属性添加响应式:

methods: {
addProperty() {
this.$set(this.user, 'gender', '男')
}
}

其内部实现原理简化如下:

function set(target, key, val) {
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
if (key in target && !(key in Object.prototype)) {
target[key] = val
return val
}
const ob = target.__ob__
if (!ob) {
target[key] = val
return val
}
defineReactive(ob.value, key, val)
ob.dep.notify()
return val
}

3.2 替换整个对象

通过创建一个新对象并赋值,可以触发原有属性的 setter,从而实现响应式更新:

methods: {
addProperty() {
this.user = Object.assign({}, this.user, { gender: '男' })
}
}

或使用展开运算符:

methods: {
addProperty() {
this.user = { …this.user, gender: '男' }
}
}

3.3 使用 this.$forceUpdate 的误区

this.$forceUpdate 可以强制组件重新渲染,但它只是一个临时方案,并不推荐作为常规手段:

methods: {
addProperty() {
this.user.gender = '男'
this.$forceUpdate()
}
}

该方案的缺点如下:

  • 只能强制当前组件更新,子组件中使用了该数据的部分不会更新
  • 性能开销大,会跳过虚拟 DOM diff 优化
  • 数据本身仍非响应式,后续修改依然无效
  • 3.4 方案对比与选择

    #publish-mermaid-1785343297822-3{font-family:inherit;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#publish-mermaid-1785343297822-3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#publish-mermaid-1785343297822-3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#publish-mermaid-1785343297822-3 .error-icon{fill:#552222;}#publish-mermaid-1785343297822-3 .error-text{fill:#552222;stroke:#552222;}#publish-mermaid-1785343297822-3 .edge-thickness-normal{stroke-width:1px;}#publish-mermaid-1785343297822-3 .edge-thickness-thick{stroke-width:3.5px;}#publish-mermaid-1785343297822-3 .edge-pattern-solid{stroke-dasharray:0;}#publish-mermaid-1785343297822-3 .edge-thickness-invisible{stroke-width:0;fill:none;}#publish-mermaid-1785343297822-3 .edge-pattern-dashed{stroke-dasharray:3;}#publish-mermaid-1785343297822-3 .edge-pattern-dotted{stroke-dasharray:2;}#publish-mermaid-1785343297822-3 .marker{fill:#333333;stroke:#333333;}#publish-mermaid-1785343297822-3 .marker.cross{stroke:#333333;}#publish-mermaid-1785343297822-3 svg{font-family:inherit;font-size:16px;}#publish-mermaid-1785343297822-3 p{margin:0;}#publish-mermaid-1785343297822-3 .label{font-family:inherit;color:#333;}#publish-mermaid-1785343297822-3 .cluster-label text{fill:#333;}#publish-mermaid-1785343297822-3 .cluster-label span{color:#333;}#publish-mermaid-1785343297822-3 .cluster-label span p{background-color:transparent;}#publish-mermaid-1785343297822-3 .label text,#publish-mermaid-1785343297822-3 span{fill:#333;color:#333;}#publish-mermaid-1785343297822-3 .node rect,#publish-mermaid-1785343297822-3 .node circle,#publish-mermaid-1785343297822-3 .node ellipse,#publish-mermaid-1785343297822-3 .node polygon,#publish-mermaid-1785343297822-3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1785343297822-3 .rough-node .label text,#publish-mermaid-1785343297822-3 .node .label text,#publish-mermaid-1785343297822-3 .image-shape .label,#publish-mermaid-1785343297822-3 .icon-shape .label{text-anchor:middle;}#publish-mermaid-1785343297822-3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#publish-mermaid-1785343297822-3 .rough-node .label,#publish-mermaid-1785343297822-3 .node .label,#publish-mermaid-1785343297822-3 .image-shape .label,#publish-mermaid-1785343297822-3 .icon-shape .label{text-align:center;}#publish-mermaid-1785343297822-3 .node.clickable{cursor:pointer;}#publish-mermaid-1785343297822-3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#publish-mermaid-1785343297822-3 .arrowheadPath{fill:#333333;}#publish-mermaid-1785343297822-3 .edgePath .path{stroke:#333333;stroke-width:1px;}#publish-mermaid-1785343297822-3 .flowchart-link{stroke:#333333;fill:none;}#publish-mermaid-1785343297822-3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1785343297822-3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#publish-mermaid-1785343297822-3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1785343297822-3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#publish-mermaid-1785343297822-3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#publish-mermaid-1785343297822-3 .cluster text{fill:#333;}#publish-mermaid-1785343297822-3 .cluster span{color:#333;}#publish-mermaid-1785343297822-3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:inherit;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#publish-mermaid-1785343297822-3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#publish-mermaid-1785343297822-3 rect.text{fill:none;stroke-width:0;}#publish-mermaid-1785343297822-3 .icon-shape,#publish-mermaid-1785343297822-3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1785343297822-3 .icon-shape p,#publish-mermaid-1785343297822-3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#publish-mermaid-1785343297822-3 .icon-shape .label rect,#publish-mermaid-1785343297822-3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1785343297822-3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#publish-mermaid-1785343297822-3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#publish-mermaid-1785343297822-3 .node .neo-node{stroke:#9370DB;}#publish-mermaid-1785343297822-3 [data-look=\”neo\”].node rect,#publish-mermaid-1785343297822-3 [data-look=\”neo\”].cluster rect,#publish-mermaid-1785343297822-3 [data-look=\”neo\”].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297822-3 [data-look=\”neo\”].swimlane.cluster rect{filter:none;}#publish-mermaid-1785343297822-3 [data-look=\”neo\”].node path{stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1785343297822-3 [data-look=\”neo\”].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297822-3 [data-look=\”neo\”].node .neo-line path{stroke:#9370DB;filter:none;}#publish-mermaid-1785343297822-3 [data-look=\”neo\”].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297822-3 [data-look=\”neo\”].node circle .state-start{fill:#000000;}#publish-mermaid-1785343297822-3 [data-look=\”neo\”].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297822-3 [data-look=\”neo\”].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297822-3 :root{–mermaid-font-family:inherit;}

    单个属性

    多个属性

    是

    否

    新增属性方案选择

    属性数量

    使用 $set

    是否需要保留原对象引用

    逐个使用 $set

    Object.assign 替换对象

    推荐方案

    四、Vue 3 的进化:Proxy 带来的彻底改变

    4.1 Proxy 的优势

    Vue 3 使用 Proxy 重写了响应式系统,从根本上解决了新增属性无法响应的问题:

    function reactive(target) {
    return new Proxy(target, {
    get(target, key, receiver) {
    const result = Reflect.get(target, key, receiver)
    track(target, key)
    return result
    },
    set(target, key, value, receiver) {
    const result = Reflect.set(target, key, value, receiver)
    trigger(target, key)
    return result
    }
    })
    }

    Proxy 可以拦截 13 种操作,包括 has、ownKeys、deleteProperty 等,因此能检测到属性的新增和删除,无需再用 $set。

    4.2 Vue 3 响应式流程

    #publish-mermaid-1785343297884-4{font-family:inherit;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#publish-mermaid-1785343297884-4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#publish-mermaid-1785343297884-4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#publish-mermaid-1785343297884-4 .error-icon{fill:#552222;}#publish-mermaid-1785343297884-4 .error-text{fill:#552222;stroke:#552222;}#publish-mermaid-1785343297884-4 .edge-thickness-normal{stroke-width:1px;}#publish-mermaid-1785343297884-4 .edge-thickness-thick{stroke-width:3.5px;}#publish-mermaid-1785343297884-4 .edge-pattern-solid{stroke-dasharray:0;}#publish-mermaid-1785343297884-4 .edge-thickness-invisible{stroke-width:0;fill:none;}#publish-mermaid-1785343297884-4 .edge-pattern-dashed{stroke-dasharray:3;}#publish-mermaid-1785343297884-4 .edge-pattern-dotted{stroke-dasharray:2;}#publish-mermaid-1785343297884-4 .marker{fill:#333333;stroke:#333333;}#publish-mermaid-1785343297884-4 .marker.cross{stroke:#333333;}#publish-mermaid-1785343297884-4 svg{font-family:inherit;font-size:16px;}#publish-mermaid-1785343297884-4 p{margin:0;}#publish-mermaid-1785343297884-4 .label{font-family:inherit;color:#333;}#publish-mermaid-1785343297884-4 .cluster-label text{fill:#333;}#publish-mermaid-1785343297884-4 .cluster-label span{color:#333;}#publish-mermaid-1785343297884-4 .cluster-label span p{background-color:transparent;}#publish-mermaid-1785343297884-4 .label text,#publish-mermaid-1785343297884-4 span{fill:#333;color:#333;}#publish-mermaid-1785343297884-4 .node rect,#publish-mermaid-1785343297884-4 .node circle,#publish-mermaid-1785343297884-4 .node ellipse,#publish-mermaid-1785343297884-4 .node polygon,#publish-mermaid-1785343297884-4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1785343297884-4 .rough-node .label text,#publish-mermaid-1785343297884-4 .node .label text,#publish-mermaid-1785343297884-4 .image-shape .label,#publish-mermaid-1785343297884-4 .icon-shape .label{text-anchor:middle;}#publish-mermaid-1785343297884-4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#publish-mermaid-1785343297884-4 .rough-node .label,#publish-mermaid-1785343297884-4 .node .label,#publish-mermaid-1785343297884-4 .image-shape .label,#publish-mermaid-1785343297884-4 .icon-shape .label{text-align:center;}#publish-mermaid-1785343297884-4 .node.clickable{cursor:pointer;}#publish-mermaid-1785343297884-4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#publish-mermaid-1785343297884-4 .arrowheadPath{fill:#333333;}#publish-mermaid-1785343297884-4 .edgePath .path{stroke:#333333;stroke-width:1px;}#publish-mermaid-1785343297884-4 .flowchart-link{stroke:#333333;fill:none;}#publish-mermaid-1785343297884-4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1785343297884-4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#publish-mermaid-1785343297884-4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1785343297884-4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#publish-mermaid-1785343297884-4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#publish-mermaid-1785343297884-4 .cluster text{fill:#333;}#publish-mermaid-1785343297884-4 .cluster span{color:#333;}#publish-mermaid-1785343297884-4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:inherit;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#publish-mermaid-1785343297884-4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#publish-mermaid-1785343297884-4 rect.text{fill:none;stroke-width:0;}#publish-mermaid-1785343297884-4 .icon-shape,#publish-mermaid-1785343297884-4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1785343297884-4 .icon-shape p,#publish-mermaid-1785343297884-4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#publish-mermaid-1785343297884-4 .icon-shape .label rect,#publish-mermaid-1785343297884-4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1785343297884-4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#publish-mermaid-1785343297884-4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#publish-mermaid-1785343297884-4 .node .neo-node{stroke:#9370DB;}#publish-mermaid-1785343297884-4 [data-look=\”neo\”].node rect,#publish-mermaid-1785343297884-4 [data-look=\”neo\”].cluster rect,#publish-mermaid-1785343297884-4 [data-look=\”neo\”].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297884-4 [data-look=\”neo\”].swimlane.cluster rect{filter:none;}#publish-mermaid-1785343297884-4 [data-look=\”neo\”].node path{stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1785343297884-4 [data-look=\”neo\”].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297884-4 [data-look=\”neo\”].node .neo-line path{stroke:#9370DB;filter:none;}#publish-mermaid-1785343297884-4 [data-look=\”neo\”].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297884-4 [data-look=\”neo\”].node circle .state-start{fill:#000000;}#publish-mermaid-1785343297884-4 [data-look=\”neo\”].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297884-4 [data-look=\”neo\”].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297884-4 :root{–mermaid-font-family:inherit;}

    reactive 包装对象

    创建 Proxy 代理

    组件 render 读取属性

    触发 get 拦截器

    track 收集依赖

    建立 effect 与依赖映射

    修改或新增属性

    触发 set 拦截器

    trigger 派发更新

    scheduler 调度 effect

    组件重新渲染

    4.3 Vue 2 与 Vue 3 响应式对比

    | 对比维度 | Vue 2 (Object.defineProperty) | Vue 3 (Proxy) |

    |———|——————————-|—————|

    | 新增属性 | 无法检测,需手动 $set | 自动检测 |

    | 删除属性 | 无法检测,需 $delete | 自动检测 |

    | 数组索引修改 | 需 $set | 自动检测 |

    | Map/Set 支持 | 不支持 | 原生支持 |

    | 性能 | 初始化时深度遍历 | 惰性响应式 |

    五、实战避坑指南:日常开发中的注意事项

    5.1 常见踩坑场景

  • 表单动态添加字段:用户点击 "添加更多" 按钮时,需要向表单对象动态添加字段
  • 接口返回数据扩展:拿到后端数据后,需要补充前端额外字段
  • 树形结构懒加载:异步获取子节点后挂载到父节点
  • 5.2 调试技巧

    可以通过检查对象的 __ob__ 属性来判断是否为响应式对象:

    console.log(this.user.__ob__)
    console.log(this.user.__ob__.value.gender)

    如果新属性不在 __ob__.value 中被 observe,则说明它不是响应式的。

    5.3 最佳实践总结

  • 优先使用 this.$set 添加单个新属性
  • 批量添加属性时使用 Object.assign 替换整个对象
  • 避免滥用 this.$forceUpdate
  • 新项目优先选用 Vue 3,从根源规避此问题
  • 数据结构在初始化时尽量声明完整,避免运行时动态新增
  • 5.4 完整避坑决策图

    #publish-mermaid-1785343297955-5{font-family:inherit;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#publish-mermaid-1785343297955-5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#publish-mermaid-1785343297955-5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#publish-mermaid-1785343297955-5 .error-icon{fill:#552222;}#publish-mermaid-1785343297955-5 .error-text{fill:#552222;stroke:#552222;}#publish-mermaid-1785343297955-5 .edge-thickness-normal{stroke-width:1px;}#publish-mermaid-1785343297955-5 .edge-thickness-thick{stroke-width:3.5px;}#publish-mermaid-1785343297955-5 .edge-pattern-solid{stroke-dasharray:0;}#publish-mermaid-1785343297955-5 .edge-thickness-invisible{stroke-width:0;fill:none;}#publish-mermaid-1785343297955-5 .edge-pattern-dashed{stroke-dasharray:3;}#publish-mermaid-1785343297955-5 .edge-pattern-dotted{stroke-dasharray:2;}#publish-mermaid-1785343297955-5 .marker{fill:#333333;stroke:#333333;}#publish-mermaid-1785343297955-5 .marker.cross{stroke:#333333;}#publish-mermaid-1785343297955-5 svg{font-family:inherit;font-size:16px;}#publish-mermaid-1785343297955-5 p{margin:0;}#publish-mermaid-1785343297955-5 .label{font-family:inherit;color:#333;}#publish-mermaid-1785343297955-5 .cluster-label text{fill:#333;}#publish-mermaid-1785343297955-5 .cluster-label span{color:#333;}#publish-mermaid-1785343297955-5 .cluster-label span p{background-color:transparent;}#publish-mermaid-1785343297955-5 .label text,#publish-mermaid-1785343297955-5 span{fill:#333;color:#333;}#publish-mermaid-1785343297955-5 .node rect,#publish-mermaid-1785343297955-5 .node circle,#publish-mermaid-1785343297955-5 .node ellipse,#publish-mermaid-1785343297955-5 .node polygon,#publish-mermaid-1785343297955-5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1785343297955-5 .rough-node .label text,#publish-mermaid-1785343297955-5 .node .label text,#publish-mermaid-1785343297955-5 .image-shape .label,#publish-mermaid-1785343297955-5 .icon-shape .label{text-anchor:middle;}#publish-mermaid-1785343297955-5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#publish-mermaid-1785343297955-5 .rough-node .label,#publish-mermaid-1785343297955-5 .node .label,#publish-mermaid-1785343297955-5 .image-shape .label,#publish-mermaid-1785343297955-5 .icon-shape .label{text-align:center;}#publish-mermaid-1785343297955-5 .node.clickable{cursor:pointer;}#publish-mermaid-1785343297955-5 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#publish-mermaid-1785343297955-5 .arrowheadPath{fill:#333333;}#publish-mermaid-1785343297955-5 .edgePath .path{stroke:#333333;stroke-width:1px;}#publish-mermaid-1785343297955-5 .flowchart-link{stroke:#333333;fill:none;}#publish-mermaid-1785343297955-5 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1785343297955-5 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#publish-mermaid-1785343297955-5 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1785343297955-5 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#publish-mermaid-1785343297955-5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#publish-mermaid-1785343297955-5 .cluster text{fill:#333;}#publish-mermaid-1785343297955-5 .cluster span{color:#333;}#publish-mermaid-1785343297955-5 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:inherit;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#publish-mermaid-1785343297955-5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#publish-mermaid-1785343297955-5 rect.text{fill:none;stroke-width:0;}#publish-mermaid-1785343297955-5 .icon-shape,#publish-mermaid-1785343297955-5 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#publish-mermaid-1785343297955-5 .icon-shape p,#publish-mermaid-1785343297955-5 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#publish-mermaid-1785343297955-5 .icon-shape .label rect,#publish-mermaid-1785343297955-5 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#publish-mermaid-1785343297955-5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#publish-mermaid-1785343297955-5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#publish-mermaid-1785343297955-5 .node .neo-node{stroke:#9370DB;}#publish-mermaid-1785343297955-5 [data-look=\”neo\”].node rect,#publish-mermaid-1785343297955-5 [data-look=\”neo\”].cluster rect,#publish-mermaid-1785343297955-5 [data-look=\”neo\”].node polygon{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297955-5 [data-look=\”neo\”].swimlane.cluster rect{filter:none;}#publish-mermaid-1785343297955-5 [data-look=\”neo\”].node path{stroke:#9370DB;stroke-width:1px;}#publish-mermaid-1785343297955-5 [data-look=\”neo\”].node .outer-path{filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297955-5 [data-look=\”neo\”].node .neo-line path{stroke:#9370DB;filter:none;}#publish-mermaid-1785343297955-5 [data-look=\”neo\”].node circle{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297955-5 [data-look=\”neo\”].node circle .state-start{fill:#000000;}#publish-mermaid-1785343297955-5 [data-look=\”neo\”].icon-shape .icon{fill:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297955-5 [data-look=\”neo\”].icon-shape .icon-neo path{stroke:#9370DB;filter:drop-shadow(1px 2px 2px rgba(185, 185, 185, 1));}#publish-mermaid-1785343297955-5 :root{–mermaid-font-family:inherit;}

    Vue 2

    Vue 3

    this.obj.newProp = val

    this.$set

    Object.assign 替换对象

    需要给对象添加新属性

    Vue 版本

    添加方式

    直接赋值即可

    界面不刷新

    正常响应

    排查并改用 $set

    问题解决

    赞(0)
    未经允许不得转载:171主机测评 » 为什么 Vue 中给对象添加新属性后界面不刷新:深入响应式原理与最佳解决方案
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址