目前我正在做一个 Vue 项目,我想有一个模型类,将所有初始数据存储在两个属性中:original
和attributes
。但是,我在通过v-model
将属性绑定到输入时遇到困难。
假设我有以下类:
class Model
{
constructor () {
return new Proxy(this, {
get: (target, property, receiver) => {
console.log('get');
return target[property];
},
set: (target, property, value, receiver) => {
console.log('set');
target[property] = value + 123;
return true;
}
});
}
}
此类将在获取属性时记录get
,在设置属性时记录set
。此外,在设置时,它将在值的末尾添加123
。将执行以下操作:
let model = new Model();
model.name = 'test'; // Logs: 'set'
console.log(model.name); // Logs: 'get' and 'test123'
这按预期工作。
当我通过v-model
将这些属性绑定到输入时,问题就开始了。将发生以下情况:
在以下位置执行操作:
<input v-model="model.name" />
...
data () {
return {
model: new Model
};
}
在第一次更改时,属性name
将添加到model
对象。根本没有日志。值末尾没有添加123
。模型值已更改为{ model: 't' }
。
第二次更改它将记录set
并添加123
。模型值已更改为{ model: 'te123' }
。
在第二次更改期间记录set
调用的参数:Model {__ob__: Observer}, "name", "te", Proxy {__ob__: Observer}
有人以前遇到过这个问题或知道这里发生了什么?
回答@sphinx后
谢谢你的回答!它有效!
为了使事情变得更加复杂,我想将某个fillable
数组中的属性设置为名为attributes
的属性。但是,相同的行为会返回。
class Model
{
constructor () {
this.attributes = {};
this.fillable = ['name'];
return new Proxy(this, {
get: (target, property, receiver) => {
console.log('get', property);
if (target.fillable.indexOf(property) > -1) { // Check if in fillable
if (!(property in target.attributes)) {
Vue.set(target.attributes, property, null);
}
return target.attributes[property];
}
return target[property];
},
set: (target, property, value, receiver) => {
console.log('set', property, value);
if (target.fillable.indexOf(property) > -1) {
Vue.set(target.attributes, property, value + 123);
} else {
target[property] = value
}
return true;
}
});
}
}
Vue.config.productionTip = false
new Vue({
el: '#app',
data() {
return {
test: new Model
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<span>{{test.name}}</span>
<input v-model="test.name" type="text">
</div>
在第一次更改期间,name
属性直接在model
对象上设置。但不调用set
方法。
在第二次更改期间,name
属性将更改,并调用set
方法。
关于为什么会发生这种情况的任何建议?
在你的考试中,getter 将创建一个属性,但它不是反应性(查看 Vue 指南:深度反应性(。
所以 Vue 无法跟踪这些变化。
正如上述指南介绍的那样,该解决方案很简单,使用Vue.set
或vm.$set
来确保您的对象具有反应性。
像下面的演示:
class Model
{
constructor () {
return new Proxy(this, {
get: (target, property, receiver) => {
console.log('get', property);
if(!(property in target)) Vue.set(target, property, null)
return target[property]
},
set: (target, property, value, receiver) => {
console.log('set', property, value);
Vue.set(target, property, value + 123)
return true;
}
});
}
}
Vue.config.productionTip = false
new Vue({
el: '#app',
data() {
return {
test: new Model
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<span>{{test.name}}</span>
<input v-model="test.name" type="text">
</div>