通常我們不需要頻繁的去更新模板上的屬性值,則會(huì)采用computed,因?yàn)樗哂芯彺婀δ?,可以提高性能?/h4>
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,每一個(gè)計(jì)算屬性的本質(zhì)就是watcher,創(chuàng)建計(jì)算屬性的watcher時(shí),會(huì)傳入一個(gè)懶惰屬性,來(lái)控制computed緩存的功能,默認(rèn)是執(zhí)行的,先處理為vm._computedWatchers對(duì)象,每個(gè)key對(duì)應(yīng)一個(gè)watch實(shí)例。進(jìn)而能夠獲取到當(dāng)前這個(gè)計(jì)算屬性的dirty,來(lái)控制是來(lái)重新觸發(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;
//每個(gè)計(jì)算屬性本質(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){
//計(jì)算屬性watcher,firstName,lastName
watcher.depend();//watcher對(duì)應(yīng)了多個(gè)dep
console.log(Dep.target,'---tera');
}
return watcher.value;
}
}
//computed 就是一個(gè)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),會(huì)觸發(fā)watcher中的evaluate,方法,evaluate方法中會(huì)調(diào)用get方法,并且同時(shí)將當(dāng)前的watcher中的dirty置為false,下一次不會(huì)執(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() {
//稍后用戶更新時(shí),可以重新調(diào)用getter方法
//defineProperty.get,每個(gè)屬性都可以收集自己的watcher,每個(gè)屬性都有一個(gè)依賴收集的對(duì)象 dep
//一個(gè)屬性可以對(duì)應(yīng)多個(gè)watcher,一個(gè)watcher也可以對(duì)應(yīng)多個(gè)屬性
pushTarget(this);
const value = this.getter.call(this.vm);
popTarget(this);
return value;
}
update() {
if (this.lazy) {
this.dirty = true;
} else {
//每次更新時(shí) this 多次調(diào)用update 我希望現(xiàn)將watcher緩存下來(lái),等一會(huì)一起更新
queueWatcher(this);
}
}
evaluate() {
this.dirty = false;
this.value = this.get();
}
}
總結(jié)
初始化 data 和 computed,分別代理其 set 和 get 方法,對(duì) data 中的所有屬性生成唯一的 dep 實(shí)例
對(duì) computed 中的 屬性生成唯一的 watcher,并保存在 vm._computedWatchers 中
訪問(wèn)計(jì)算屬性時(shí),設(shè)置 Dep.target 指向 計(jì)算屬性的 watcher,調(diào)用該屬性具體方法
方法中訪問(wèn) data 的屬性,即會(huì)調(diào)用 data 屬性的 get 方法,將 data 屬性的 dep 加入到 計(jì)算屬性的 watcher , 同時(shí)該 dep 中的 subs 添加這個(gè) watcher
設(shè)置 data 的這個(gè)屬性時(shí),調(diào)用該屬性代理的 set 方法,觸發(fā) dep 的 notify 方法
因?yàn)闀r(shí) computed 屬性,只是將 watcher 中的 dirty 設(shè)置為 true
最后,訪問(wèn)計(jì)算屬性的 get 方法時(shí),得知該屬性的 watcher.dirty 為 true,則調(diào)用 watcher.evaluate() 方法獲取新的值
參考:http://www.itdecent.cn/p/a79806d317a4
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
}
},
})
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;
//每個(gè)計(jì)算屬性本質(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){
//計(jì)算屬性watcher,firstName,lastName
watcher.depend();//watcher對(duì)應(yīng)了多個(gè)dep
console.log(Dep.target,'---tera');
}
return watcher.value;
}
}
//computed 就是一個(gè)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)
}
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() {
//稍后用戶更新時(shí),可以重新調(diào)用getter方法
//defineProperty.get,每個(gè)屬性都可以收集自己的watcher,每個(gè)屬性都有一個(gè)依賴收集的對(duì)象 dep
//一個(gè)屬性可以對(duì)應(yīng)多個(gè)watcher,一個(gè)watcher也可以對(duì)應(yīng)多個(gè)屬性
pushTarget(this);
const value = this.getter.call(this.vm);
popTarget(this);
return value;
}
update() {
if (this.lazy) {
this.dirty = true;
} else {
//每次更新時(shí) this 多次調(diào)用update 我希望現(xiàn)將watcher緩存下來(lái),等一會(huì)一起更新
queueWatcher(this);
}
}
evaluate() {
this.dirty = false;
this.value = this.get();
}
}
初始化 data 和 computed,分別代理其 set 和 get 方法,對(duì) data 中的所有屬性生成唯一的 dep 實(shí)例
對(duì) computed 中的 屬性生成唯一的 watcher,并保存在 vm._computedWatchers 中
訪問(wèn)計(jì)算屬性時(shí),設(shè)置 Dep.target 指向 計(jì)算屬性的 watcher,調(diào)用該屬性具體方法
方法中訪問(wèn) data 的屬性,即會(huì)調(diào)用 data 屬性的 get 方法,將 data 屬性的 dep 加入到 計(jì)算屬性的 watcher , 同時(shí)該 dep 中的 subs 添加這個(gè) watcher
設(shè)置 data 的這個(gè)屬性時(shí),調(diào)用該屬性代理的 set 方法,觸發(fā) dep 的 notify 方法
因?yàn)闀r(shí) computed 屬性,只是將 watcher 中的 dirty 設(shè)置為 true
最后,訪問(wèn)計(jì)算屬性的 get 方法時(shí),得知該屬性的 watcher.dirty 為 true,則調(diào)用 watcher.evaluate() 方法獲取新的值