源碼分析Vue.js的監(jiān)聽(tīng)實(shí)現(xiàn)教程
前言
相信一說(shuō)到監(jiān)聽(tīng),當(dāng)然就離不了設(shè)計(jì)模式中鼎鼎大名的觀察者模式。舉個(gè)例子,你家后院著火了,可一定要等到煙霧很大火光很亮你才能發(fā)現(xiàn)啊,可是當(dāng)你安裝了一個(gè)火災(zāi)預(yù)警器,當(dāng)發(fā)生火災(zāi)就立馬能夠通知到你了。這就是一個(gè)典型的觀察者模式。當(dāng)然也還有一些其他變種,比如發(fā)布/訂閱(publish/subscribe)模式。
我們知道如果要將數(shù)據(jù)和視圖關(guān)聯(lián)起來(lái),在數(shù)據(jù)變更的時(shí)候,同步視圖,同理視圖變更,數(shù)據(jù)也發(fā)生變化。vue.js是怎么實(shí)現(xiàn)這個(gè)的呢?下面我們來(lái)揭開(kāi)它的神秘面紗。
demo:
<script src="../vue.js"> </script>
<div id="app">
<p>
{{ message }}
</p>
<input v-model="message">
</div>
<script type="text/javascript">
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
}
});
</script>
set: function reactiveSetter(newVal) {
var value = getter ? getter.call(obj) : val;
if (newVal === value) {
return;
}
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = observe(newVal);
dep.notify();
}
這段代碼出現(xiàn)在解析data屬性的時(shí)候,即調(diào)用Object.defineProperty方法配置data的屬性。一旦屬性發(fā)生變化,就notify發(fā)送廣播。
Dep.prototype.notify = function () {
// stablize the subscriber list first
var subs = toArray(this.subs);
for (var i = 0, l = subs.length; i < l; i++) {
subs[i].update();
}
};
notify 最終是周知subscribe(訂閱者)更新,那么上面的數(shù)據(jù)變更就是發(fā)布者。 subscribe是Watcher這個(gè)類(lèi)的實(shí)例化對(duì)象,在實(shí)例化的時(shí)候,會(huì)傳入回調(diào)函數(shù)來(lái)執(zhí)行update,vue弄了一個(gè)隊(duì)列來(lái)執(zhí)行watcher的更新函數(shù),具體可參考源碼。
Watcher.prototype.run = function () {
……
if (value !== this.value || (isObject(value) || this.deep) && !this.shallow) {
……
} else {
this.cb.call(this.vm, value, oldValue);
}
}
this.queued = this.shallow = false;
}
};
在Directive(指令)class中實(shí)例化了Watcher,_update函數(shù)負(fù)責(zé)來(lái)更新
var watcher = this._watcher = new Watcher(this.vm, this.expression, this._update, // callback
{
filters: this.filters,
twoWay: this.twoWay,
deep: this.deep,
preProcess: preProcess,
postProcess: postProcess,
scope: this._scope
});
在解析模板的時(shí)候會(huì)解析Directive,然后綁定,實(shí)例化watcher,這樣模板-data就關(guān)聯(lián)在一起了。
圖片描述

