首先非常抱歉我的英文不是很好, 我使用了尽量多的代码来描述我的需求
在使用Vue拓展(
Vue.extend)时,data和computed对于使用者来说并没有什么区别
目前的处理方式是,data拥有较高的优先级,他会覆盖computed
但我觉得,这样的设计容易产生歧义,使用者往往会认为程序没有达到预期效果,
如下面这个例子, 在定义extend时
var vExt = Vue.extend({
data: {
fields: {
ext_computed1: "ext_computed1"
}
},
computed: {
ext_computed1: {
get: function () { return this.fields.ext_computed1; },
set: function (value) {
this.fields.ext_computed1 = "ext_computed1:" + value;
}
}
}
});
对于使用者来说,data和computed在使用上没有任何区别
目前的处理方式,容易产生歧义,认为程序没有达到预期效果,如以下代码
var xx = new vExt({
el: "####x",
data: {
ext_computed1: "123"
}
});
var result = xx.ext_computed1; //result = "123", but I thought 'result' = "ext_computed1:123"
这时xx.ext_computed1 返回的是 "123", 但使用者预期的可能是 "ext_computed1:123"
另外一种情况正好相反:
如: 拓展定义
var vExt = Vue.extend({
data: function () {
return {
ext_data1: null
};
}
});
使用者:
var xx = new vExt({
el: "####x",
data: {
fields: {
data1: "my:data1"
}
},
computed: {
ext_data1: {
get: function () { return this.fields.data1; },
set: function (value) {
this.fields.data1 = "my:" + value;
}
}
}
});
var result = xx.ext_computed1; //result = "ext_data1", but I thought 'result' = "my:ext_data1"
xx.ext_data1 返回的是 "ext_data1", 但预期是 "my:ext_data1"
以上两种情况的预期并不是一个特殊的需求
在大多数服务器语言中他们是非常正常的一个行为
例如C#中的继承
class Parent
{
public virtual string Name { get; set; } = "xxxx";
}
class Child: Parent
{
private string _name;
public override string Name
{
get { return _name; }
set { _name = "my:" + value; }
}
}
initState函数中,是先执行initData再执行initComputed
所以我的思路是在 initData 中判断,如果存在 同名的computed 则不生成代理
function initData(vm) {
var data = vm.$options.data;
var computed = vm.$options.computed;
data = vm._data = typeof data === 'function'
? data.call(vm)
: data || {};
if (!isPlainObject(data)) {
data = {};
"development" !== 'production' && warn(
'data functions should return an object:\n' +
'https://vuejs.org/v2/guide/components.html####data-Must-Be-a-Function',
vm
);
}
// proxy data on instance
var keys = Object.keys(data);
var computed = vm.$options.computed;
var cKeys = computed && Object.keys(computed); //得到 computed 枚举
var props = vm.$options.props;
var i = keys.length;
while (i--) {
if (props && hasOwn(props, keys[i])) {
"development" !== 'production' && warn(
"The data property \"" + (keys[i]) + "\" is already declared as a prop. " +
"Use prop default value instead.",
vm
);
} else if (!isReserved(keys[i])) {
if (cKeys || cKeys.indexOf(keys[i]) === -1) { //当 computed 存在时,不生成代理
proxy(vm, "_data", keys[i]);
}
}
}
// observe data
observe(data, true /* asRootData */);
return data; //返回data值后面用, 这里也可以只返回与 computed 同名的属性
}
上面的处理可以让data不将computed覆盖
function initState (vm) {
vm._watchers = [];
var opts = vm.$options;
if (opts.props) { initProps(vm, opts.props); }
if (opts.methods) { initMethods(vm, opts.methods); }
var initValues = null;
if (opts.data) {
initValues = initData(vm); //获取初始化的data
} else {
observe(vm._data = {}, true /* asRootData */);
}
if (opts.computed) { initComputed(vm, opts.computed); }
if (opts.watch) { initWatch(vm, opts.watch); }
if (!initValues) return;
var keys = Object.keys(initValues);
for (var i = 0; i < keys.length; i++) {
vm[keys[i]] = initValues[keys[i]]; //将data赋值给computed
}
}
上面的处理,可以让computed拥有初始值,并触发setter(如果有)
在我印象中,尤大的中文也是非常厉害的(知乎刷得溜溜的) 😄
我想你应该精炼一下你的问题:
var vm = new Vue({
data: {
prop1: 'this is data prop'
},
computed: {
prop1: function () {
return 'this is computed prop'
}
}
});
console.log(vm.prop1); // 'this is data prop'
你的问题是prop1始终是data的属性,而非计算属性。
因为Vue在初始化computed的时候做了限制:
source: https://github.com/vuejs/vue/blob/dev/src/core/instance/state.js#L166
从注释上来看,这是为了防止覆盖组件原型链上的计算属性
但是同时也阻止了computed覆盖data的代理属性,是吗?
你那个精简后的虽然也是同一个现象,但问题的表现还是有区别的,同一个对象中data和computed重名是完全可以避免的。
但我想说的是,写拓展的和使用拓展的可能不是同一个人。
那么data和computed的冲突就会是个问题了,就好像服务器语言中的继承和重写一样。
当一个拓展的实例希望江拓展的data改为computed时,他会发现完全没有任何办法。
另一种情况是,当一个拓展的实例希望在初始化时给某个属性(使用者并不关心这个属性是data还是computed)赋值时,他却只能通过vm.xxx=yyy的方式
源码中确实是做了判断,不覆盖data,但是我不明白这样做的用意,在我的理解中,computed和data在使用上没有任何区别,他们本质上应该是相同的,data会被代理为类似computed的存在。
所以这样看来computed的优先级应该高于data。
另外,如果已经在存在一个与data同名的computed,也说明使用者希望由自己控制这个属性的行为,更应该优先使用computed。
以上是我个人在使用中的一些不便和疑惑的地方。
@blqw 还没睡吗 : ) 我想你对计算属性的理解有点模糊
中文文档: https://cn.vuejs.org/v2/guide/computed.html#计算属性
computed存在的意义是为了简化模板的语法,将复杂的计算逻辑留在代码中。
他和data属性的代理意义是不同的
至于为什么data的代理属性优先级高于计算属性。我想作者考虑的是两者的使用频率。
另外,下次提交的时候麻烦使用英语,想清楚问题后用翻译软件翻译过来就是了。会有更多的人解答你的问题
你的原始例子对 computed 和 options 的理解有偏差。data 就是 data,computed 就是 computed,在 options 里面是完全分开的,只有在创建出来的实例上面共用一个 namespace。
原则上来说,data 和 computed 有重复定义是错误的使用方式,e7e86fd 对这种情况添加了警告。
Most helpful comment
你的原始例子对 computed 和 options 的理解有偏差。data 就是 data,computed 就是 computed,在 options 里面是完全分开的,只有在创建出来的实例上面共用一个 namespace。
原则上来说,data 和 computed 有重复定义是错误的使用方式,e7e86fd 对这种情况添加了警告。