We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
我们从一个例子开始
定义一个p标签,通过v-for指令进行遍历
p
v-for
然后给botton标签绑定点击事件,我们预期点击按钮时,数据新增一个属性,界面也 新增一行
botton
<p v-for="(value,key) in item" :key="key"> {{ value }} </p> <button @click="addProperty">动态添加新属性</button>
实例化一个vue实例,定义data属性和methods方法
vue
data
methods
const app = new Vue({ el:"#app", data:()=>{ item:{ oldProperty:"旧属性" } }, methods:{ addProperty(){ this.items.newProperty = "新属性" // 为items添加新属性 console.log(this.items) // 输出带有newProperty的items } } })
点击按钮,发现结果不及预期,数据虽然更新了(console打印出了新属性),但页面并没有更新
console
为什么产生上面的情况呢?
下面来分析一下
vue2是用过Object.defineProperty实现数据响应式
vue2
Object.defineProperty
const obj = {} Object.defineProperty(obj, 'foo', { get() { console.log(`get foo:${val}`); return val }, set(newVal) { if (newVal !== val) { console.log(`set foo:${newVal}`); val = newVal } } }) }
当我们访问foo属性或者设置foo值的时候都能够触发setter与getter
foo
setter
getter
obj.foo obj.foo = 'new'
但是我们为obj添加新属性的时候,却无法触发事件属性的拦截
obj
obj.bar = '新属性'
原因是一开始obj的foo属性被设成了响应式数据,而bar是后面新增的属性,并没有通过Object.defineProperty设置成响应式数据
bar
Vue 不允许在已经创建的实例上动态添加新的响应式属性
Vue
若想实现数据与视图同步更新,可采取下面三种解决方案:
Vue.set( target, propertyName/index, value )
参数
{Object | Array} target
{string | number} propertyName/index
{any} value
返回值:设置的值
通过Vue.set向响应式对象中添加一个property,并确保这个新 property 同样是响应式的,且触发视图更新
Vue.set
property
关于Vue.set源码(省略了很多与本节不相关的代码)
源码位置:src\core\observer\index.js
src\core\observer\index.js
function set (target: Array<any> | Object, key: any, val: any): any { ... defineReactive(ob.value, key, val) ob.dep.notify() return val }
这里无非再次调用defineReactive方法,实现新增属性的响应式
defineReactive
关于defineReactive方法,内部还是通过Object.defineProperty实现属性拦截
大致代码如下:
function defineReactive(obj, key, val) { Object.defineProperty(obj, key, { get() { console.log(`get ${key}:${val}`); return val }, set(newVal) { if (newVal !== val) { console.log(`set ${key}:${newVal}`); val = newVal } } }) }
直接使用Object.assign()添加到对象的新属性不会触发更新
Object.assign()
应创建一个新的对象,合并原对象和混入对象的属性
this.someObject = Object.assign({},this.someObject,{newProperty1:1,newProperty2:2 ...})
如果你发现你自己需要在 Vue 中做一次强制更新,99.9% 的情况,是你在某个地方做错了事
$forceUpdate迫使 Vue 实例重新渲染
$forceUpdate
PS:仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。
如果为对象添加少量的新属性,可以直接采用Vue.set()
Vue.set()
如果需要为新对象添加大量的新属性,则通过Object.assign()创建新对象
如果你实在不知道怎么操作时,可采取$forceUpdate()进行强制刷新 (不建议)
$forceUpdate()
PS:vue3是用过proxy实现数据响应式的,直接动态添加新属性仍可以实现数据响应式
vue3
proxy
The text was updated successfully, but these errors were encountered:
No branches or pull requests
面试官:动态给vue的data添加一个新的属性时会发生什么?怎样解决?
一、直接添加属性的问题
我们从一个例子开始
定义一个
p
标签,通过v-for
指令进行遍历然后给
botton
标签绑定点击事件,我们预期点击按钮时,数据新增一个属性,界面也 新增一行实例化一个
vue
实例,定义data
属性和methods
方法点击按钮,发现结果不及预期,数据虽然更新了(
console
打印出了新属性),但页面并没有更新二、原理分析
为什么产生上面的情况呢?
下面来分析一下
vue2
是用过Object.defineProperty
实现数据响应式当我们访问
foo
属性或者设置foo
值的时候都能够触发setter
与getter
但是我们为
obj
添加新属性的时候,却无法触发事件属性的拦截原因是一开始
obj
的foo
属性被设成了响应式数据,而bar
是后面新增的属性,并没有通过Object.defineProperty
设置成响应式数据三、解决方案
Vue
不允许在已经创建的实例上动态添加新的响应式属性若想实现数据与视图同步更新,可采取下面三种解决方案:
Vue.set()
Vue.set( target, propertyName/index, value )
参数
{Object | Array} target
{string | number} propertyName/index
{any} value
返回值:设置的值
通过
Vue.set
向响应式对象中添加一个property
,并确保这个新property
同样是响应式的,且触发视图更新关于
Vue.set
源码(省略了很多与本节不相关的代码)源码位置:
src\core\observer\index.js
这里无非再次调用
defineReactive
方法,实现新增属性的响应式关于
defineReactive
方法,内部还是通过Object.defineProperty
实现属性拦截大致代码如下:
Object.assign()
直接使用
Object.assign()
添加到对象的新属性不会触发更新应创建一个新的对象,合并原对象和混入对象的属性
$forceUpdate
如果你发现你自己需要在
Vue
中做一次强制更新,99.9% 的情况,是你在某个地方做错了事$forceUpdate
迫使Vue
实例重新渲染PS:仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。
小结
如果为对象添加少量的新属性,可以直接采用
Vue.set()
如果需要为新对象添加大量的新属性,则通过
Object.assign()
创建新对象如果你实在不知道怎么操作时,可采取
$forceUpdate()
进行强制刷新 (不建议)PS:
vue3
是用过proxy
实现数据响应式的,直接动态添加新属性仍可以实现数据响应式参考文献
The text was updated successfully, but these errors were encountered: