目前我正在开发一个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 Guide: Reactivity In Depth)。
所以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>