通常我們不需要頻繁的去更新模板上的屬性值,則會采用computed,因為他具有緩存功能胰伍,可以提高性能。
computed有兩種寫法
1酸休、
<div id="app">{{fullName}}</div>
let vm = new Vue({
el:'#app',
data(){
return {
// arr:[[1,2,3]],
// name:'123',
firstName:'yan',
lastName:'liying'
}
},
computed:{
fullName:{
get(){
return this.firstName + this.lastName;
},
set(val){
}
}
},
})
2骂租、
<div id="app">{{fullName}}</div>
let vm = new Vue({
el:'#app',
data(){
return {
// arr:[[1,2,3]],
// name:'123',
firstName:'yan',
lastName:'liying'
}
},
computed:{
fullName(){
return this.firstName + this.lastName
}
},
})
vue中初始化computed,每一個計算屬性的本質(zhì)就是watcher斑司,創(chuàng)建計算屬性的watcher時渗饮,會傳入一個懶惰屬性,來控制computed緩存的功能宿刮,默認(rèn)是執(zhí)行的互站,先處理為vm._computedWatchers對象,每個key對應(yīng)一個watch實例。進(jìn)而能夠獲取到當(dāng)前這個計算屬性的dirty僵缺,來控制是來重新觸發(fā)get胡桃,還是走上一次的緩存;
function initComputed(vm, computed) {
for (let key in computed) {
const userDef = computed[key];
let watch = vm._computedWatcher = {}
//依賴的屬性變化就重新取值 get
let getter = typeof userDef == "function" ? userDef : userDef.get;
//每個計算屬性本質(zhì)就是watcher
watch[key] = new Watcher(vm, getter, () => {}, { lazy: true }); //默認(rèn)執(zhí)行
//將key定義在vm上
defineProperty(vm, key, userDef);
}
}
function createComputedGetter(key){
return function computedGetter(){
let watcher = this._computedWatcher[key];
if(watcher.dirty){
watcher.evaluate();
}
//如果當(dāng)前取完值后 dep.target還有值磕潮,需要繼續(xù)向上收集
if(Dep.target){
//計算屬性watcher翠胰,firstName,lastName
watcher.depend();//watcher對應(yīng)了多個dep
console.log(Dep.target,'---tera');
}
return watcher.value;
}
}
//computed 就是一個defineProperty
function defineProperty(vm, key, userDef) {
let sharedProperty = {};
if(typeof userDef == 'function'){
sharedProperty.get = userDef;
}else{
sharedProperty.get = createComputedGetter(key);
sharedProperty.set = userDef.set;
}
Object.defineProperty(vm, key, sharedProperty)
}
watcher中的dirty默認(rèn)是true,是執(zhí)行狀態(tài)自脯,會觸發(fā)watcher中的evaluate亡容,方法,evaluate方法中會調(diào)用get方法冤今,并且同時將當(dāng)前的watcher中的dirty置為false,下一次不會執(zhí)行茂缚。
class Watcher {
constructor(vm, exporOrFn, cb, options = {}) {
this.vm = vm;
this.exporOrFn = exporOrFn;
this.options = options;
this.cb = cb;
this.id = id++;
this.user = !!options.user; //是否是用戶調(diào)用
this.lazy = !!options.lazy; //computed懶執(zhí)行的flag
this.deps = [];
this.dirty = this.lazy;
this.depsId = new Set();
//默認(rèn)應(yīng)該讓exportOrFn執(zhí)行戏罢,exportOrFn做了什么事?去render上取值
if (typeof exporOrFn == "string") {
this.getter = function () {
let arr = exporOrFn.split(".");
let obj = vm;
for (var i = 0; i < arr.length; i++) {
obj = obj[arr[i]];
}
return obj;
};
} else {
this.getter = exporOrFn;
}
//value 值是第一次渲染的value
this.value = this.lazy ? undefined : this.get(); //默認(rèn)初始化要取值
}
get() {
//稍后用戶更新時脚囊,可以重新調(diào)用getter方法
//defineProperty.get,每個屬性都可以收集自己的watcher,每個屬性都有一個依賴收集的對象 dep
//一個屬性可以對應(yīng)多個watcher龟糕,一個watcher也可以對應(yīng)多個屬性
pushTarget(this);
const value = this.getter.call(this.vm);
popTarget(this);
return value;
}
update() {
if (this.lazy) {
this.dirty = true;
} else {
//每次更新時 this 多次調(diào)用update 我希望現(xiàn)將watcher緩存下來,等一會一起更新
queueWatcher(this);
}
}
evaluate() {
this.dirty = false;
this.value = this.get();
}
}
總結(jié)
初始化 data 和 computed悔耘,分別代理其 set 和 get 方法讲岁,對 data 中的所有屬性生成唯一的 dep 實例
對 computed 中的 屬性生成唯一的 watcher,并保存在 vm._computedWatchers 中
訪問計算屬性時,設(shè)置 Dep.target 指向 計算屬性的 watcher缓艳,調(diào)用該屬性具體方法
方法中訪問 data 的屬性校摩,即會調(diào)用 data 屬性的 get 方法,將 data 屬性的 dep 加入到 計算屬性的 watcher 阶淘, 同時該 dep 中的 subs 添加這個 watcher
設(shè)置 data 的這個屬性時衙吩,調(diào)用該屬性代理的 set 方法,觸發(fā) dep 的 notify 方法
因為時 computed 屬性溪窒,只是將 watcher 中的 dirty 設(shè)置為 true
最后坤塞,訪問計算屬性的 get 方法時,得知該屬性的 watcher.dirty 為 true澈蚌,則調(diào)用 watcher.evaluate() 方法獲取新的值