<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專(zhuān)題視頻專(zhuān)題問(wèn)答1問(wèn)答10問(wèn)答100問(wèn)答1000問(wèn)答2000關(guān)鍵字專(zhuān)題1關(guān)鍵字專(zhuān)題50關(guān)鍵字專(zhuān)題500關(guān)鍵字專(zhuān)題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專(zhuān)題關(guān)鍵字專(zhuān)題tag2tag3文章專(zhuān)題文章專(zhuān)題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專(zhuān)題3
        問(wèn)答文章1 問(wèn)答文章501 問(wèn)答文章1001 問(wèn)答文章1501 問(wèn)答文章2001 問(wèn)答文章2501 問(wèn)答文章3001 問(wèn)答文章3501 問(wèn)答文章4001 問(wèn)答文章4501 問(wèn)答文章5001 問(wèn)答文章5501 問(wèn)答文章6001 問(wèn)答文章6501 問(wèn)答文章7001 問(wèn)答文章7501 問(wèn)答文章8001 問(wèn)答文章8501 問(wèn)答文章9001 問(wèn)答文章9501
        當(dāng)前位置: 首頁(yè) - 科技 - 知識(shí)百科 - 正文

        淺析從vue源碼看觀察者模式

        來(lái)源:懂視網(wǎng) 責(zé)編:小采 時(shí)間:2020-11-27 22:20:39
        文檔

        淺析從vue源碼看觀察者模式

        淺析從vue源碼看觀察者模式:觀察者模式 首先話題下來(lái),我們得反問(wèn)一下自己,什么是觀察者模式? 概念 觀察者模式(Observer):通常又被稱(chēng)作為發(fā)布-訂閱者模式。它定義了一種一對(duì)多的依賴(lài)關(guān)系,即當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變的時(shí)候,所有依賴(lài)于它的對(duì)象都會(huì)得到通知并自動(dòng)更新,解決了
        推薦度:
        導(dǎo)讀淺析從vue源碼看觀察者模式:觀察者模式 首先話題下來(lái),我們得反問(wèn)一下自己,什么是觀察者模式? 概念 觀察者模式(Observer):通常又被稱(chēng)作為發(fā)布-訂閱者模式。它定義了一種一對(duì)多的依賴(lài)關(guān)系,即當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變的時(shí)候,所有依賴(lài)于它的對(duì)象都會(huì)得到通知并自動(dòng)更新,解決了

        觀察者模式

        首先話題下來(lái),我們得反問(wèn)一下自己,什么是觀察者模式?

        概念

        觀察者模式(Observer):通常又被稱(chēng)作為發(fā)布-訂閱者模式。它定義了一種一對(duì)多的依賴(lài)關(guān)系,即當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變的時(shí)候,所有依賴(lài)于它的對(duì)象都會(huì)得到通知并自動(dòng)更新,解決了主體對(duì)象與觀察者之間功能的耦合。

        講個(gè)故事

        上面對(duì)于觀察者模式的概念可能會(huì)比較官方化,所以我們講個(gè)故事來(lái)理解它。

        A:是共產(chǎn)黨派往國(guó)民黨密探,代號(hào) 001(發(fā)布者)
        B:是共產(chǎn)黨的通信人員,負(fù)責(zé)與 A 進(jìn)行秘密交接(訂閱者)

        1. A 日常工作就是在明面采集國(guó)民黨的一些情報(bào)
        2. B 則負(fù)責(zé)暗中觀察著 A
        3. 一旦 A 傳遞出一些有關(guān)國(guó)民黨的消息(更多時(shí)候需要對(duì)消息進(jìn)行封裝傳遞,后面根據(jù)源碼具體分析)
        4. B 會(huì)立馬訂閱到該消息,然后做一些相對(duì)應(yīng)的變更,比如說(shuō)通知共產(chǎn)黨們做一些事情應(yīng)對(duì)國(guó)民黨的一些動(dòng)作。

        適用性

        以下任一場(chǎng)景都可以使用觀察者模式

        1. 當(dāng)一個(gè)抽象模型有兩個(gè)方面,其中一個(gè)方面依賴(lài)于另一方面。講這兩者封裝在獨(dú)立的對(duì)象中可以讓它們可以各自獨(dú)立的改變和復(fù)用
        2. 當(dāng)一個(gè)對(duì)象的改變的時(shí)候,需要同時(shí)改變其它對(duì)象,但是卻不知道具體多少對(duì)象有待改變
        3. 當(dāng)一個(gè)對(duì)象必須通知其它對(duì)象,但是卻不知道具體對(duì)象到底是誰(shuí)。換句話說(shuō),你不希望這些對(duì)象是緊密耦合的。

        vue 對(duì)于觀察者模式的使用

        vue 使用到觀察者模式的地方有很多,這里我們主要談?wù)剬?duì)于數(shù)據(jù)初始化這一塊的。

        var vm = new Vue({
         data () {
         return {
         a: 'hello vue'
         }
         }
        })

        1、實(shí)現(xiàn)數(shù)據(jù)劫持

        上圖我們可以看到,vue 是利用的是 Object.defineProperty() 對(duì)數(shù)據(jù)進(jìn)行劫持。 并在數(shù)據(jù)傳遞變更的時(shí)候封裝了一層中轉(zhuǎn)站,即我們看到的 Dep 和 Watcher 兩個(gè)類(lèi)。

        這一小節(jié),我們只看如何通過(guò)觀察者模式對(duì)數(shù)據(jù)進(jìn)行劫持。

        1.1、遞歸遍歷

        我們都知道,vue 對(duì)于 data 里面的數(shù)據(jù)都做了劫持的,那只能對(duì)對(duì)象進(jìn)行遍歷從而完成每個(gè)屬性的劫持,源碼具體如下

        walk (obj: Object) {
         const keys = Object.keys(obj)
         // 遍歷將其變成 vue 的訪問(wèn)器屬性
         for (let i = 0; i < keys.length; i++) {
         defineReactive(obj, keys[i], obj[keys[i]])
         }
        }

        1.2、發(fā)布/訂閱

        從上面對(duì)象的遍歷我們看到了 defineReactive ,那么劫持最關(guān)鍵的點(diǎn)也在于這個(gè)函數(shù),該函數(shù)里面封裝了 getter  和 setter 函數(shù),使用觀察者模式,互相監(jiān)聽(tīng)

        // 設(shè)置為訪問(wèn)器屬性,并在其 getter 和 setter 函數(shù)中,使用發(fā)布/訂閱模式,互相監(jiān)聽(tīng)。
        export function defineReactive (
         obj: Object,
         key: string,
         val: any
        ) {
         // 這里用到了觀察者(發(fā)布/訂閱)模式進(jìn)行了劫持封裝,它定義了一種一對(duì)多的關(guān)系,讓多個(gè)觀察者監(jiān)聽(tīng)一個(gè)主題對(duì)象,這個(gè)主題對(duì)象的狀態(tài)發(fā)生改變時(shí)會(huì)通知所有觀察者對(duì)象,觀察者對(duì)象就可以更新自己的狀態(tài)。
         // 實(shí)例化一個(gè)主題對(duì)象,對(duì)象中有空的觀察者列表
         const dep = new Dep() 
         // 獲取屬性描述符對(duì)象(更多的為了 computed 里面的自定義 get 和 set 進(jìn)行的設(shè)計(jì))
         const property = Object.getOwnPropertyDescriptor(obj, key)
         if (property && property.configurable === false) {
         return
         }
         const getter = property && property.get
         const setter = property && property.set 
         let childOb = observe(val)
         Object.defineProperty(obj, key, {
         enumerable: true,
         configurable: true,
         // 收集依賴(lài),建立一對(duì)多的的關(guān)系,讓多個(gè)觀察者監(jiān)聽(tīng)當(dāng)前主題對(duì)象
         get: function reactiveGetter () {
         const value = getter ? getter.call(obj) : val
         if (Dep.target) {
         dep.depend()
         if (childOb) {
         childOb.dep.depend()
         // 這里是對(duì)數(shù)組進(jìn)行劫持
         if (Array.isArray(value)) {
         dependArray(value)
         }
         }
         }
         return value
         },
         // 劫持到數(shù)據(jù)變更,并發(fā)布消息進(jìn)行通知
         set: function reactiveSetter (newVal) {
         const value = getter ? getter.call(obj) : val
         if (newVal === value || (newVal !== newVal && value !== value)) {
         return
         }
         if (setter) {
         setter.call(obj, newVal)
         } else {
         val = newVal
         }
         childOb = observe(newVal)
         dep.notify()
         }
         })
        }
        

        1.3、返回 Observer 實(shí)例

        上面我們看到了observe 函數(shù),核心就是返回一個(gè) Observer 實(shí)例

        return new Observer(value)

        2、消息封裝,實(shí)現(xiàn) "中轉(zhuǎn)站"

        首先我們要理解,為什么要做一層消息傳遞的封裝?

        我們?cè)谥v解觀察者模式的時(shí)候有提到它的 適用性 。這里也同理,我們?cè)诮俪值綌?shù)據(jù)變更的時(shí)候,并進(jìn)行數(shù)據(jù)變更通知的時(shí)候,如果不做一個(gè)"中轉(zhuǎn)站"的話,我們根本不知道到底誰(shuí)訂閱了消息,具體有多少對(duì)象訂閱了消息。

        這就好比上文中我提到的故事中的密探 A(發(fā)布者) 和共產(chǎn)黨 B(訂閱者)。密探 A 與 共產(chǎn)黨 B 進(jìn)行信息傳遞,兩人都知道對(duì)方這么一個(gè)人的存在,但密探 A 不知道具體 B 是誰(shuí)以及到底有多少共產(chǎn)黨(訂閱者)訂閱著自己,可能很多共產(chǎn)黨都訂閱著密探 A 的信息,so 密探 A(發(fā)布者) 需要通過(guò)暗號(hào) 收集到所有訂閱著其消息的共產(chǎn)黨們(訂閱者),這里對(duì)于訂閱者的收集其實(shí)就是一層封裝。然后密探 A 只需將消息發(fā)布出去,而訂閱者們接受到通知,只管進(jìn)行自己的 update 操作即可。

        簡(jiǎn)單一點(diǎn),即收集完訂閱者們的密探 A 只管發(fā)布消息,共產(chǎn)黨 B 以及更多的共產(chǎn)黨只管訂閱消息并進(jìn)行對(duì)應(yīng)的 update 操作,每個(gè)模塊確保其獨(dú)立性,實(shí)現(xiàn)高內(nèi)聚低耦合這兩大原則。

        廢話不多說(shuō),我們接下來(lái)直接開(kāi)始講 vue 是如何做的消息封裝的

        2.1、Dep

        Dep,全名 Dependency,從名字我們也能大概看出 Dep 類(lèi)是用來(lái)做依賴(lài)收集的,具體怎么收集呢。我們直接看源碼

        let uid = 0
        
        export default class Dep {
         static target: ?Watcher;
         id: number;
         subs: Array<Watcher>;
        
         constructor () {
         // 用來(lái)給每個(gè)訂閱者 Watcher 做唯一標(biāo)識(shí)符,防止重復(fù)收集
         this.id = uid++
         // 定義subs數(shù)組,用來(lái)做依賴(lài)收集(收集所有的訂閱者 Watcher)
         this.subs = []
         }
        
         // 收集訂閱者
         addSub (sub: Watcher) {
         this.subs.push(sub)
         }
        
         depend () {
         if (Dep.target) {
         Dep.target.addDep(this)
         }
         }
        
         notify () {
         // stabilize the subscriber list first
         const subs = this.subs.slice()
         for (let i = 0, l = subs.length; i < l; i++) {
         subs[i].update()
         }
         }
        }
        
        // the current target watcher being evaluated.
        // this is globally unique because there could be only one
        // watcher being evaluated at any time.
        Dep.target = null
        
        

        代碼很簡(jiǎn)短,但它做的事情卻很重要

        1. 定義subs數(shù)組,用來(lái)收集訂閱者Watcher
        2. 當(dāng)劫持到數(shù)據(jù)變更的時(shí)候,通知訂閱者Watcher進(jìn)行update操作

        源碼中,還拋出了兩個(gè)方法用來(lái)操作 Dep.target ,具體如下

        // 定義收集目標(biāo)棧
        const targetStack = []
        
        export function pushTarget (_target: Watcher) {
         if (Dep.target) targetStack.push(Dep.target)
         // 改變目標(biāo)指向
         Dep.target = _target
        }
        
        export function popTarget () {
         // 刪除當(dāng)前目標(biāo),重算指向
         Dep.target = targetStack.pop()
        }
        

        2.2、 Watcher

        Watcher 意為觀察者,它負(fù)責(zé)做的事情就是訂閱 Dep ,當(dāng)Dep 發(fā)出消息傳遞(notify)的時(shí)候,所以訂閱著 Dep 的 Watchers 會(huì)進(jìn)行自己的 update 操作。廢話不多說(shuō),直接看源碼就知道了。

        export default class Watcher {
         vm: Component;
         expression: string;
         cb: Function;
        
         constructor (
         vm: Component,
         expOrFn: string | Function,
         cb: Function,
         options?: Object
         ) {
         this.vm = vm
         vm._watchers.push(this)
         this.cb = cb
         // parse expression for getter
         if (typeof expOrFn === 'function') {
         this.getter = expOrFn
         } else {
         // 解析表達(dá)式
         this.getter = parsePath(expOrFn)
         if (!this.getter) {
         this.getter = function () {}
         }
         }
         this.value = this.get()
         }
        
         get () {
         // 將目標(biāo)收集到目標(biāo)棧
         pushTarget(this)
         const vm = this.vm
         
         let value = this.getter.call(vm, vm)
         // 刪除目標(biāo)
         popTarget()
         
         return value
         }
        
         // 訂閱 Dep,同時(shí)讓 Dep 知道自己訂閱著它
         addDep (dep: Dep) {
         const id = dep.id
         if (!this.newDepIds.has(id)) {
         this.newDepIds.add(id)
         this.newDeps.push(dep)
         if (!this.depIds.has(id)) {
         // 收集訂閱者
         dep.addSub(this)
         }
         }
         }
        
         // 訂閱者'消費(fèi)'動(dòng)作,當(dāng)接收到變更時(shí)則會(huì)執(zhí)行
         update () {
         this.run()
         }
        
         run () {
         const value = this.get()
         const oldValue = this.value
         this.value = value
         this.cb.call(this.vm, value, oldValue)
         }
        }
        
        

        上述代碼中,我刪除了一些與目前探討無(wú)關(guān)的代碼,如果需要進(jìn)行詳細(xì)研究的,可以自行查閱 vue2.5.3 版本的源碼。

        現(xiàn)在再去看 Dep 和 Watcher,我們需要知道兩個(gè)點(diǎn)

        1. Dep 負(fù)責(zé)收集所有的訂閱者 Watcher ,具體誰(shuí)不用管,具體有多少也不用管,只需要通過(guò) target 指向的計(jì)算去收集訂閱其消息的 Watcher 即可,然后只需要做好消息發(fā)布 notify 即可。
        2. Watcher 負(fù)責(zé)訂閱 Dep ,并在訂閱的時(shí)候讓 Dep 進(jìn)行收集,接收到 Dep 發(fā)布的消息時(shí),做好其 update 操作即可。

        兩者看似相互依賴(lài),實(shí)則卻保證了其獨(dú)立性,保證了模塊的單一性。

        更多的應(yīng)用

        vue 還有一些地方用到了"萬(wàn)能"的觀察者模式,比如我們熟知的組件之間的事件傳遞,$on 以及 $emit 的設(shè)計(jì)。

        $emit 負(fù)責(zé)發(fā)布消息,并對(duì)訂閱者 $on 做統(tǒng)一消費(fèi),即執(zhí)行 cbs 里面所有的事件。

        Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
         const vm: Component = this
         if (Array.isArray(event)) {
         for (let i = 0, l = event.length; i < l; i++) {
         this.$on(event[i], fn)
         }
         } else {
         (vm._events[event] || (vm._events[event] = [])).push(fn)
         }
         return vm
        }
        
        Vue.prototype.$emit = function (event: string): Component {
         const vm: Component = this
         let cbs = vm._events[event]
         if (cbs) {
         cbs = cbs.length > 1 ? toArray(cbs) : cbs
         const args = toArray(arguments, 1)
         for (let i = 0, l = cbs.length; i < l; i++) {
         cbs[i].apply(vm, args)
         }
         }
         return vm
        }
        
        

        總結(jié)

        本文探討了觀察者模式的基本概念、適用場(chǎng)景,以及在 vue 源碼中的具體應(yīng)用。這一節(jié)將總結(jié)一下觀察者模式的一些優(yōu)缺點(diǎn)

        1. 目標(biāo)和觀察者間的抽象耦合:一個(gè)目標(biāo)只知道他有一系列的觀察者(目標(biāo)進(jìn)行依賴(lài)收集),卻不知道其中任意一個(gè)觀察者屬于哪一個(gè)具體的類(lèi),這樣目標(biāo)與觀察者之間的耦合是抽象的和最小的。
        2. 支持廣播通信:觀察者里面的通信,不像其它通常的一些請(qǐng)求需要指定它的接受者。通知將會(huì)自動(dòng)廣播給所有已訂閱該目標(biāo)對(duì)象的相關(guān)對(duì)象,即上文中的 dep.notify() 。當(dāng)然,目標(biāo)對(duì)象并不關(guān)心到底有多少對(duì)象對(duì)自己感興趣,它唯一的職責(zé)就是通知它的各位觀察者,處理還是忽略一個(gè)通知取決于觀察者本身。
        3. 一些意外的更新:因?yàn)橐粋€(gè)觀察者它自己并不知道其它觀察者的存在,它可能對(duì)改變目標(biāo)的最終代價(jià)一無(wú)所知。如果觀察者直接在目標(biāo)上做操作的話,可能會(huì)引起一系列對(duì)觀察者以及依賴(lài)于這些觀察者的那些對(duì)象的更新,所以一般我們會(huì)把一些操作放在目標(biāo)內(nèi)部,防止出現(xiàn)上述的問(wèn)題。

        OK,本文到這就差不多了,更多的源碼設(shè)計(jì)思路細(xì)節(jié)將在同系列的其它文章中進(jìn)行一一解讀。

        聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        淺析從vue源碼看觀察者模式

        淺析從vue源碼看觀察者模式:觀察者模式 首先話題下來(lái),我們得反問(wèn)一下自己,什么是觀察者模式? 概念 觀察者模式(Observer):通常又被稱(chēng)作為發(fā)布-訂閱者模式。它定義了一種一對(duì)多的依賴(lài)關(guān)系,即當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變的時(shí)候,所有依賴(lài)于它的對(duì)象都會(huì)得到通知并自動(dòng)更新,解決了
        推薦度:
        標(biāo)簽: VUE 觀察者模式 vue源碼
        • 熱門(mén)焦點(diǎn)

        最新推薦

        猜你喜歡

        熱門(mén)推薦

        專(zhuān)題
        Top
        主站蜘蛛池模板: 国产成人精品免费视频网页大全| 亚洲免费一区二区| 综合在线免费视频| 亚洲高清日韩精品第一区| 国产免费无码AV片在线观看不卡| 亚洲女同成av人片在线观看| 国产日韩AV免费无码一区二区三区| 国产aⅴ无码专区亚洲av麻豆| 久久久精品视频免费观看| 亚洲日韩乱码中文无码蜜桃臀网站 | 思思久久99热免费精品6| 亚洲国产精品无码久久青草| 性生大片视频免费观看一级| 亚洲精品视频在线看| 在线观看肉片AV网站免费| 久久精品国产亚洲AV高清热| 一二三四在线观看免费高清中文在线观看 | 好紧我太爽了视频免费国产| 亚洲自偷自拍另类图片二区| 成人人免费夜夜视频观看| 久久亚洲精品无码gv| 免费a在线观看播放| 国产男女爽爽爽免费视频| 亚洲av丰满熟妇在线播放| 无码乱肉视频免费大全合集 | 羞羞漫画小舞被黄漫免费| 奇米影视亚洲春色| 日韩人妻一区二区三区免费| 亚洲一区二区三区久久| 国产成人无码区免费A∨视频网站 国产成人涩涩涩视频在线观看免费 | 精品国产精品久久一区免费式| 免费国产黄网站在线看| 亚洲av最新在线网址| 免费AA片少妇人AA片直播| 国产在亚洲线视频观看| 亚洲va中文字幕无码久久不卡| 久久电影网午夜鲁丝片免费| 成年网在线观看免费观看网址 | 91嫩草亚洲精品| 免费看国产一级特黄aa大片| 日本中文字幕免费高清视频|