什么是计算属性?
计算属性就是根据一定的逻辑,将一个新属性与data数据的某个属性进行关联,由此获取与原数据对应的值。
以一个例子来说明:
<div id="test">
你输入的:<input type="text" v-model="message"><br/>
将变成:<input type="text" v-model="newMessage" disabled>
</div>
let vm = new Vue({
el:'#test',
data:{ message:''},
computed:{
newMessage:function(){
return this.message==''?'':this.message+',哈哈!';
},
newMessageForTest:{
get:function(){
return this.message==''?'':this.message+',嘿嘿!';
}
}
}
});
【这里提供了写两种计算属性的方法。newMessage默认对应的方法为message的getter方法,而newMessageForTest为message专门提供了get。】
计算属性的作用就是让 Vue 知道 vm.newMessage 依赖于 vm.message,当 vm.message发生改变时,所有依赖 vm.newMessage 的绑定也会更新。
如图:
用源码说话
initComputed
function initState (vm) {
vm._watchers = [];
var opts = vm.$options;
if (opts.props) { initProps(vm, opts.props); }
if (opts.methods) { initMethods(vm, opts.methods); }
if (opts.data) {
initData(vm);
} else {
observe(vm._data = {}, true /* asRootData */);
}
if (opts.computed) { initComputed(vm, opts.computed); }
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch);
}
}
Vue在初始化组件的时候调用initState(),此时若用户配置了computed,则进入initComputed(vm, opts.computed)对计算属性进行初始化。
var watchers = vm._computedWatchers = Object.create(null);
initComputed ()接收vm,computed两个参数,
首先定义了一个空对象watchers,并赋值给_computedWatchers挂载到vm下。watchers就是以键值对的方式,存储计算属性对应的方法。
var isSSR = isServerRendering();
判断是不是服务器端渲染,计算属性在服务器渲染的情况下只有getter。如果是服务器端渲染,Vue不会为计算属性添加Watcher。
关于SSR的学习来自:https://codesky.me/archives/h...
for (var key in computed) {
var userDef = computed[key];
var getter = typeof userDef === 'function' ? userDef : userDef.get;
if ("development" !== 'production' && getter == null) {
warn(
("Getter is missing for computed property \"" + key + "\"."),
vm
);
}
if (!isSSR) {
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
computedWatcherOptions
);
}
if (!(key in vm)) {
defineComputed(vm, key, userDef);
} else {
if (key in vm.$data) {
warn(("The computed property \"" + key + "\" is already defined in data."), vm);
} else if (vm.$options.props && key in vm.$options.props) {
warn(("The computed property \"" + key + "\" is already defined as a prop."), vm);
}
}
}
- 遍历用户定义的所有计算属性,创建变量userDef存储该计算属性的get方法。如果用户用下图newMessageForTest的方式定义计算属性,userDef存储的就是用户定义的get方法,如果是用下图newMessage的方式定义计算属性,Vue就把该计算属性对应的function作为它的getter。
2. 为计算属性创建观察者Watcher,存放在上面定义的watchers空对象中。
3. 经过判断后调用defineComputed定义计算属性。
defineComputed
if (typeof userDef === 'function') {
sharedPropertyDefinition.get = shouldCache
? createComputedGetter(key)
: userDef;
sharedPropertyDefinition.set = noop;
} else {
sharedPropertyDefinition.get = userDef.get
? shouldCache && userDef.cache !== false
? createComputedGetter(key)
: userDef.get
: noop;
sharedPropertyDefinition.set = userDef.set
? userDef.set
: noop;
}
if ("development" !== 'production' &&
sharedPropertyDefinition.set === noop) {
sharedPropertyDefinition.set = function () {
warn(
("Computed property \"" + key + "\" was assigned to but it has no setter."),
this
);
};
}
Object.defineProperty(target, key, sharedPropertyDefinition);
defineComputed 接收了target,key,userDef三个参数,分别是该组件,计算属性,以及计算属性对应的方法,这段代码根据组件的不同状态,将计算属性绑定到组件上。
关于Object.defineProperty学习于https://segmentfault.com/a/11...
createComputedGetter
function createComputedGetter (key) {
return function computedGetter () {
var watcher = this._computedWatchers && this._computedWatchers[key];
if (watcher) {
watcher.depend();
return watcher.evaluate()
}
}
}
- 通过计算属性key的值找到对应的watcher,并启动该观察者进行观察。
- 通过watcher.depend()依赖收集绑定,本文就是将message和newMessag绑定。
this.dep.depend();
watcher.depend()里通过this.dep(该依赖)调用depend()。
Dep.prototype.depend = function depend () {
if (Dep.target) {
Dep.target.addDep(this);
}
};
而Dep的depend方法调用了Watcher的addDep方法。
Watcher.prototype.addDep = function addDep (dep) {
var id = dep.id;
if (!this.newDepIds.has(id)) {
this.newDepIds.add(id);
this.newDeps.push(dep);
if (!this.depIds.has(id)) {
dep.addSub(this);
}
}
};
addDep将依赖dep push到newDeps中,将dep的id push到newDepIds。dep{id:2,subs:[watcher(计算属性),watcher2(计算属性的关联属性)]}。
上图是message的watcher,对应的expression就是Vue的_update()方法。
这是newMessage的watcher,对应的expression就是用户自定义的get方法。
watcher.depend和evalute
当message发生改变时,触发对应的dep.notify()方法,发布通知观察者watcher执行update,之后会触发watcher的getter方法获取计算属性改变后关联属性的值,并将新的值更改到watcher对象下,进行数据更新了。
(Watcher.prototype.get):
value = this.getter.call(vm, vm);
(Watcher.prototype.getAndInvoke):
// set new value
var oldValue = this.value;
this.value = value;
this.dirty = false;
watcher.evalute()就是返回挂载到watcher下的新的value。
Watcher.prototype.evaluate = function evaluate () {
if (this.dirty) {
this.value = this.get();
this.dirty = false;
}
return this.value
};
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。