觀察者模式
林林總總的mvc或者mvvm框架基本也都是利用了觀察者模式,這個(gè)也非常有用,尤其在復(fù)雜的系統(tǒng)之中。
利用觀察者模式,在典型的ajax應(yīng)用中,回調(diào)的處理邏輯可以不跟請(qǐng)求耦合在一塊,這樣邏輯上也會(huì)更加清晰。如下是一個(gè)簡(jiǎn)單的發(fā)布/訂閱模式的實(shí)現(xiàn)
var PubSub = {};
(function (q) {
var topics = {}, subUid = -1;
q.publish = function (topic) {
if(!topics[topic]){
return false;
}
var subscribers = topics[topic],
len = subscribers ? subscribers.length : 0;
while(len--){
var args = Array.prototype.slice.call(arguments, 1);
args.unshift(topic);
subscribers[len].callback.apply(this, args);
}
return this;
};
q.subscribe = function (topic, callback) {
if(!topics[topic]){
topics[topic] = [];
}
var subuid = (++subUid).toString();
topics[topic].push({
token: subuid,
callback: callback
});
return subuid;
};
q.unsubscribe = function (subid) {
for(var k in topics){
if(topics[k]){
for(var i = 0, j = topics[k].length; i < j; i++){
if(topics[k][i].token === subid){
topics[k].splice(i, 1);
return subid;
}
}
}
}
return this;
};
})(PubSub);
這就是一個(gè)簡(jiǎn)單的訂閱發(fā)布系統(tǒng),每注冊(cè)一個(gè)訂閱者,其實(shí)就是將其回調(diào)處理的callback保存在一個(gè)字典對(duì)象的數(shù)組中,字典對(duì)象的key值可以隨意定義,只要與發(fā)布時(shí)的key對(duì)應(yīng)起來(lái)就好。
怎么使用呢?
<script>
var messageLogger = function(){
console.log(JSON.stringify(arguments));
};
var subscription = PubSub.subscribe('/newMessage', messageLogger);
// {"0":"/newMessage","1":"hello world"}
PubSub.publish('/newMessage', 'hello world');
// {"0":"/newMessage","1":["test","a","b","c"]}
PubSub.publish('/newMessage', ['test', 'a', 'b', 'c']);
// {"0":"/newMessage","1":{"sender":"hello world","body":"hey man"}}
PubSub.publish('/newMessage', {
sender: 'hello world',
body: 'hey man'
});
PubSub.unsubscribe(subscription);
PubSub.publish('/newMessage', ['test', 'a', 'b', 'c'], 1);
</script>
最后一個(gè)將不會(huì)打印出來(lái),因?yàn)橐呀?jīng)取消訂閱了。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
- 用Vue.js實(shí)現(xiàn)監(jiān)聽(tīng)屬性的變化
- Vue.JS入門(mén)教程之事件監(jiān)聽(tīng)
- 詳解使用vue-router進(jìn)行頁(yè)面切換時(shí)滾動(dòng)條位置與滾動(dòng)監(jiān)聽(tīng)事件
- vue.js 1.x與2.0中js實(shí)時(shí)監(jiān)聽(tīng)input值的變化
- 詳解Vue監(jiān)聽(tīng)數(shù)據(jù)變化原理
- vue監(jiān)聽(tīng)滾動(dòng)事件實(shí)現(xiàn)滾動(dòng)監(jiān)聽(tīng)
- Vue.js實(shí)戰(zhàn)之通過(guò)監(jiān)聽(tīng)滾動(dòng)事件實(shí)現(xiàn)動(dòng)態(tài)錨點(diǎn)
- Vue監(jiān)聽(tīng)數(shù)組變化源碼解析
- vuejs2.0實(shí)現(xiàn)分頁(yè)組件使用$emit進(jìn)行事件監(jiān)聽(tīng)數(shù)據(jù)傳遞的方法
- Vue監(jiān)聽(tīng)數(shù)據(jù)對(duì)象變化源碼
相關(guān)文章
vue2.0在table中實(shí)現(xiàn)全選和反選的示例代碼
這篇文章主要介紹了vue2.0在table中實(shí)現(xiàn)全選和反選的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11
vue移動(dòng)端裁剪圖片結(jié)合插件Cropper的使用實(shí)例代碼
本篇文章主要介紹了vue移動(dòng)端裁剪圖片結(jié)合插件Cropper的使用實(shí)例代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
vue如何動(dòng)態(tài)實(shí)時(shí)的顯示時(shí)間淺析
這篇文章主要給大家介紹了關(guān)于vue如何動(dòng)態(tài)實(shí)時(shí)的顯示時(shí)間,以及vue時(shí)間戳 獲取本地時(shí)間實(shí)時(shí)更新的相關(guān)資料,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05
vue中如何給多個(gè)按鈕動(dòng)態(tài)添加類(lèi)名
這篇文章主要介紹了vue中如何給多個(gè)按鈕動(dòng)態(tài)添加類(lèi)名問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11
defineProps宏函數(shù)不需要從vue中import導(dǎo)入的原因解析
這篇文章主要介紹了defineProps宏函數(shù)不需要從vue中import導(dǎo)入的原因解析,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2024-07-07
vue 自定義指令自動(dòng)獲取文本框焦點(diǎn)的方法
今天小編就為大家分享一篇vue 自定義指令自動(dòng)獲取文本框焦點(diǎn)的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-08-08

