發(fā)布訂閱模式
目標(biāo):解耦危融,讓各個(gè)模塊之間沒有緊密的聯(lián)系。
在Vue中双泪,整個(gè)的更新是按照組件為單位進(jìn)行判斷持搜,以節(jié)點(diǎn)為單位進(jìn)行更新。
------如果代碼中沒有自定義組件焙矛,那么在比較算法的時(shí)候葫盼,我們會(huì)將全部的模板對(duì)應(yīng)的虛擬DOM進(jìn)行比較,比較不同了就就把不同的進(jìn)行更新(二次提交)村斟。
------如果代碼中含有自定義組件贫导,那么在比較算法的時(shí)候,就會(huì)判斷更新的是哪一些組件中的屬性邓梅,只會(huì)判斷更新數(shù)據(jù)的組件脱盲,其他組件不會(huì)更新。
每一個(gè)組件都有自己的一套屬性和狀態(tài)日缨,每一個(gè)組件都有自己的渲染方法或者說是虛擬DOM的比較方法钱反,所以讓各個(gè)模塊之間的關(guān)系盡量分開。
希望修改了什么屬性,就盡可能值只更新這些屬性對(duì)應(yīng)的頁面DOM面哥。
例子:
比如:預(yù)售可能一個(gè)東西沒有貨哎壳,告訴老板,如果東西到了就告訴我
老板就是發(fā)布者
訂閱什么東西作為中間媒介
我就是訂閱者
實(shí)際上就是事件模型
1.首先有一個(gè)event對(duì)象
2.對(duì)象上提供了一些方法尚卫,有on归榕,off,emit方法
on用來綁定訂閱方法吱涉,off用來移除方法刹泄,emit相當(dāng)于老板發(fā)送一個(gè)消息觸發(fā)事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewpoort" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>text</title>
</head>
<body>
<script>
//需要有一個(gè)全局的event對(duì)象,提供on怎爵,off特石,emit方法
var event = (function () {
eventObjs = {}
return {
/*注冊(cè)事件,可以連續(xù)注冊(cè)鳖链,可以注冊(cè)多個(gè)事件*/
on: function (type, handler) {
(eventObjs[type] || (eventObjs[type] = [])).push(handler)
},
/*移除事件姆蘸,
如果沒有參數(shù),移除所有事件芙委,
如果只帶有事件名參數(shù)逞敷,就移除這個(gè)事件名下的所有事件
如果帶有兩個(gè)參數(shù),那么就是表示移除某一個(gè)事件的具體處理函數(shù)
*/
off: function (type, handler) {
if (arguments.length === 0) { //沒有參數(shù)移除所有事件
eventObjs = {}
} else if (arguments.length === 1) { //只有事件的類型灌侣,移除該事件的所有處理函數(shù)
eventObjs[type] = []
} else if (arguments.length === 2) { //移除type事件的handler處理函數(shù)
//使用循環(huán)移除所有該函數(shù)對(duì)應(yīng)的type事件
let _events = eventObjs[type]
if (!_events) return
//倒著循環(huán) 數(shù)組的序號(hào)不會(huì)受到影響
for (let i =_events.length - 1; i >= 0; i-- ) {
if (_events[i] === handler) {
_events.splice(i, 1)
}
}
}
},
/*發(fā)射事件推捐,觸發(fā)事件,包裝參數(shù) 傳遞給事件處理函數(shù)*/
emit: function (type) {
let args = Array.prototype.slice.call(arguments, 1) //獲得arguments從1開始后的所有參數(shù)顶瞳,返回的是一個(gè)數(shù)組
let _events = eventObjs[type]
if (!_events) return
for (let i = 0; i < _events.length; i++) {
//如果要綁定上下文就需要使用call或apply
_events[i].apply(null, args) //調(diào)用這個(gè)方法和傳參
}
}
}
}())
function f() {
console.log('第一個(gè)click事件')
}
function foo() {
console.log('第二個(gè)click事件')
}
//注冊(cè)事件
event.on('click', f) //可以移除
event.on('click', foo) //可以移除
event.on('click', () => {console.log('第三個(gè)click事件')}) //不可以移除 玖姑,引用類型是比較地址
//() => {console.log('第三個(gè)click事件')} === () => {console.log('第三個(gè)click事件')} //false
//[] === [] false {} === {} false
//如果用變量接住就可以比較愕秫,引用的是同一個(gè)地址
//let arr = [] arr===arr true
event.emit('click')
</script>
</body>
</html>
通過以上代碼對(duì)發(fā)布訂閱模式小結(jié):
發(fā)布訂閱模式(形式不局限于函數(shù)慨菱,可以是對(duì)象等等)
1.要有一個(gè)中間的全局容器(eventObjs),用來存儲(chǔ)可以被觸發(fā)的東西(東西可以是函數(shù)戴甩,對(duì)象等等)符喝。
2.需要一個(gè)方法(on方法),可以往容器中傳入東西(函數(shù)甜孤,對(duì)象等)协饲。
3.需要一個(gè)方法(emit方法),可以將容器中的東西取出來使用(函數(shù)調(diào)用缴川,對(duì)象的方法調(diào)用)茉稠。
在Vue中傳入的是對(duì)象,在Vue里那個(gè)全局的容器是target把夸,對(duì)象叫watcher而线,on方法叫depend,Vue中的觸發(fā)是更新觸發(fā)