淺析vue 函數(shù)配置項(xiàng)watch及函數(shù) $watch 源碼分享
Vue雙向榜單的原理
大家都知道Vue采用的是MVVM的設(shè)計(jì)模式,采用數(shù)據(jù)驅(qū)動(dòng)實(shí)現(xiàn)雙向綁定,不明白雙向綁定原理的需要先補(bǔ)充雙向綁定的知識(shí),在watch的處理中將運(yùn)用到Vue的雙向榜單原理,所以再次回顧一下:
Vue的數(shù)據(jù)通過Object.defineProperty設(shè)置對(duì)象的get和set實(shí)現(xiàn)對(duì)象屬性的獲取,vue的data下的數(shù)據(jù)對(duì)應(yīng)唯一 一個(gè)dep對(duì)象,dep對(duì)象會(huì)存儲(chǔ)改屬性對(duì)應(yīng)的watcher,在獲取數(shù)據(jù)(get)的時(shí)候?yàn)橄嚓P(guān)屬性添加具有對(duì)應(yīng)處理函數(shù)的watcher,在設(shè)置屬性的時(shí)候,觸發(fā)def對(duì)象下watcher執(zhí)行相關(guān)的邏輯
// 為data的的所有屬性添加getter 和 setter
function defineReactive( obj,key,val,customSetter,shallow
) {
//
var dep = new Dep();
/*....省略部分....*/
var childOb = !shallow && observe(val); //為對(duì)象添加備份依賴dep
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
var value = getter ? getter.call(obj) : val;
if (Dep.target) {
dep.depend(); //
if (childOb) {
childOb.dep.depend(); //依賴dep 添加watcher 用于set ,array改變等使用
if (Array.isArray(value)) {
dependArray(value);
}
}
}
return value
},
set: function reactiveSetter(newVal) {
var value = getter ? getter.call(obj) : val;
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if ("development" !== 'production' && customSetter) {
customSetter();
}
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = !shallow && observe(newVal);
dep.notify();//有改變觸發(fā)watcher進(jìn)行更新
}
});
}
在vue進(jìn)行實(shí)例化的時(shí)候,將調(diào)用 initWatch(vm, opts.watch);進(jìn)行初始化watch的初始化,該函數(shù)最終將調(diào)用 vm.$watch(expOrFn, handler, options) 進(jìn)行watch的配置,下面我們將講解 vm.$watch方法
Vue.prototype.$watch = function (
expOrFn,
cb,
options
) {
var vm = this;
if (isPlainObject(cb)) {
return createWatcher(vm, expOrFn, cb, options)
}
options = options || {};
options.user = true;
//為需要觀察的 expOrFn 添加watcher ,expOrFn的值有改變時(shí)執(zhí)行cb,
//在watcher的實(shí)例化的過程中會(huì)對(duì)expOrFn進(jìn)行解析,并為expOrFn涉及到的data數(shù)據(jù)下的def添加該watcher
var watcher = new Watcher(vm, expOrFn, cb, options);
//immediate==true 立即執(zhí)行watch handler
if (options.immediate) {
cb.call(vm, watcher.value);
}
//取消觀察函數(shù)
return function unwatchFn() {
watcher.teardown();
}
};
來看看實(shí)例化watcher的過程中(只分享是觀察函數(shù)中的實(shí)例的watcher)
var Watcher = function Watcher(
vm,
expOrFn,
cb,
options,
isRenderWatcher
) {
this.vm = vm;
if (isRenderWatcher) {
vm._watcher = this;
}
vm._watchers.push(this);
// options
if (options) {
this.deep = !!options.deep; //是否觀察對(duì)象內(nèi)部值的變化
this.user = !!options.user;
this.lazy = !!options.lazy;
this.sync = !!options.sync;
} else {
this.deep = this.user = this.lazy = this.sync = false;
}
this.cb = cb; // 觀察屬性改變時(shí)執(zhí)行的函數(shù)
this.id = ++uid$1; // uid for batching
this.active = true;
this.dirty = this.lazy; // for lazy watchers
this.deps = [];
this.newDeps = [];
this.depIds = new _Set();
this.newDepIds = new _Set();
this.expression = expOrFn.toString();
// parse expression for getter
if (typeof expOrFn === 'function') {
this.getter = expOrFn;
} else {
// 將需要觀察的數(shù)據(jù):string | Function | Object | Array等進(jìn)行解析 如:a.b.c, 并返回訪問該表達(dá)式的函數(shù)
this.getter = parsePath(expOrFn);
if (!this.getter) {
this.getter = function () { };
"development" !== 'production' && warn(
"Failed watching path: \"" + expOrFn + "\" " +
'Watcher only accepts simple dot-delimited paths. ' +
'For full control, use a function instead.',
vm
);
}
}
// this.get()將訪問需要觀察的數(shù)據(jù)
this.value = this.lazy
? undefined
: this.get();
};
/**
* Evaluate the getter, and re-collect dependencies.
*/
Watcher.prototype.get = function get() {
//this為$watch方法中實(shí)例化的watcher
pushTarget(this);講this賦給Dep.target并緩存之前的watcher
var value;
var vm = this.vm;
try {
//訪問需要觀察的數(shù)據(jù),在獲取數(shù)據(jù)的getter中執(zhí)行dep.depend();將$watch方法中實(shí)例化的watcher添加到對(duì)應(yīng)數(shù)據(jù)下的dep中
value = this.getter.call(vm, vm);
} catch (e) {
if (this.user) {
handleError(e, vm, ("getter for watcher \"" + (this.expression) + "\""));
} else {
throw e
}
} finally {
// "touch" every property so they are all tracked as
// dependencies for deep watching
if (this.deep) {
traverse(value);
}
popTarget(); //將之前的watcher賦給Dep.target
this.cleanupDeps();
}
return value
};<br><br><br><br>
Watcher.prototype.run = function run() {
/*....省略部分....*/
var value = this.get(); //重新獲取info的值
var oldValue = this.value; //保存老的值
this.value = value;
this.cb.call(this.vm, value, oldValue); //執(zhí)行watch的回調(diào)
/*....省略部分....*/
};
以上代碼在watcher實(shí)例化的時(shí)候執(zhí)行 this.getter = parsePath(expOrFn); 返回一個(gè)訪問該屬性的函數(shù),參數(shù)為被訪問的對(duì)象 如vm.$watch("info",function(new, old){console.log("watch success")});, this.getter =function(obj){return obj.info};,在執(zhí)行watcher的get方法中,將執(zhí)行value = this.getter.call(vm, vm);,觸發(fā)屬性的get方法,添加該watcher至info屬性對(duì)應(yīng)的def對(duì)象中,如果需要深度監(jiān)聽,將執(zhí)行traverse(value),依次訪問info(假設(shè)info只對(duì)象)對(duì)象下的屬性,如果info的屬性還有是對(duì)象的屬性,將進(jìn)行遞歸訪問,以達(dá)到info以及info下所有的屬性的def對(duì)象都會(huì)添加該watcher實(shí)例。
當(dāng)我們執(zhí)行vm.info="change"時(shí),將出發(fā)info的set方法,執(zhí)行dep.notify();出發(fā)info所依賴的watcher執(zhí)行watcher的run方法,即實(shí)現(xiàn)監(jiān)聽
總結(jié)
以上所述是小編給大家介紹的vue 函數(shù)配置項(xiàng)watch及函數(shù) $watch 源碼分享,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- vue3 watch和watchEffect的使用以及有哪些區(qū)別
- vue watch監(jiān)控對(duì)象的簡(jiǎn)單方法示例
- vue中watch的用法匯總
- Vue中computed和watch有哪些區(qū)別
- 詳解Vue中的watch和computed
- 解決Vue watch里調(diào)用方法的坑
- 淺談vue.watch的觸發(fā)條件是什么
- 解決vue watch數(shù)據(jù)的方法被調(diào)用了兩次的問題
- 解決vue偵聽器watch,調(diào)用this時(shí)出現(xiàn)undefined的問題
- vue 子組件watch監(jiān)聽不到prop的解決
- vue項(xiàng)目watch內(nèi)的函數(shù)重復(fù)觸發(fā)問題的解決
相關(guān)文章
Vue3?攜手?TypeScript?搭建完整項(xiàng)目結(jié)構(gòu)
TypeScript 是JS的一個(gè)超級(jí),主要提供了類型系統(tǒng)和對(duì)ES6的支持,使用 TypeScript 可以增加代碼的可讀性和可維護(hù)性,在 react 和 vue 社區(qū)中也越來越多人開始使用TypeScript,這篇文章主要介紹了Vue3?攜手?TypeScript?搭建完整項(xiàng)目結(jié)構(gòu),需要的朋友可以參考下2022-04-04
vue-cli-service不是內(nèi)部或外部命令,也不是可運(yùn)行的程序或批處理文件問題
在Vue項(xiàng)目構(gòu)建過程中,如果遇到無法識(shí)別'vue-cli-service'命令的錯(cuò)誤提示,通常是因?yàn)闆]有全局安裝vue-cli,解決這個(gè)問題的步驟主要包括:首先檢查Vue版本,如果未安裝則先安裝Vue;其次全局安裝vue-cli;若在安裝過程中遇到cnpm命令找不到的情況2024-10-10
vue3?使用setup語(yǔ)法糖實(shí)現(xiàn)分類管理功能
這篇文章主要介紹了vue3?使用setup語(yǔ)法糖實(shí)現(xiàn)分類管理,本次模塊使用 vue3+element-plus 實(shí)現(xiàn)一個(gè)新聞?wù)镜暮笈_(tái)分類管理模塊,其中新增、編輯采用對(duì)話框方式公用一個(gè)表單,需要的朋友可以參考下2022-08-08
vue操作下拉選擇器獲取選擇的數(shù)據(jù)的id方法
今天小編就為大家分享一篇vue操作下拉選擇器獲取選擇的數(shù)據(jù)的id方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-08-08
通過vue-cli來學(xué)習(xí)修改Webpack多環(huán)境配置和發(fā)布問題
這篇文章主要介紹了隨著Vue-cli來'學(xué)'并'改'Webpack之多環(huán)境配置和發(fā)布的相關(guān)知識(shí),本文將會(huì)根據(jù)一些實(shí)際的業(yè)務(wù)需求,先學(xué)習(xí)vue-cli生成的模版,然后在進(jìn)行相關(guān)修改,感興趣的朋友一起跟著小編學(xué)習(xí)吧2017-12-12
vue二級(jí)菜單導(dǎo)航點(diǎn)擊選中事件的方法
今天小編就為大家分享一篇vue二級(jí)菜單導(dǎo)航點(diǎn)擊選中事件的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-09-09
vue實(shí)現(xiàn)畫筆回放canvas轉(zhuǎn)視頻播放功能
這篇文章主要介紹了vue實(shí)現(xiàn)畫筆回放,canvas轉(zhuǎn)視頻播放功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-01-01
vue3 學(xué)習(xí)筆記之a(chǎn)xios的使用變化總結(jié)
本篇文章主要旨在幫助正在學(xué)vue3或者準(zhǔn)備學(xué)vue3的同學(xué)了解網(wǎng)絡(luò)請(qǐng)求axios該如何使用,防止接觸了一點(diǎn)點(diǎn)vue3的同學(xué)會(huì)有個(gè)疑問。有興趣的小伙伴可以關(guān)注一下2021-11-11

